summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--Documentation/RelNotes/2.7.0.txt236
-rw-r--r--Documentation/blame-options.txt5
-rw-r--r--Documentation/config.txt14
-rw-r--r--Documentation/git-bisect.txt103
-rw-r--r--Documentation/git-branch.txt16
-rw-r--r--Documentation/git-for-each-ref.txt38
-rw-r--r--Documentation/git-gc.txt7
-rw-r--r--Documentation/git-p4.txt39
-rw-r--r--Documentation/git-quiltimport.txt11
-rw-r--r--Documentation/git-rebase.txt3
-rw-r--r--Documentation/git-remote.txt10
-rw-r--r--Documentation/git-rev-list.txt2
-rw-r--r--Documentation/git-stash.txt2
-rw-r--r--Documentation/git-tag.txt27
-rw-r--r--Documentation/git-worktree.txt49
-rw-r--r--Documentation/gitignore.txt23
-rw-r--r--Documentation/glossary-content.txt5
-rw-r--r--Documentation/rev-list-options.txt23
-rwxr-xr-xGIT-VERSION-GEN2
-rw-r--r--Makefile7
l---------RelNotes2
-rw-r--r--archive-tar.c30
-rw-r--r--archive.c5
-rw-r--r--bisect.c11
-rw-r--r--branch.c79
-rw-r--r--branch.h8
-rw-r--r--builtin.h1
-rw-r--r--builtin/am.c49
-rw-r--r--builtin/apply.c29
-rw-r--r--builtin/blame.c14
-rw-r--r--builtin/branch.c508
-rw-r--r--builtin/clean.c6
-rw-r--r--builtin/clone.c17
-rw-r--r--builtin/config.c34
-rw-r--r--builtin/fetch.c32
-rw-r--r--builtin/for-each-ref.c15
-rw-r--r--builtin/fsck.c154
-rw-r--r--builtin/gc.c58
-rw-r--r--builtin/help.c33
-rw-r--r--builtin/index-pack.c2
-rw-r--r--builtin/init-db.c185
-rw-r--r--builtin/log.c3
-rw-r--r--builtin/ls-remote.c8
-rw-r--r--builtin/ls-tree.c9
-rw-r--r--builtin/mailsplit.c39
-rw-r--r--builtin/merge-index.c6
-rw-r--r--builtin/merge-recursive.c2
-rw-r--r--builtin/merge.c20
-rw-r--r--builtin/name-rev.c20
-rw-r--r--builtin/notes.c2
-rw-r--r--builtin/read-tree.c2
-rw-r--r--builtin/receive-pack.c36
-rw-r--r--builtin/remote-ext.c34
-rw-r--r--builtin/remote.c59
-rw-r--r--builtin/rerere.c6
-rw-r--r--builtin/rev-list.c4
-rw-r--r--builtin/show-branch.c2
-rw-r--r--builtin/submodule--helper.c282
-rw-r--r--builtin/tag.c390
-rw-r--r--builtin/unpack-file.c2
-rw-r--r--builtin/upload-archive.c9
-rw-r--r--builtin/worktree.c88
-rw-r--r--bulk-checkin.c4
-rw-r--r--cache.h39
-rw-r--r--color.c46
-rw-r--r--color.h7
-rw-r--r--compat/hstrerror.c2
-rw-r--r--compat/inet_ntop.c6
-rw-r--r--compat/mingw.c8
-rw-r--r--compat/nedmalloc/nedmalloc.c5
-rw-r--r--compat/precompose_utf8.c21
-rw-r--r--compat/precompose_utf8.h2
-rw-r--r--compat/winansi.c2
-rw-r--r--config.mak.uname3
-rw-r--r--configure.ac7
-rw-r--r--connect.c2
-rw-r--r--contrib/hooks/multimail/CHANGES41
-rw-r--r--contrib/hooks/multimail/CONTRIBUTING.rst30
-rw-r--r--contrib/hooks/multimail/README258
-rw-r--r--contrib/hooks/multimail/README.Git4
-rw-r--r--contrib/hooks/multimail/doc/gerrit.rst56
-rw-r--r--contrib/hooks/multimail/doc/gitolite.rst109
-rwxr-xr-xcontrib/hooks/multimail/git_multimail.py936
-rwxr-xr-xcontrib/hooks/multimail/migrate-mailhook-config2
-rwxr-xr-xcontrib/hooks/multimail/post-receive.example8
-rwxr-xr-xcontrib/subtree/git-subtree.sh4
-rwxr-xr-xcontrib/subtree/t/t7900-subtree.sh194
-rw-r--r--convert.c3
-rw-r--r--daemon.c30
-rw-r--r--date.c74
-rw-r--r--diff-no-index.c6
-rw-r--r--diff.c21
-rw-r--r--dir.c93
-rw-r--r--entry.c4
-rw-r--r--environment.c7
-rw-r--r--fast-import.c19
-rw-r--r--fetch-pack.c56
-rwxr-xr-xgit-bisect.sh117
-rw-r--r--git-compat-util.h8
-rwxr-xr-xgit-filter-branch.sh34
-rwxr-xr-xgit-p4.py330
-rwxr-xr-xgit-quiltimport.sh16
-rwxr-xr-xgit-rebase.sh5
-rwxr-xr-xgit-send-email.perl6
-rwxr-xr-xgit-stash.sh20
-rwxr-xr-xgit-submodule.sh164
-rw-r--r--git.c3
-rw-r--r--grep.c36
-rw-r--r--hex.c13
-rw-r--r--http-push.c71
-rw-r--r--http-walker.c19
-rw-r--r--http.c13
-rw-r--r--imap-send.c5
-rw-r--r--ll-merge.c12
-rw-r--r--mailmap.c3
-rw-r--r--merge-recursive.c17
-rw-r--r--notes.c9
-rw-r--r--pack-bitmap.c13
-rw-r--r--parse-options-cb.c19
-rw-r--r--parse-options.h10
-rw-r--r--path.c387
-rw-r--r--pkt-line.c8
-rw-r--r--po/ru.po3550
-rw-r--r--progress.c20
-rw-r--r--reachable.c8
-rw-r--r--ref-filter.c718
-rw-r--r--ref-filter.h50
-rw-r--r--reflog-walk.c7
-rw-r--r--refs.c117
-rw-r--r--refs.h1
-rw-r--r--remote-curl.c5
-rw-r--r--remote.c142
-rw-r--r--rerere.c555
-rw-r--r--rerere.h12
-rw-r--r--revision.c2
-rw-r--r--run-command.c16
-rw-r--r--run-command.h1
-rw-r--r--setup.c29
-rw-r--r--sha1_file.c185
-rw-r--r--sha1_name.c45
-rw-r--r--sideband.c4
-rw-r--r--strbuf.c13
-rw-r--r--strbuf.h23
-rw-r--r--submodule-config.c74
-rw-r--r--submodule.c15
-rwxr-xr-xt/t0001-init.sh4
-rwxr-xr-xt/t0002-gitfile.sh42
-rwxr-xr-xt/t0027-auto-crlf.sh191
-rwxr-xr-xt/t0060-path-utils.sh6
-rwxr-xr-xt/t1400-update-ref.sh19
-rwxr-xr-xt/t1401-symbolic-ref.sh29
-rwxr-xr-xt/t1430-bad-ref-name.sh31
-rwxr-xr-xt/t2025-worktree-add.sh5
-rwxr-xr-xt/t2027-worktree-list.sh89
-rwxr-xr-xt/t3001-ls-files-others-exclude.sh25
-rwxr-xr-xt/t3203-branch-output.sh20
-rwxr-xr-xt/t3210-pack-refs.sh7
-rwxr-xr-xt/t3420-rebase-autostash.sh10
-rwxr-xr-xt/t5505-remote.sh37
-rwxr-xr-xt/t5700-clone-reference.sh21
-rwxr-xr-xt/t5801-remote-helpers.sh12
-rwxr-xr-xt/t5802-connect-helper.sh28
-rwxr-xr-xt/t6030-bisect-porcelain.sh135
-rwxr-xr-xt/t6300-for-each-ref.sh162
-rwxr-xr-xt/t6302-for-each-ref-filter.sh258
-rwxr-xr-xt/t6500-gc.sh13
-rwxr-xr-xt/t7003-filter-branch.sh14
-rwxr-xr-xt/t7004-tag.sh47
-rwxr-xr-xt/t7410-submodule-checkout-to.sh10
-rwxr-xr-xt/t7610-mergetool.sh2
-rwxr-xr-xt/t7800-difftool.sh8
-rwxr-xr-xt/t9811-git-p4-label-import.sh45
-rwxr-xr-xt/t9815-git-p4-submit-fail.sh7
-rwxr-xr-xt/t9819-git-p4-case-folding.sh6
-rwxr-xr-xt/t9822-git-p4-path-encoding.sh58
-rwxr-xr-xt/t9823-git-p4-mock-lfs.sh192
-rwxr-xr-xt/t9824-git-p4-git-lfs.sh288
-rwxr-xr-xt/t9825-git-p4-handle-utf16-without-bom.sh50
-rw-r--r--t/test-lib-functions.sh25
-rw-r--r--tag.c2
-rw-r--r--test-dump-cache-tree.c2
-rw-r--r--test-path-utils.c2
-rw-r--r--trace.c23
-rw-r--r--transport.c13
-rw-r--r--unpack-trees.c4
-rw-r--r--url.c3
-rw-r--r--utf8.c21
-rw-r--r--utf8.h15
-rw-r--r--walker.c5
-rw-r--r--worktree.c219
-rw-r--r--worktree.h38
-rw-r--r--wrapper.c16
193 files changed, 9729 insertions, 4487 deletions
diff --git a/.gitignore b/.gitignore
index 4fd81baf85..1c2f832138 100644
--- a/.gitignore
+++ b/.gitignore
@@ -155,6 +155,7 @@
/git-status
/git-stripspace
/git-submodule
+/git-submodule--helper
/git-svn
/git-symbolic-ref
/git-tag
diff --git a/Documentation/RelNotes/2.7.0.txt b/Documentation/RelNotes/2.7.0.txt
new file mode 100644
index 0000000000..85c35202d1
--- /dev/null
+++ b/Documentation/RelNotes/2.7.0.txt
@@ -0,0 +1,236 @@
+Git 2.7 Release Notes
+=====================
+
+Updates since v2.6
+------------------
+
+UI, Workflows & Features
+
+ * "git remote" learned "get-url" subcommand to show the URL for a
+ given remote name used for fetching and pushing.
+
+ * There was no way to defeat a configured rebase.autostash variable
+ from the command line, as "git rebase --no-autostash" was missing.
+
+ * "git log --date=local" used to only show the normal (default)
+ format in the local timezone. The command learned to take 'local'
+ as an instruction to use the local timezone with other formats,
+
+ * The refs used during a "git bisect" session is now per-worktree so
+ that independent bisect sessions can be done in different worktrees
+ created with "git worktree add".
+
+ * Users who are too busy to type three extra keystrokes to ask for
+ "git stash show -p" can now set stash.showPatch configuration
+ varible to true to always see the actual patch, not just the list
+ of paths affected with feel for the extent of damage via diffstat.
+
+ * "quiltimport" allows to specify the series file by honoring the
+ $QUILT_SERIES environment and also --series command line option.
+
+ * The use of 'good/bad' in "git bisect" made it confusing to use when
+ hunting for a state change that is not a regression (e.g. bugfix).
+ The command learned 'old/new' and then allows the end user to
+ say e.g. "bisect start --term-old=fast --term-new=slow" to find a
+ performance regression.
+
+ * "git interpret-trailers" can now run outside of a Git repository.
+
+ * "git p4" learned to reencode the pathname it uses to communicate
+ with the p4 depot with a new option.
+
+ * Give progress meter to "git filter-branch".
+
+ * Allow a later "!/abc/def" to override an earlier "/abc" that
+ appears in the same .gitignore file to make it easier to express
+ "everything in /abc directory is ignored, except for ...".
+
+ * Teach "git p4" to send large blobs outside the repository by
+ talking to Git LFS.
+
+
+Performance, Internal Implementation, Development Support etc.
+
+ * The infrastructure to rewrite "git submodule" in C is being built
+ incrementally. Let's polish these early parts well enough and make
+ them graduate to 'next' and 'master', so that the more involved
+ follow-up can start cooking on a solid ground.
+
+ * Some features from "git tag -l" and "git branch -l" have been made
+ available to "git for-each-ref" so that eventually the unified
+ implementation can be shared across all three.
+
+ * Because "test_when_finished" in our test framework queues the
+ clean-up tasks to be done in a shell variable, it should not be
+ used inside a subshell. Add a mechanism to allow 'bash' to catch
+ such uses, and fix the ones that were found.
+ (merge 0968f12 jk/test-lint-forbid-when-finished-in-subshell later to maint).
+
+ * The debugging infrastructure for pkt-line based communication has
+ been improved to mark the side-band communication specifically.
+ (merge fd89433 jk/async-pkt-line later to maint).
+
+ * Update "git branch" that list existing branches, using the
+ ref-filter API that is shared with "git tag" and "git
+ for-each-ref".
+
+ * The test for various line-ending conversions has been enhanced.
+
+ * A few test scripts around "git p4" have been improved for
+ portability.
+
+ * Many allocations that is manually counted (correctly) that are
+ followed by strcpy/sprintf have been replaced with a less error
+ prone constructs such as xstrfmt.
+
+
+Also contains various documentation updates and code clean-ups.
+
+
+Fixes since v2.6
+----------------
+
+Unless otherwise noted, all the fixes since v2.6 in the maintenance
+track are contained in this release (see the maintenance releases'
+notes for details).
+
+ * Very small number of options take a parameter that is optional
+ (which is not a great UI element as they can only appear at the end
+ of the command line). Add notice to documentation of each and
+ every one of them.
+ (merge 2b594bf mm/keyid-docs later to maint).
+
+ * "git blame --first-parent v1.0..v2.0" was not rejected but did not
+ limit the blame to commits on the first parent chain.
+ (merge 95a4fb0 jk/blame-first-parent later to maint).
+
+ * "git subtree" (in contrib/) now can take whitespaces in the
+ pathnames, not only in the in-tree pathname but the name of the
+ directory that the repository is in. (merge 5b6ab38
+ as/subtree-with-spaces later to maint).
+
+ * The ssh transport, just like any other transport over the network,
+ did not clear GIT_* environment variables, but it is possible to
+ use SendEnv and AcceptEnv to leak them to the remote invocation of
+ Git, which is not a good idea at all. Explicitly clear them just
+ like we do for the local transport.
+ (merge a48b409 jk/connect-clear-env later to maint).
+
+ * Correct "git p4 --detect-labels" so that it does not fail to create
+ a tag that points at a commit that is also being imported.
+ (merge b43702a ld/p4-import-labels later to maint).
+
+ * The Makefile always runs the library archiver with hardcoded "crs"
+ options, which was inconvenient for exotic platforms on which
+ people want to use programs with totally different set of command
+ line options.
+ (merge ac179b4 jw/make-arflags-customizable later to maint).
+
+ * Customization to change the behaviour with "make -w" and "make -s"
+ in our Makefile was broken when they were used together.
+ (merge ef49e05 jk/make-findstring-makeflags-fix later to maint).
+
+ * Allocation related functions and stdio are unsafe things to call
+ inside a signal handler, and indeed killing the pager can cause
+ glibc to deadlock waiting on allocation mutex as our signal handler
+ tries to free() some data structures in wait_for_pager(). Reduce
+ these unsafe calls.
+ (merge 507d780 ti/glibc-stdio-mutex-from-signal-handler later to maint).
+
+ * The way how --ref/--notes to specify the notes tree reference are
+ DWIMmed was not clearly documented.
+ (merge e14c92e jk/notes-dwim-doc later to maint).
+
+ * "git gc" used to barf when a symbolic ref has gone dangling
+ (e.g. the branch that used to be your upstream's default when you
+ cloned from it is now gone, and you did "fetch --prune").
+ (merge 14886b4 js/gc-with-stale-symref later to maint).
+
+ * "git clone --dissociate" runs a big "git repack" process at the
+ end, and it helps to close file descriptors that are open on the
+ packs and their idx files before doing so on filesystems that
+ cannot remove a file that is still open.
+ (merge 786b150 js/clone-dissociate later to maint).
+
+ * Description of the "log.follow" configuration variable in "git log"
+ documentation is now also copied to "git config" documentation.
+ (merge fd8d07e dt/log-follow-config later to maint).
+
+ * "git rebase -i" had a minor regression recently, which stopped
+ considering a line that begins with an indented '#' in its insn
+ sheet not a comment, which is now fixed.
+ (merge 1db168e gr/rebase-i-drop-warn later to maint).
+
+ * After "git checkout --detach", "git status" reported a fairly
+ useless "HEAD detached at HEAD", instead of saying at which exact
+ commit.
+ (merge 0eb8548 mm/detach-at-HEAD-reflog later to maint).
+
+ * When "git send-email" wanted to talk over Net::SMTP::SSL,
+ Net::Cmd::datasend() did not like to be fed too many bytes at the
+ same time and failed to send messages. Send the payload one line
+ at a time to work around the problem.
+ (merge f60c483 sa/send-email-smtp-batch-data-limit later to maint).
+
+ * When "git am" was rewritten as a built-in, it stopped paying
+ attention to user.signingkey, which was fixed.
+ (merge 434c64d pt/am-builtin later to maint).
+
+ * It was not possible to use a repository-lookalike created by "git
+ worktree add" as a local source of "git clone".
+ (merge d78db84 nd/clone-linked-checkout later to maint).
+
+ * On a case insensitive filesystems, setting GIT_WORK_TREE variable
+ using a random cases that does not agree with what the filesystem
+ thinks confused Git that it wasn't inside the working tree.
+ (merge 63ec5e1 js/icase-wt-detection later to maint).
+
+ * Performance-measurement tests did not work without an installed Git.
+ (merge 31cd128 sb/perf-without-installed-git later to maint).
+
+ * A test script for the HTTP service had a timing dependent bug,
+ which was fixed.
+ (merge 362d8b6 sb/http-flaky-test-fix later to maint).
+
+ * There were some classes of errors that "git fsck" diagnosed to its
+ standard error that did not cause it to exit with non-zero status.
+ (merge 122f76f jc/fsck-dropped-errors later to maint).
+
+ * Work around "git p4" failing when the P4 depot records the contents
+ in UTF-16 without UTF-16 BOM.
+ (merge 1f5f390 ls/p4-translation-failure later to maint).
+
+ * When "git gc --auto" is backgrounded, its diagnosis message is
+ lost. Save it to a file in $GIT_DIR and show it next time the "gc
+ --auto" is run.
+ (merge 329e6e8 nd/gc-auto-background-fix later to maint).
+
+ * The submodule code has been taught to work better with separate
+ work trees created via "git worktree add".
+ (merge 11f9dd7 mk/submodule-gitdir-path later to maint).
+
+ * "git gc" is safe to run anytime only because it has the built-in
+ grace period to protect young objects. In order to run with no
+ grace period, the user must make sure that the repository is
+ quiescent.
+ (merge fae1a90 jc/doc-gc-prune-now later to maint).
+
+ * A recent "filter-branch --msg-filter" broke skipping of the commit
+ object header, which is fixed.
+ (merge a5a4b3f jk/filter-branch-use-of-sed-on-incomplete-line later to maint).
+
+ * The normalize_ceiling_entry() function does not muck with the end
+ of the path it accepts, and the real world callers do rely on that,
+ but a test insisted that the function drops a trailing slash.
+ (merge b2a7123 rd/test-path-utils later to maint).
+
+ * Code clean-up and minor fixes.
+ (merge 15ed07d jc/rerere later to maint).
+ (merge e7a7401 pt/pull-builtin later to maint).
+ (merge 29bc480 nd/ls-remote-does-not-have-u-option later to maint).
+ (merge be510e0 jk/asciidoctor-section-heading-markup-fix later to maint).
+ (merge 83e6bda tk/typofix-connect-unknown-proto-error later to maint).
+ (merge a43eb67 tk/doc-interpret-trailers-grammo later to maint).
+ (merge ba128e2 es/worktree-add-cleanup later to maint).
+ (merge 44cd91e cc/quote-comments later to maint).
+ (merge 147875f sb/submodule-config-parse later to maint).
diff --git a/Documentation/blame-options.txt b/Documentation/blame-options.txt
index a09969ba08..760eab7428 100644
--- a/Documentation/blame-options.txt
+++ b/Documentation/blame-options.txt
@@ -63,11 +63,10 @@ include::line-range-format.txt[]
`-` to make the command read from the standard input).
--date <format>::
- The value is one of the following alternatives:
- {relative,local,default,iso,rfc,short}. If --date is not
+ Specifies the format used to output dates. If --date is not
provided, the value of the blame.date config variable is
used. If the blame.date config variable is also not set, the
- iso format is used. For more information, See the discussion
+ iso format is used. For supported values, see the discussion
of the --date option at linkgit:git-log[1].
-M|<num>|::
diff --git a/Documentation/config.txt b/Documentation/config.txt
index fec0dea5d0..391a0c3c85 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -1829,9 +1829,7 @@ log.abbrevCommit::
log.date::
Set the default date-time mode for the 'log' command.
Setting a value for log.date is similar to using 'git log''s
- `--date` option. Possible values are `relative`, `local`,
- `default`, `iso`, `rfc`, and `short`; see linkgit:git-log[1]
- for details.
+ `--date` option. See linkgit:git-log[1] for details.
log.decorate::
Print out the ref names of any commits that are shown by the log
@@ -2593,6 +2591,16 @@ status.submoduleSummary::
submodule summary' command, which shows a similar output but does
not honor these settings.
+stash.showPatch::
+ If this is set to true, the `git stash show` command without an
+ option will show the stash in patch form. Defaults to false.
+ See description of 'show' command in linkgit:git-stash[1].
+
+stash.showStat::
+ If this is set to true, the `git stash show` command without an
+ option will show diffstat of the stash. Defaults to true.
+ See description of 'show' command in linkgit:git-stash[1].
+
submodule.<name>.path::
submodule.<name>.url::
The path within this project and URL for a submodule. These
diff --git a/Documentation/git-bisect.txt b/Documentation/git-bisect.txt
index e97f2de21b..2044fe6820 100644
--- a/Documentation/git-bisect.txt
+++ b/Documentation/git-bisect.txt
@@ -16,9 +16,11 @@ DESCRIPTION
The command takes various subcommands, and different options depending
on the subcommand:
- git bisect start [--no-checkout] [<bad> [<good>...]] [--] [<paths>...]
- git bisect bad [<rev>]
- git bisect good [<rev>...]
+ git bisect start [--term-{old,good}=<term> --term-{new,bad}=<term>]
+ [--no-checkout] [<bad> [<good>...]] [--] [<paths>...]
+ git bisect (bad|new) [<rev>]
+ git bisect (good|old) [<rev>...]
+ git bisect terms [--term-good | --term-bad]
git bisect skip [(<rev>|<range>)...]
git bisect reset [<commit>]
git bisect visualize
@@ -36,6 +38,13 @@ whether the selected commit is "good" or "bad". It continues narrowing
down the range until it finds the exact commit that introduced the
change.
+In fact, `git bisect` can be used to find the commit that changed
+*any* property of your project; e.g., the commit that fixed a bug, or
+the commit that caused a benchmark's performance to improve. To
+support this more general usage, the terms "old" and "new" can be used
+in place of "good" and "bad", or you can choose your own terms. See
+section "Alternate terms" below for more information.
+
Basic bisect commands: start, bad, good
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -111,6 +120,79 @@ bad revision, while `git bisect reset HEAD` will leave you on the
current bisection commit and avoid switching commits at all.
+Alternate terms
+~~~~~~~~~~~~~~~
+
+Sometimes you are not looking for the commit that introduced a
+breakage, but rather for a commit that caused a change between some
+other "old" state and "new" state. For example, you might be looking
+for the commit that introduced a particular fix. Or you might be
+looking for the first commit in which the source-code filenames were
+finally all converted to your company's naming standard. Or whatever.
+
+In such cases it can be very confusing to use the terms "good" and
+"bad" to refer to "the state before the change" and "the state after
+the change". So instead, you can use the terms "old" and "new",
+respectively, in place of "good" and "bad". (But note that you cannot
+mix "good" and "bad" with "old" and "new" in a single session.)
+
+In this more general usage, you provide `git bisect` with a "new"
+commit has some property and an "old" commit that doesn't have that
+property. Each time `git bisect` checks out a commit, you test if that
+commit has the property. If it does, mark the commit as "new";
+otherwise, mark it as "old". When the bisection is done, `git bisect`
+will report which commit introduced the property.
+
+To use "old" and "new" instead of "good" and bad, you must run `git
+bisect start` without commits as argument and then run the following
+commands to add the commits:
+
+------------------------------------------------
+git bisect old [<rev>]
+------------------------------------------------
+
+to indicate that a commit was before the sought change, or
+
+------------------------------------------------
+git bisect new [<rev>...]
+------------------------------------------------
+
+to indicate that it was after.
+
+To get a reminder of the currently used terms, use
+
+------------------------------------------------
+git bisect terms
+------------------------------------------------
+
+You can get just the old (respectively new) term with `git bisect term
+--term-old` or `git bisect term --term-good`.
+
+If you would like to use your own terms instead of "bad"/"good" or
+"new"/"old", you can choose any names you like (except existing bisect
+subcommands like `reset`, `start`, ...) by starting the
+bisection using
+
+------------------------------------------------
+git bisect start --term-old <term-old> --term-new <term-new>
+------------------------------------------------
+
+For example, if you are looking for a commit that introduced a
+performance regression, you might use
+
+------------------------------------------------
+git bisect start --term-old fast --term-new slow
+------------------------------------------------
+
+Or if you are looking for the commit that fixed a bug, you might use
+
+------------------------------------------------
+git bisect start --term-new fixed --term-old broken
+------------------------------------------------
+
+Then, use `git bisect <term-old>` and `git bisect <term-new>` instead
+of `git bisect good` and `git bisect bad` to mark commits.
+
Bisect visualize
~~~~~~~~~~~~~~~~
@@ -387,6 +469,21 @@ In this case, when 'git bisect run' finishes, bisect/bad will refer to a commit
has at least one parent whose reachable graph is fully traversable in the sense
required by 'git pack objects'.
+* Look for a fix instead of a regression in the code
++
+------------
+$ git bisect start
+$ git bisect new HEAD # current commit is marked as new
+$ git bisect old HEAD~10 # the tenth commit from now is marked as old
+------------
++
+or:
+------------
+$ git bisect start --term-old broken --term-new fixed
+$ git bisect fixed
+$ git bisect broken HEAD~10
+------------
+
Getting help
~~~~~~~~~~~~
diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index bbbade4f51..4a7037f1c8 100644
--- a/Documentation/git-branch.txt
+++ b/Documentation/git-branch.txt
@@ -11,7 +11,8 @@ SYNOPSIS
'git branch' [--color[=<when>] | --no-color] [-r | -a]
[--list] [-v [--abbrev=<length> | --no-abbrev]]
[--column[=<options>] | --no-column]
- [(--merged | --no-merged | --contains) [<commit>]] [<pattern>...]
+ [(--merged | --no-merged | --contains) [<commit>]] [--sort=<key>]
+ [--points-at <object>] [<pattern>...]
'git branch' [--set-upstream | --track | --no-track] [-l] [-f] <branchname> [<start-point>]
'git branch' (--set-upstream-to=<upstream> | -u <upstream>) [<branchname>]
'git branch' --unset-upstream [<branchname>]
@@ -231,6 +232,19 @@ start-point is either a local or remote-tracking branch.
The new name for an existing branch. The same restrictions as for
<branchname> apply.
+--sort=<key>::
+ Sort based on the key given. Prefix `-` to sort in descending
+ order of the value. You may use the --sort=<key> option
+ multiple times, in which case the last key becomes the primary
+ key. The keys supported are the same as those in `git
+ for-each-ref`. Sort order defaults to sorting based on the
+ full refname (including `refs/...` prefix). This lists
+ detached HEAD (if present) first, then local branches and
+ finally remote-tracking branches.
+
+
+--points-at <object>::
+ Only list branches of the given object.
Examples
--------
diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt
index 7f8d9a5b5f..c6f073cea4 100644
--- a/Documentation/git-for-each-ref.txt
+++ b/Documentation/git-for-each-ref.txt
@@ -10,6 +10,8 @@ SYNOPSIS
[verse]
'git for-each-ref' [--count=<count>] [--shell|--perl|--python|--tcl]
[(--sort=<key>)...] [--format=<format>] [<pattern>...]
+ [--points-at <object>] [(--merged | --no-merged) [<object>]]
+ [--contains [<object>]]
DESCRIPTION
-----------
@@ -62,6 +64,20 @@ OPTIONS
the specified host language. This is meant to produce
a scriptlet that can directly be `eval`ed.
+--points-at <object>::
+ Only list refs which points at the given object.
+
+--merged [<object>]::
+ Only list refs whose tips are reachable from the
+ specified commit (HEAD if not specified).
+
+--no-merged [<object>]::
+ Only list refs whose tips are not reachable from the
+ specified commit (HEAD if not specified).
+
+--contains [<object>]::
+ Only list tags which contain the specified commit (HEAD if not
+ specified).
FIELD NAMES
-----------
@@ -111,6 +127,17 @@ color::
Change output color. Followed by `:<colorname>`, where names
are described in `color.branch.*`.
+align::
+ Left-, middle-, or right-align the content between
+ %(align:...) and %(end). The "align:" is followed by `<width>`
+ and `<position>` in any order separated by a comma, where the
+ `<position>` is either left, right or middle, default being
+ left and `<width>` is the total length of the content with
+ alignment. If the contents length is more than the width then
+ no alignment is performed. If used with '--quote' everything
+ in between %(align:...) and %(end) is quoted, but if nested
+ then only the topmost level performs quoting.
+
In addition to the above, for commit and tag objects, the header
field names (`tree`, `parent`, `object`, `type`, and `tag`) can
be used to specify the value in the header field.
@@ -123,20 +150,23 @@ The complete message in a commit and tag object is `contents`.
Its first line is `contents:subject`, where subject is the concatenation
of all lines of the commit message up to the first blank line. The next
line is 'contents:body', where body is all of the lines after the first
-blank line. Finally, the optional GPG signature is `contents:signature`.
+blank line. The optional GPG signature is `contents:signature`. The
+first `N` lines of the message is obtained using `contents:lines=N`.
For sorting purposes, fields with numeric values sort in numeric
order (`objectsize`, `authordate`, `committerdate`, `taggerdate`).
All other fields are used to sort in their byte-value order.
+There is also an option to sort by versions, this can be done by using
+the fieldname `version:refname` or its alias `v:refname`.
+
In any case, a field name that refers to a field inapplicable to
the object referred by the ref does not cause an error. It
returns an empty string instead.
As a special case for the date-type fields, you may specify a format for
-the date by adding one of `:default`, `:relative`, `:short`, `:local`,
-`:iso8601`, `:rfc2822` or `:raw` to the end of the fieldname; e.g.
-`%(taggerdate:relative)`.
+the date by adding `:` followed by date format name (see the
+values the `--date` option to linkgit::git-rev-list[1] takes).
EXAMPLES
diff --git a/Documentation/git-gc.txt b/Documentation/git-gc.txt
index 52234987f9..fa1510480a 100644
--- a/Documentation/git-gc.txt
+++ b/Documentation/git-gc.txt
@@ -63,8 +63,11 @@ automatic consolidation of packs.
--prune=<date>::
Prune loose objects older than date (default is 2 weeks ago,
overridable by the config variable `gc.pruneExpire`).
- --prune=all prunes loose objects regardless of their age.
- --prune is on by default.
+ --prune=all prunes loose objects regardless of their age (do
+ not use --prune=all unless you know exactly what you are doing.
+ Unless the repository is quiescent, you will lose newly created
+ objects that haven't been anchored with the refs and end up
+ corrupting your repository). --prune is on by default.
--no-prune::
Do not prune any loose objects.
diff --git a/Documentation/git-p4.txt b/Documentation/git-p4.txt
index 82aa5d6073..c3ff7d0d9b 100644
--- a/Documentation/git-p4.txt
+++ b/Documentation/git-p4.txt
@@ -510,6 +510,45 @@ git-p4.useClientSpec::
option '--use-client-spec'. See the "CLIENT SPEC" section above.
This variable is a boolean, not the name of a p4 client.
+git-p4.pathEncoding::
+ Perforce keeps the encoding of a path as given by the originating OS.
+ Git expects paths encoded as UTF-8. Use this config to tell git-p4
+ what encoding Perforce had used for the paths. This encoding is used
+ to transcode the paths to UTF-8. As an example, Perforce on Windows
+ often uses “cp1252†to encode path names.
+
+git-p4.largeFileSystem::
+ Specify the system that is used for large (binary) files. Please note
+ that large file systems do not support the 'git p4 submit' command.
+ Only Git LFS [1] is implemented right now. Download
+ and install the Git LFS command line extension to use this option
+ and configure it like this:
++
+-------------
+git config git-p4.largeFileSystem GitLFS
+-------------
++
+ [1] https://git-lfs.github.com/
+
+git-p4.largeFileExtensions::
+ All files matching a file extension in the list will be processed
+ by the large file system. Do not prefix the extensions with '.'.
+
+git-p4.largeFileThreshold::
+ All files with an uncompressed size exceeding the threshold will be
+ processed by the large file system. By default the threshold is
+ defined in bytes. Add the suffix k, m, or g to change the unit.
+
+git-p4.largeFileCompressedThreshold::
+ All files with a compressed size exceeding the threshold will be
+ processed by the large file system. This option might slow down
+ your clone/sync process. By default the threshold is defined in
+ bytes. Add the suffix k, m, or g to change the unit.
+
+git-p4.largeFilePush::
+ Boolean variable which defines if large files are automatically
+ pushed to a server.
+
Submit variables
~~~~~~~~~~~~~~~~
git-p4.detectRenames::
diff --git a/Documentation/git-quiltimport.txt b/Documentation/git-quiltimport.txt
index d64388cb8e..ff633b0db7 100644
--- a/Documentation/git-quiltimport.txt
+++ b/Documentation/git-quiltimport.txt
@@ -10,6 +10,7 @@ SYNOPSIS
--------
[verse]
'git quiltimport' [--dry-run | -n] [--author <author>] [--patches <dir>]
+ [--series <file>]
DESCRIPTION
@@ -42,13 +43,19 @@ OPTIONS
information can be found in the patch description.
--patches <dir>::
- The directory to find the quilt patches and the
- quilt series file.
+ The directory to find the quilt patches.
+
The default for the patch directory is patches
or the value of the $QUILT_PATCHES environment
variable.
+--series <file>::
+ The quilt series file.
++
+The default for the series file is <patches>/series
+or the value of the $QUILT_SERIES environment
+variable.
+
GIT
---
Part of the linkgit:git[1] suite
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index bccfdf7fde..6cca8bb51d 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -434,7 +434,8 @@ If the '--autosquash' option is enabled by default using the
configuration variable `rebase.autoSquash`, this option can be
used to override and disable this setting.
---[no-]autostash::
+--autostash::
+--no-autostash::
Automatically create a temporary stash before the operation
begins, and apply it after the operation ends. This means
that you can run rebase on a dirty worktree. However, use
diff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt
index 4c6d6de7b7..3c9bf45829 100644
--- a/Documentation/git-remote.txt
+++ b/Documentation/git-remote.txt
@@ -15,6 +15,7 @@ SYNOPSIS
'git remote remove' <name>
'git remote set-head' <name> (-a | --auto | -d | --delete | <branch>)
'git remote set-branches' [--add] <name> <branch>...
+'git remote get-url' [--push] [--all] <name>
'git remote set-url' [--push] <name> <newurl> [<oldurl>]
'git remote set-url --add' [--push] <name> <newurl>
'git remote set-url --delete' [--push] <name> <url>
@@ -131,6 +132,15 @@ The named branches will be interpreted as if specified with the
With `--add`, instead of replacing the list of currently tracked
branches, adds to that list.
+'get-url'::
+
+Retrieves the URLs for a remote. Configurations for `insteadOf` and
+`pushInsteadOf` are expanded here. By default, only the first URL is listed.
++
+With '--push', push URLs are queried rather than fetch URLs.
++
+With '--all', all URLs for the remote will be listed.
+
'set-url'::
Changes URLs for the remote. Sets first URL for remote <name> that matches
diff --git a/Documentation/git-rev-list.txt b/Documentation/git-rev-list.txt
index 7b49c85347..ef22f1775b 100644
--- a/Documentation/git-rev-list.txt
+++ b/Documentation/git-rev-list.txt
@@ -45,7 +45,7 @@ SYNOPSIS
[ --regexp-ignore-case | -i ]
[ --extended-regexp | -E ]
[ --fixed-strings | -F ]
- [ --date=(local|relative|default|iso|iso-strict|rfc|short) ]
+ [ --date=<format>]
[ [ --objects | --objects-edge | --objects-edge-aggressive ]
[ --unpacked ] ]
[ --pretty | --header ]
diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt
index 375213fe46..92df596e5f 100644
--- a/Documentation/git-stash.txt
+++ b/Documentation/git-stash.txt
@@ -95,6 +95,8 @@ show [<stash>]::
shows the latest one. By default, the command shows the diffstat, but
it will accept any format known to 'git diff' (e.g., `git stash show
-p stash@{1}` to view the second most recent stash in patch form).
+ You can use stash.showStat and/or stash.showPatch config variables
+ to change the default behavior.
pop [--index] [-q|--quiet] [<stash>]::
diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt
index 08b4dfbf1b..7220e5eca1 100644
--- a/Documentation/git-tag.txt
+++ b/Documentation/git-tag.txt
@@ -13,7 +13,8 @@ SYNOPSIS
<tagname> [<commit> | <object>]
'git tag' -d <tagname>...
'git tag' [-n[<num>]] -l [--contains <commit>] [--points-at <object>]
- [--column[=<options>] | --no-column] [--create-reflog] [<pattern>...]
+ [--column[=<options>] | --no-column] [--create-reflog] [--sort=<key>]
+ [--format=<format>] [--[no-]merged [<commit>]] [<pattern>...]
'git tag' -v <tagname>...
DESCRIPTION
@@ -94,14 +95,16 @@ OPTIONS
using fnmatch(3)). Multiple patterns may be given; if any of
them matches, the tag is shown.
---sort=<type>::
- Sort in a specific order. Supported type is "refname"
- (lexicographic order), "version:refname" or "v:refname" (tag
+--sort=<key>::
+ Sort based on the key given. Prefix `-` to sort in
+ descending order of the value. You may use the --sort=<key> option
+ multiple times, in which case the last key becomes the primary
+ key. Also supports "version:refname" or "v:refname" (tag
names are treated as versions). The "version:refname" sort
order can also be affected by the
- "versionsort.prereleaseSuffix" configuration variable. Prepend
- "-" to reverse sort order. When this option is not given, the
- sort order defaults to the value configured for the 'tag.sort'
+ "versionsort.prereleaseSuffix" configuration variable.
+ The keys supported are the same as those in `git for-each-ref`.
+ Sort order defaults to the value configured for the 'tag.sort'
variable if it exists, or lexicographic order otherwise. See
linkgit:git-config[1].
@@ -156,6 +159,16 @@ This option is only applicable when listing tags without annotation lines.
The object that the new tag will refer to, usually a commit.
Defaults to HEAD.
+<format>::
+ A string that interpolates `%(fieldname)` from the object
+ pointed at by a ref being shown. The format is the same as
+ that of linkgit:git-for-each-ref[1]. When unspecified,
+ defaults to `%(refname:short)`.
+
+--[no-]merged [<commit>]::
+ Only list tags whose tips are reachable, or not reachable
+ if '--no-merged' is used, from the specified commit ('HEAD'
+ if not specified).
CONFIGURATION
-------------
diff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt
index fb68156cf8..5b9ad0429c 100644
--- a/Documentation/git-worktree.txt
+++ b/Documentation/git-worktree.txt
@@ -11,6 +11,7 @@ SYNOPSIS
[verse]
'git worktree add' [-f] [--detach] [-b <new-branch>] <path> [<branch>]
'git worktree prune' [-n] [-v] [--expire <expire>]
+'git worktree list' [--porcelain]
DESCRIPTION
-----------
@@ -59,6 +60,13 @@ prune::
Prune working tree information in $GIT_DIR/worktrees.
+list::
+
+List details of each worktree. The main worktree is listed first, followed by
+each of the linked worktrees. The output details include if the worktree is
+bare, the revision currently checked out, and the branch currently checked out
+(or 'detached HEAD' if none).
+
OPTIONS
-------
@@ -86,6 +94,11 @@ OPTIONS
With `prune`, do not remove anything; just report what it would
remove.
+--porcelain::
+ With `list`, output in an easy-to-parse format for scripts.
+ This format will remain stable across Git versions and regardless of user
+ configuration. See below for details.
+
-v::
--verbose::
With `prune`, report all removals.
@@ -134,6 +147,41 @@ to `/path/main/.git/worktrees/test-next` then a file named
`test-next` entry from being pruned. See
linkgit:gitrepository-layout[5] for details.
+LIST OUTPUT FORMAT
+------------------
+The worktree list command has two output formats. The default format shows the
+details on a single line with columns. For example:
+
+------------
+S git worktree list
+/path/to/bare-source (bare)
+/path/to/linked-worktree abcd1234 [master]
+/path/to/other-linked-worktree 1234abc (detached HEAD)
+------------
+
+Porcelain Format
+~~~~~~~~~~~~~~~~
+The porcelain format has a line per attribute. Attributes are listed with a
+label and value separated by a single space. Boolean attributes (like 'bare'
+and 'detached') are listed as a label only, and are only present if and only
+if the value is true. An empty line indicates the end of a worktree. For
+example:
+
+------------
+S git worktree list --porcelain
+worktree /path/to/bare-source
+bare
+
+worktree /path/to/linked-worktree
+HEAD abcd1234abcd1234abcd1234abcd1234abcd1234
+branch refs/heads/master
+
+worktree /path/to/other-linked-worktree
+HEAD 1234abc1234abc1234abc1234abc1234abc1234a
+detached
+
+------------
+
EXAMPLES
--------
You are in the middle of a refactoring session and your boss comes in and
@@ -167,7 +215,6 @@ performed manually, such as:
- `remove` to remove a linked working tree and its administrative files (and
warn if the working tree is dirty)
- `mv` to move or rename a working tree and update its administrative files
-- `list` to list linked working trees
- `lock` to prevent automatic pruning of administrative files (for instance,
for a working tree on a portable device)
diff --git a/Documentation/gitignore.txt b/Documentation/gitignore.txt
index 473623d631..79a1948a0b 100644
--- a/Documentation/gitignore.txt
+++ b/Documentation/gitignore.txt
@@ -82,12 +82,12 @@ PATTERN FORMAT
- An optional prefix "`!`" which negates the pattern; any
matching file excluded by a previous pattern will become
- included again. It is not possible to re-include a file if a parent
- directory of that file is excluded. Git doesn't list excluded
- directories for performance reasons, so any patterns on contained
- files have no effect, no matter where they are defined.
+ included again.
Put a backslash ("`\`") in front of the first "`!`" for patterns
that begin with a literal "`!`", for example, "`\!important!.txt`".
+ It is possible to re-include a file if a parent directory of that
+ file is excluded if certain conditions are met. See section NOTES
+ for detail.
- If the pattern ends with a slash, it is removed for the
purpose of the following description, but it would only find
@@ -141,6 +141,21 @@ not tracked by Git remain untracked.
To stop tracking a file that is currently tracked, use
'git rm --cached'.
+To re-include files or directories when their parent directory is
+excluded, the following conditions must be met:
+
+ - The rules to exclude a directory and re-include a subset back must
+ be in the same .gitignore file.
+
+ - The directory part in the re-include rules must be literal (i.e. no
+ wildcards)
+
+ - The rules to exclude the parent directory must not end with a
+ trailing slash.
+
+ - The rules to exclude the parent directory must have at least one
+ slash.
+
EXAMPLES
--------
diff --git a/Documentation/glossary-content.txt b/Documentation/glossary-content.txt
index 8c6478b2f2..e225974253 100644
--- a/Documentation/glossary-content.txt
+++ b/Documentation/glossary-content.txt
@@ -413,8 +413,9 @@ exclude;;
[[def_per_worktree_ref]]per-worktree ref::
Refs that are per-<<def_working_tree,worktree>>, rather than
- global. This is presently only <<def_HEAD,HEAD>>, but might
- later include other unusual refs.
+ global. This is presently only <<def_HEAD,HEAD>> and any refs
+ that start with `refs/bisect/`, but might later include other
+ unusual refs.
[[def_pseudoref]]pseudoref::
Pseudorefs are a class of files under `$GIT_DIR` which behave
diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt
index f1c52208f0..4f009d4424 100644
--- a/Documentation/rev-list-options.txt
+++ b/Documentation/rev-list-options.txt
@@ -701,15 +701,19 @@ include::pretty-options.txt[]
--relative-date::
Synonym for `--date=relative`.
---date=(relative|local|default|iso|iso-strict|rfc|short|raw)::
+--date=<format>::
Only takes effect for dates shown in human-readable format, such
as when using `--pretty`. `log.date` config variable sets a default
- value for the log command's `--date` option.
+ value for the log command's `--date` option. By default, dates
+ are shown in the original time zone (either committer's or
+ author's). If `-local` is appended to the format (e.g.,
+ `iso-local`), the user's local time zone is used instead.
+
`--date=relative` shows dates relative to the current time,
-e.g. ``2 hours ago''.
+e.g. ``2 hours ago''. The `-local` option cannot be used with
+`--raw` or `--relative`.
+
-`--date=local` shows timestamps in user's local time zone.
+`--date=local` is an alias for `--date=default-local`.
+
`--date=iso` (or `--date=iso8601`) shows timestamps in a ISO 8601-like format.
The differences to the strict ISO 8601 format are:
@@ -732,10 +736,15 @@ format, often found in email messages.
`--date=format:...` feeds the format `...` to your system `strftime`.
Use `--date=format:%c` to show the date in your system locale's
preferred format. See the `strftime` manual for a complete list of
-format placeholders.
+format placeholders. When using `-local`, the correct syntax is
+`--date=format-local:...`.
+
-`--date=default` shows timestamps in the original time zone
-(either committer's or author's).
+`--date=default` is the default format, and is similar to
+`--date=rfc2822`, with a few exceptions:
+
+ - there is no comma after the day-of-week
+
+ - the time zone is omitted when the local time zone is used
ifdef::git-rev-list[]
--header::
diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
index 787670926a..94e40c4000 100755
--- a/GIT-VERSION-GEN
+++ b/GIT-VERSION-GEN
@@ -1,7 +1,7 @@
#!/bin/sh
GVF=GIT-VERSION-FILE
-DEF_VER=v2.6.2
+DEF_VER=v2.6.0.GIT
LF='
'
diff --git a/Makefile b/Makefile
index 04c2231023..8466333027 100644
--- a/Makefile
+++ b/Makefile
@@ -74,8 +74,6 @@ all::
# Define HAVE_PATHS_H if you have paths.h and want to use the default PATH
# it specifies.
#
-# Define NO_D_INO_IN_DIRENT if you don't have d_ino in your struct dirent.
-#
# Define NO_D_TYPE_IN_DIRENT if your platform defines DT_UNKNOWN but lacks
# d_type in struct dirent (Cygwin 1.5, fixed in Cygwin 1.7).
#
@@ -810,6 +808,7 @@ LIB_OBJS += version.o
LIB_OBJS += versioncmp.o
LIB_OBJS += walker.o
LIB_OBJS += wildmatch.o
+LIB_OBJS += worktree.o
LIB_OBJS += wrapper.o
LIB_OBJS += write_or_die.o
LIB_OBJS += ws.o
@@ -905,6 +904,7 @@ BUILTIN_OBJS += builtin/shortlog.o
BUILTIN_OBJS += builtin/show-branch.o
BUILTIN_OBJS += builtin/show-ref.o
BUILTIN_OBJS += builtin/stripspace.o
+BUILTIN_OBJS += builtin/submodule--helper.o
BUILTIN_OBJS += builtin/symbolic-ref.o
BUILTIN_OBJS += builtin/tag.o
BUILTIN_OBJS += builtin/unpack-file.o
@@ -1163,9 +1163,6 @@ endif
ifdef NO_D_TYPE_IN_DIRENT
BASIC_CFLAGS += -DNO_D_TYPE_IN_DIRENT
endif
-ifdef NO_D_INO_IN_DIRENT
- BASIC_CFLAGS += -DNO_D_INO_IN_DIRENT
-endif
ifdef NO_GECOS_IN_PWENT
BASIC_CFLAGS += -DNO_GECOS_IN_PWENT
endif
diff --git a/RelNotes b/RelNotes
index 022358074d..3ba13ce253 120000
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/2.6.2.txt \ No newline at end of file
+Documentation/RelNotes/2.7.0.txt \ No newline at end of file
diff --git a/archive-tar.c b/archive-tar.c
index 0d1e6bd754..501ca97760 100644
--- a/archive-tar.c
+++ b/archive-tar.c
@@ -167,21 +167,21 @@ static void prepare_header(struct archiver_args *args,
struct ustar_header *header,
unsigned int mode, unsigned long size)
{
- sprintf(header->mode, "%07o", mode & 07777);
- sprintf(header->size, "%011lo", S_ISREG(mode) ? size : 0);
- sprintf(header->mtime, "%011lo", (unsigned long) args->time);
+ xsnprintf(header->mode, sizeof(header->mode), "%07o", mode & 07777);
+ xsnprintf(header->size, sizeof(header->size), "%011lo", S_ISREG(mode) ? size : 0);
+ xsnprintf(header->mtime, sizeof(header->mtime), "%011lo", (unsigned long) args->time);
- sprintf(header->uid, "%07o", 0);
- sprintf(header->gid, "%07o", 0);
+ xsnprintf(header->uid, sizeof(header->uid), "%07o", 0);
+ xsnprintf(header->gid, sizeof(header->gid), "%07o", 0);
strlcpy(header->uname, "root", sizeof(header->uname));
strlcpy(header->gname, "root", sizeof(header->gname));
- sprintf(header->devmajor, "%07o", 0);
- sprintf(header->devminor, "%07o", 0);
+ xsnprintf(header->devmajor, sizeof(header->devmajor), "%07o", 0);
+ xsnprintf(header->devminor, sizeof(header->devminor), "%07o", 0);
memcpy(header->magic, "ustar", 6);
memcpy(header->version, "00", 2);
- sprintf(header->chksum, "%07o", ustar_header_chksum(header));
+ snprintf(header->chksum, sizeof(header->chksum), "%07o", ustar_header_chksum(header));
}
static int write_extended_header(struct archiver_args *args,
@@ -193,7 +193,7 @@ static int write_extended_header(struct archiver_args *args,
memset(&header, 0, sizeof(header));
*header.typeflag = TYPEFLAG_EXT_HEADER;
mode = 0100666;
- sprintf(header.name, "%s.paxheader", sha1_to_hex(sha1));
+ xsnprintf(header.name, sizeof(header.name), "%s.paxheader", sha1_to_hex(sha1));
prepare_header(args, &header, mode, size);
write_blocked(&header, sizeof(header));
write_blocked(buffer, size);
@@ -233,10 +233,10 @@ static int write_tar_entry(struct archiver_args *args,
size_t rest = pathlen - plen - 1;
if (plen > 0 && rest <= sizeof(header.name)) {
memcpy(header.prefix, path, plen);
- memcpy(header.name, path + plen + 1, rest);
+ memcpy(header.name, path + plen + 1, rest);
} else {
- sprintf(header.name, "%s.data",
- sha1_to_hex(sha1));
+ xsnprintf(header.name, sizeof(header.name), "%s.data",
+ sha1_to_hex(sha1));
strbuf_append_ext_header(&ext_header, "path",
path, pathlen);
}
@@ -259,8 +259,8 @@ static int write_tar_entry(struct archiver_args *args,
if (S_ISLNK(mode)) {
if (size > sizeof(header.linkname)) {
- sprintf(header.linkname, "see %s.paxheader",
- sha1_to_hex(sha1));
+ xsnprintf(header.linkname, sizeof(header.linkname),
+ "see %s.paxheader", sha1_to_hex(sha1));
strbuf_append_ext_header(&ext_header, "linkpath",
buffer, size);
} else
@@ -301,7 +301,7 @@ static int write_global_extended_header(struct archiver_args *args)
memset(&header, 0, sizeof(header));
*header.typeflag = TYPEFLAG_GLOBAL_HEADER;
mode = 0100666;
- strcpy(header.name, "pax_global_header");
+ xsnprintf(header.name, sizeof(header.name), "pax_global_header");
prepare_header(args, &header, mode, ext_header.len);
write_blocked(&header, sizeof(header));
write_blocked(ext_header.buf, ext_header.len);
diff --git a/archive.c b/archive.c
index 01b0899b3f..4ac86c8373 100644
--- a/archive.c
+++ b/archive.c
@@ -171,13 +171,14 @@ static void queue_directory(const unsigned char *sha1,
unsigned mode, int stage, struct archiver_context *c)
{
struct directory *d;
- d = xmallocz(sizeof(*d) + base->len + 1 + strlen(filename));
+ size_t len = base->len + 1 + strlen(filename) + 1;
+ d = xmalloc(sizeof(*d) + len);
d->up = c->bottom;
d->baselen = base->len;
d->mode = mode;
d->stage = stage;
c->bottom = d;
- d->len = sprintf(d->path, "%.*s%s/", (int)base->len, base->buf, filename);
+ d->len = xsnprintf(d->path, len, "%.*s%s/", (int)base->len, base->buf, filename);
hashcpy(d->oid.hash, sha1);
}
diff --git a/bisect.c b/bisect.c
index 041a13d093..053d1a2ab9 100644
--- a/bisect.c
+++ b/bisect.c
@@ -730,6 +730,11 @@ static void handle_bad_merge_base(void)
"This means the bug has been fixed "
"between %s and [%s].\n",
bad_hex, bad_hex, good_hex);
+ } else if (!strcmp(term_bad, "new") && !strcmp(term_good, "old")) {
+ fprintf(stderr, "The merge base %s is new.\n"
+ "The property has changed "
+ "between %s and [%s].\n",
+ bad_hex, bad_hex, good_hex);
} else {
fprintf(stderr, "The merge base %s is %s.\n"
"This means the first '%s' commit is "
@@ -762,11 +767,11 @@ static void handle_skipped_merge_base(const unsigned char *mb)
}
/*
- * "check_merge_bases" checks that merge bases are not "bad".
+ * "check_merge_bases" checks that merge bases are not "bad" (or "new").
*
- * - If one is "bad", it means the user assumed something wrong
+ * - If one is "bad" (or "new"), it means the user assumed something wrong
* and we must exit with a non 0 error code.
- * - If one is "good", that's good, we have nothing to do.
+ * - If one is "good" (or "old"), that's good, we have nothing to do.
* - If one is "skipped", we can't know but we should warn.
* - If we don't know, we should check it out and ask the user to test.
*/
diff --git a/branch.c b/branch.c
index d013374e5a..77d7f2a63e 100644
--- a/branch.c
+++ b/branch.c
@@ -4,6 +4,7 @@
#include "refs.h"
#include "remote.h"
#include "commit.h"
+#include "worktree.h"
struct tracking {
struct refspec spec;
@@ -311,84 +312,6 @@ void remove_branch_state(void)
unlink(git_path_squash_msg());
}
-static char *find_linked_symref(const char *symref, const char *branch,
- const char *id)
-{
- struct strbuf sb = STRBUF_INIT;
- struct strbuf path = STRBUF_INIT;
- struct strbuf gitdir = STRBUF_INIT;
- char *existing = NULL;
-
- /*
- * $GIT_COMMON_DIR/$symref (e.g. HEAD) is practically outside
- * $GIT_DIR so resolve_ref_unsafe() won't work (it uses
- * git_path). Parse the ref ourselves.
- */
- if (id)
- strbuf_addf(&path, "%s/worktrees/%s/%s", get_git_common_dir(), id, symref);
- else
- strbuf_addf(&path, "%s/%s", get_git_common_dir(), symref);
-
- if (!strbuf_readlink(&sb, path.buf, 0)) {
- if (!starts_with(sb.buf, "refs/") ||
- check_refname_format(sb.buf, 0))
- goto done;
- } else if (strbuf_read_file(&sb, path.buf, 0) >= 0 &&
- starts_with(sb.buf, "ref:")) {
- strbuf_remove(&sb, 0, strlen("ref:"));
- strbuf_trim(&sb);
- } else
- goto done;
- if (strcmp(sb.buf, branch))
- goto done;
- if (id) {
- strbuf_reset(&path);
- strbuf_addf(&path, "%s/worktrees/%s/gitdir", get_git_common_dir(), id);
- if (strbuf_read_file(&gitdir, path.buf, 0) <= 0)
- goto done;
- strbuf_rtrim(&gitdir);
- } else
- strbuf_addstr(&gitdir, get_git_common_dir());
- strbuf_strip_suffix(&gitdir, ".git");
-
- existing = strbuf_detach(&gitdir, NULL);
-done:
- strbuf_release(&path);
- strbuf_release(&sb);
- strbuf_release(&gitdir);
-
- return existing;
-}
-
-char *find_shared_symref(const char *symref, const char *target)
-{
- struct strbuf path = STRBUF_INIT;
- DIR *dir;
- struct dirent *d;
- char *existing;
-
- if ((existing = find_linked_symref(symref, target, NULL)))
- return existing;
-
- strbuf_addf(&path, "%s/worktrees", get_git_common_dir());
- dir = opendir(path.buf);
- strbuf_release(&path);
- if (!dir)
- return NULL;
-
- while ((d = readdir(dir)) != NULL) {
- if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
- continue;
- existing = find_linked_symref(symref, target, d->d_name);
- if (existing)
- goto done;
- }
-done:
- closedir(dir);
-
- return existing;
-}
-
void die_if_checked_out(const char *branch)
{
char *existing;
diff --git a/branch.h b/branch.h
index d3446ed73c..58aa45fe72 100644
--- a/branch.h
+++ b/branch.h
@@ -59,12 +59,4 @@ extern int read_branch_desc(struct strbuf *, const char *branch_name);
*/
extern void die_if_checked_out(const char *branch);
-/*
- * Check if a per-worktree symref points to a ref in the main worktree
- * or any linked worktree, and return the path to the exising worktree
- * if it is. Returns NULL if there is no existing ref. The caller is
- * responsible for freeing the returned path.
- */
-extern char *find_shared_symref(const char *symref, const char *target);
-
#endif
diff --git a/builtin.h b/builtin.h
index 79aaf0afe8..6b95006a0a 100644
--- a/builtin.h
+++ b/builtin.h
@@ -120,6 +120,7 @@ extern int cmd_show(int argc, const char **argv, const char *prefix);
extern int cmd_show_branch(int argc, const char **argv, const char *prefix);
extern int cmd_status(int argc, const char **argv, const char *prefix);
extern int cmd_stripspace(int argc, const char **argv, const char *prefix);
+extern int cmd_submodule__helper(int argc, const char **argv, const char *prefix);
extern int cmd_symbolic_ref(int argc, const char **argv, const char *prefix);
extern int cmd_tag(int argc, const char **argv, const char *prefix);
extern int cmd_tar_tree(int argc, const char **argv, const char *prefix);
diff --git a/builtin/am.c b/builtin/am.c
index 3bd4fd701b..6c8bbca226 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -1590,16 +1590,44 @@ static int build_fake_ancestor(const struct am_state *state, const char *index_f
}
/**
+ * Do the three-way merge using fake ancestor, his tree constructed
+ * from the fake ancestor and the postimage of the patch, and our
+ * state.
+ */
+static int run_fallback_merge_recursive(const struct am_state *state,
+ unsigned char *orig_tree,
+ unsigned char *our_tree,
+ unsigned char *his_tree)
+{
+ struct child_process cp = CHILD_PROCESS_INIT;
+ int status;
+
+ cp.git_cmd = 1;
+
+ argv_array_pushf(&cp.env_array, "GITHEAD_%s=%.*s",
+ sha1_to_hex(his_tree), linelen(state->msg), state->msg);
+ if (state->quiet)
+ argv_array_push(&cp.env_array, "GIT_MERGE_VERBOSITY=0");
+
+ argv_array_push(&cp.args, "merge-recursive");
+ argv_array_push(&cp.args, sha1_to_hex(orig_tree));
+ argv_array_push(&cp.args, "--");
+ argv_array_push(&cp.args, sha1_to_hex(our_tree));
+ argv_array_push(&cp.args, sha1_to_hex(his_tree));
+
+ status = run_command(&cp) ? (-1) : 0;
+ discard_cache();
+ read_cache();
+ return status;
+}
+
+/**
* Attempt a threeway merge, using index_path as the temporary index.
*/
static int fall_back_threeway(const struct am_state *state, const char *index_path)
{
unsigned char orig_tree[GIT_SHA1_RAWSZ], his_tree[GIT_SHA1_RAWSZ],
our_tree[GIT_SHA1_RAWSZ];
- const unsigned char *bases[1] = {orig_tree};
- struct merge_options o;
- struct commit *result;
- char *his_tree_name;
if (get_sha1("HEAD", our_tree) < 0)
hashcpy(our_tree, EMPTY_TREE_SHA1_BIN);
@@ -1651,22 +1679,11 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa
* changes.
*/
- init_merge_options(&o);
-
- o.branch1 = "HEAD";
- his_tree_name = xstrfmt("%.*s", linelen(state->msg), state->msg);
- o.branch2 = his_tree_name;
-
- if (state->quiet)
- o.verbosity = 0;
-
- if (merge_recursive_generic(&o, our_tree, his_tree, 1, bases, &result)) {
+ if (run_fallback_merge_recursive(state, orig_tree, our_tree, his_tree)) {
rerere(state->allow_rerere_autoupdate);
- free(his_tree_name);
return error(_("Failed to merge in the changes."));
}
- free(his_tree_name);
return 0;
}
diff --git a/builtin/apply.c b/builtin/apply.c
index 4aa53f7fd8..deb1364fa8 100644
--- a/builtin/apply.c
+++ b/builtin/apply.c
@@ -77,8 +77,7 @@ static enum ws_ignore {
static const char *patch_input_file;
-static const char *root;
-static int root_len;
+static struct strbuf root = STRBUF_INIT;
static int read_stdin = 1;
static int options;
@@ -494,8 +493,8 @@ static char *find_name_gnu(const char *line, const char *def, int p_value)
}
strbuf_remove(&name, 0, cp - name.buf);
- if (root)
- strbuf_insert(&name, 0, root, root_len);
+ if (root.len)
+ strbuf_insert(&name, 0, root.buf, root.len);
return squash_slash(strbuf_detach(&name, NULL));
}
@@ -697,11 +696,8 @@ static char *find_name_common(const char *line, const char *def,
return squash_slash(xstrdup(def));
}
- if (root) {
- char *ret = xmalloc(root_len + len + 1);
- strcpy(ret, root);
- memcpy(ret + root_len, start, len);
- ret[root_len + len] = '\0';
+ if (root.len) {
+ char *ret = xstrfmt("%s%.*s", root.buf, len, start);
return squash_slash(ret);
}
@@ -1277,8 +1273,8 @@ static int parse_git_header(const char *line, int len, unsigned int size, struct
* the default name from the header.
*/
patch->def_name = git_header_name(line, len);
- if (patch->def_name && root) {
- char *s = xstrfmt("%s%s", root, patch->def_name);
+ if (patch->def_name && root.len) {
+ char *s = xstrfmt("%s%s", root.buf, patch->def_name);
free(patch->def_name);
patch->def_name = s;
}
@@ -4501,14 +4497,9 @@ static int option_parse_whitespace(const struct option *opt,
static int option_parse_directory(const struct option *opt,
const char *arg, int unset)
{
- root_len = strlen(arg);
- if (root_len && arg[root_len - 1] != '/') {
- char *new_root;
- root = new_root = xmalloc(root_len + 2);
- strcpy(new_root, arg);
- strcpy(new_root + root_len++, "/");
- } else
- root = arg;
+ strbuf_reset(&root);
+ strbuf_addstr(&root, arg);
+ strbuf_complete(&root, '/');
return 0;
}
diff --git a/builtin/blame.c b/builtin/blame.c
index 295ce92da5..6fc7bff9a3 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -459,12 +459,13 @@ static void queue_blames(struct scoreboard *sb, struct origin *porigin,
static struct origin *make_origin(struct commit *commit, const char *path)
{
struct origin *o;
- o = xcalloc(1, sizeof(*o) + strlen(path) + 1);
+ size_t pathlen = strlen(path) + 1;
+ o = xcalloc(1, sizeof(*o) + pathlen);
o->commit = commit;
o->refcnt = 1;
o->next = commit->util;
commit->util = o;
- strcpy(o->path, path);
+ memcpy(o->path, path, pathlen); /* includes NUL */
return o;
}
@@ -1879,9 +1880,9 @@ static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent,
int cnt;
const char *cp;
struct origin *suspect = ent->suspect;
- char hex[41];
+ char hex[GIT_SHA1_HEXSZ + 1];
- strcpy(hex, sha1_to_hex(suspect->commit->object.sha1));
+ sha1_to_hex_r(hex, suspect->commit->object.sha1);
printf("%s %d %d %d\n",
hex,
ent->s_lno + 1,
@@ -1917,11 +1918,11 @@ static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt)
const char *cp;
struct origin *suspect = ent->suspect;
struct commit_info ci;
- char hex[41];
+ char hex[GIT_SHA1_HEXSZ + 1];
int show_raw_time = !!(opt & OUTPUT_RAW_TIMESTAMP);
get_commit_info(suspect->commit, &ci, 1);
- strcpy(hex, sha1_to_hex(suspect->commit->object.sha1));
+ sha1_to_hex_r(hex, suspect->commit->object.sha1);
cp = nth_line(sb, ent->lno);
for (cnt = 0; cnt < ent->num_lines; cnt++) {
@@ -2612,7 +2613,6 @@ parse_done:
fewer display columns. */
blame_date_width = utf8_strwidth(_("4 years, 11 months ago")) + 1; /* add the null */
break;
- case DATE_LOCAL:
case DATE_NORMAL:
blame_date_width = sizeof("Thu Oct 19 16:00:04 2006 -0700");
break;
diff --git a/builtin/branch.c b/builtin/branch.c
index ff05869949..01f9530822 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -19,18 +19,17 @@
#include "column.h"
#include "utf8.h"
#include "wt-status.h"
+#include "ref-filter.h"
static const char * const builtin_branch_usage[] = {
N_("git branch [<options>] [-r | -a] [--merged | --no-merged]"),
N_("git branch [<options>] [-l] [-f] <branch-name> [<start-point>]"),
N_("git branch [<options>] [-r] (-d | -D) <branch-name>..."),
N_("git branch [<options>] (-m | -M) [<old-branch>] <new-branch>"),
+ N_("git branch [<options>] [-r | -a] [--points-at]"),
NULL
};
-#define REF_LOCAL_BRANCH 0x01
-#define REF_REMOTE_BRANCH 0x02
-
static const char *head;
static unsigned char head_sha1[20];
@@ -52,13 +51,6 @@ enum color_branch {
BRANCH_COLOR_UPSTREAM = 5
};
-static enum merge_filter {
- NO_FILTER = 0,
- SHOW_NOT_MERGED,
- SHOW_MERGED
-} merge_filter;
-static unsigned char merge_filter_ref[20];
-
static struct string_list output = STRING_LIST_INIT_DUP;
static unsigned int colopts;
@@ -121,7 +113,7 @@ static int branch_merged(int kind, const char *name,
void *reference_name_to_free = NULL;
int merged;
- if (kind == REF_LOCAL_BRANCH) {
+ if (kind == FILTER_REFS_BRANCHES) {
struct branch *branch = branch_get(name);
const char *upstream = branch_get_upstream(branch, NULL);
unsigned char sha1[20];
@@ -199,14 +191,14 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
struct strbuf bname = STRBUF_INIT;
switch (kinds) {
- case REF_REMOTE_BRANCH:
+ case FILTER_REFS_REMOTES:
fmt = "refs/remotes/%s";
/* For subsequent UI messages */
remote_branch = 1;
force = 1;
break;
- case REF_LOCAL_BRANCH:
+ case FILTER_REFS_BRANCHES:
fmt = "refs/heads/%s";
break;
default:
@@ -223,7 +215,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
int flags = 0;
strbuf_branchname(&bname, argv[i]);
- if (kinds == REF_LOCAL_BRANCH && !strcmp(head, bname.buf)) {
+ if (kinds == FILTER_REFS_BRANCHES && !strcmp(head, bname.buf)) {
error(_("Cannot delete the branch '%s' "
"which you are currently on."), bname.buf);
ret = 1;
@@ -279,147 +271,6 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
return(ret);
}
-struct ref_item {
- char *name;
- char *dest;
- unsigned int kind, width;
- struct commit *commit;
- int ignore;
-};
-
-struct ref_list {
- struct rev_info revs;
- int index, alloc, maxwidth, verbose, abbrev;
- struct ref_item *list;
- struct commit_list *with_commit;
- int kinds;
-};
-
-static char *resolve_symref(const char *src, const char *prefix)
-{
- unsigned char sha1[20];
- int flag;
- const char *dst;
-
- dst = resolve_ref_unsafe(src, 0, sha1, &flag);
- if (!(dst && (flag & REF_ISSYMREF)))
- return NULL;
- if (prefix)
- skip_prefix(dst, prefix, &dst);
- return xstrdup(dst);
-}
-
-struct append_ref_cb {
- struct ref_list *ref_list;
- const char **pattern;
- int ret;
-};
-
-static int match_patterns(const char **pattern, const char *refname)
-{
- if (!*pattern)
- return 1; /* no pattern always matches */
- while (*pattern) {
- if (!wildmatch(*pattern, refname, 0, NULL))
- return 1;
- pattern++;
- }
- return 0;
-}
-
-static int append_ref(const char *refname, const struct object_id *oid, int flags, void *cb_data)
-{
- struct append_ref_cb *cb = (struct append_ref_cb *)(cb_data);
- struct ref_list *ref_list = cb->ref_list;
- struct ref_item *newitem;
- struct commit *commit;
- int kind, i;
- const char *prefix, *orig_refname = refname;
-
- static struct {
- int kind;
- const char *prefix;
- } ref_kind[] = {
- { REF_LOCAL_BRANCH, "refs/heads/" },
- { REF_REMOTE_BRANCH, "refs/remotes/" },
- };
-
- /* Detect kind */
- for (i = 0; i < ARRAY_SIZE(ref_kind); i++) {
- prefix = ref_kind[i].prefix;
- if (skip_prefix(refname, prefix, &refname)) {
- kind = ref_kind[i].kind;
- break;
- }
- }
- if (ARRAY_SIZE(ref_kind) <= i)
- return 0;
-
- /* Don't add types the caller doesn't want */
- if ((kind & ref_list->kinds) == 0)
- return 0;
-
- if (!match_patterns(cb->pattern, refname))
- return 0;
-
- commit = NULL;
- if (ref_list->verbose || ref_list->with_commit || merge_filter != NO_FILTER) {
- commit = lookup_commit_reference_gently(oid->hash, 1);
- if (!commit) {
- cb->ret = error(_("branch '%s' does not point at a commit"), refname);
- return 0;
- }
-
- /* Filter with with_commit if specified */
- if (!is_descendant_of(commit, ref_list->with_commit))
- return 0;
-
- if (merge_filter != NO_FILTER)
- add_pending_object(&ref_list->revs,
- (struct object *)commit, refname);
- }
-
- ALLOC_GROW(ref_list->list, ref_list->index + 1, ref_list->alloc);
-
- /* Record the new item */
- newitem = &(ref_list->list[ref_list->index++]);
- newitem->name = xstrdup(refname);
- newitem->kind = kind;
- newitem->commit = commit;
- newitem->width = utf8_strwidth(refname);
- newitem->dest = resolve_symref(orig_refname, prefix);
- newitem->ignore = 0;
- /* adjust for "remotes/" */
- if (newitem->kind == REF_REMOTE_BRANCH &&
- ref_list->kinds != REF_REMOTE_BRANCH)
- newitem->width += 8;
- if (newitem->width > ref_list->maxwidth)
- ref_list->maxwidth = newitem->width;
-
- return 0;
-}
-
-static void free_ref_list(struct ref_list *ref_list)
-{
- int i;
-
- for (i = 0; i < ref_list->index; i++) {
- free(ref_list->list[i].name);
- free(ref_list->list[i].dest);
- }
- free(ref_list->list);
-}
-
-static int ref_cmp(const void *r1, const void *r2)
-{
- struct ref_item *c1 = (struct ref_item *)(r1);
- struct ref_item *c2 = (struct ref_item *)(r2);
-
- if (c1->kind != c2->kind)
- return c1->kind - c2->kind;
- return strcmp(c1->name, c2->name);
-}
-
static void fill_tracking_info(struct strbuf *stat, const char *branch_name,
int show_upstream_ref)
{
@@ -482,8 +333,8 @@ static void fill_tracking_info(struct strbuf *stat, const char *branch_name,
free(ref);
}
-static void add_verbose_info(struct strbuf *out, struct ref_item *item,
- int verbose, int abbrev)
+static void add_verbose_info(struct strbuf *out, struct ref_array_item *item,
+ struct ref_filter *filter, const char *refname)
{
struct strbuf subject = STRBUF_INIT, stat = STRBUF_INIT;
const char *sub = _(" **** invalid ref ****");
@@ -494,32 +345,74 @@ static void add_verbose_info(struct strbuf *out, struct ref_item *item,
sub = subject.buf;
}
- if (item->kind == REF_LOCAL_BRANCH)
- fill_tracking_info(&stat, item->name, verbose > 1);
+ if (item->kind == FILTER_REFS_BRANCHES)
+ fill_tracking_info(&stat, refname, filter->verbose > 1);
strbuf_addf(out, " %s %s%s",
- find_unique_abbrev(item->commit->object.sha1, abbrev),
+ find_unique_abbrev(item->commit->object.sha1, filter->abbrev),
stat.buf, sub);
strbuf_release(&stat);
strbuf_release(&subject);
}
-static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
- int abbrev, int current, char *prefix)
+static char *get_head_description(void)
+{
+ struct strbuf desc = STRBUF_INIT;
+ struct wt_status_state state;
+ memset(&state, 0, sizeof(state));
+ wt_status_get_state(&state, 1);
+ if (state.rebase_in_progress ||
+ state.rebase_interactive_in_progress)
+ strbuf_addf(&desc, _("(no branch, rebasing %s)"),
+ state.branch);
+ else if (state.bisect_in_progress)
+ strbuf_addf(&desc, _("(no branch, bisect started on %s)"),
+ state.branch);
+ else if (state.detached_from) {
+ /* TRANSLATORS: make sure these match _("HEAD detached at ")
+ and _("HEAD detached from ") in wt-status.c */
+ if (state.detached_at)
+ strbuf_addf(&desc, _("(HEAD detached at %s)"),
+ state.detached_from);
+ else
+ strbuf_addf(&desc, _("(HEAD detached from %s)"),
+ state.detached_from);
+ }
+ else
+ strbuf_addstr(&desc, _("(no branch)"));
+ free(state.branch);
+ free(state.onto);
+ free(state.detached_from);
+ return strbuf_detach(&desc, NULL);
+}
+
+static void format_and_print_ref_item(struct ref_array_item *item, int maxwidth,
+ struct ref_filter *filter, const char *remote_prefix)
{
char c;
+ int current = 0;
int color;
struct strbuf out = STRBUF_INIT, name = STRBUF_INIT;
-
- if (item->ignore)
- return;
+ const char *prefix = "";
+ const char *desc = item->refname;
+ char *to_free = NULL;
switch (item->kind) {
- case REF_LOCAL_BRANCH:
- color = BRANCH_COLOR_LOCAL;
+ case FILTER_REFS_BRANCHES:
+ skip_prefix(desc, "refs/heads/", &desc);
+ if (!filter->detached && !strcmp(desc, head))
+ current = 1;
+ else
+ color = BRANCH_COLOR_LOCAL;
break;
- case REF_REMOTE_BRANCH:
+ case FILTER_REFS_REMOTES:
+ skip_prefix(desc, "refs/remotes/", &desc);
color = BRANCH_COLOR_REMOTE;
+ prefix = remote_prefix;
+ break;
+ case FILTER_REFS_DETACHED_HEAD:
+ desc = to_free = get_head_description();
+ current = 1;
break;
default:
color = BRANCH_COLOR_PLAIN;
@@ -532,8 +425,8 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
color = BRANCH_COLOR_CURRENT;
}
- strbuf_addf(&name, "%s%s", prefix, item->name);
- if (verbose) {
+ strbuf_addf(&name, "%s%s", prefix, desc);
+ if (filter->verbose) {
int utf8_compensation = strlen(name.buf) - utf8_strwidth(name.buf);
strbuf_addf(&out, "%c %s%-*s%s", c, branch_get_color(color),
maxwidth + utf8_compensation, name.buf,
@@ -542,155 +435,82 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
strbuf_addf(&out, "%c %s%s%s", c, branch_get_color(color),
name.buf, branch_get_color(BRANCH_COLOR_RESET));
- if (item->dest)
- strbuf_addf(&out, " -> %s", item->dest);
- else if (verbose)
+ if (item->symref) {
+ skip_prefix(item->symref, "refs/remotes/", &desc);
+ strbuf_addf(&out, " -> %s", desc);
+ }
+ else if (filter->verbose)
/* " f7c0c00 [ahead 58, behind 197] vcs-svn: drop obj_pool.h" */
- add_verbose_info(&out, item, verbose, abbrev);
+ add_verbose_info(&out, item, filter, desc);
if (column_active(colopts)) {
- assert(!verbose && "--column and --verbose are incompatible");
+ assert(!filter->verbose && "--column and --verbose are incompatible");
string_list_append(&output, out.buf);
} else {
printf("%s\n", out.buf);
}
strbuf_release(&name);
strbuf_release(&out);
+ free(to_free);
}
-static int calc_maxwidth(struct ref_list *refs)
-{
- int i, w = 0;
- for (i = 0; i < refs->index; i++) {
- if (refs->list[i].ignore)
- continue;
- if (refs->list[i].width > w)
- w = refs->list[i].width;
- }
- return w;
-}
-
-static char *get_head_description(void)
-{
- struct strbuf desc = STRBUF_INIT;
- struct wt_status_state state;
- memset(&state, 0, sizeof(state));
- wt_status_get_state(&state, 1);
- if (state.rebase_in_progress ||
- state.rebase_interactive_in_progress)
- strbuf_addf(&desc, _("(no branch, rebasing %s)"),
- state.branch);
- else if (state.bisect_in_progress)
- strbuf_addf(&desc, _("(no branch, bisect started on %s)"),
- state.branch);
- else if (state.detached_from) {
- /* TRANSLATORS: make sure these match _("HEAD detached at ")
- and _("HEAD detached from ") in wt-status.c */
- if (state.detached_at)
- strbuf_addf(&desc, _("(HEAD detached at %s)"),
- state.detached_from);
- else
- strbuf_addf(&desc, _("(HEAD detached from %s)"),
- state.detached_from);
- }
- else
- strbuf_addstr(&desc, _("(no branch)"));
- free(state.branch);
- free(state.onto);
- free(state.detached_from);
- return strbuf_detach(&desc, NULL);
-}
-
-static void show_detached(struct ref_list *ref_list)
+static int calc_maxwidth(struct ref_array *refs, int remote_bonus)
{
- struct commit *head_commit = lookup_commit_reference_gently(head_sha1, 1);
-
- if (head_commit && is_descendant_of(head_commit, ref_list->with_commit)) {
- struct ref_item item;
- item.name = get_head_description();
- item.width = utf8_strwidth(item.name);
- item.kind = REF_LOCAL_BRANCH;
- item.dest = NULL;
- item.commit = head_commit;
- item.ignore = 0;
- if (item.width > ref_list->maxwidth)
- ref_list->maxwidth = item.width;
- print_ref_item(&item, ref_list->maxwidth, ref_list->verbose, ref_list->abbrev, 1, "");
- free(item.name);
+ int i, max = 0;
+ for (i = 0; i < refs->nr; i++) {
+ struct ref_array_item *it = refs->items[i];
+ const char *desc = it->refname;
+ int w;
+
+ skip_prefix(it->refname, "refs/heads/", &desc);
+ skip_prefix(it->refname, "refs/remotes/", &desc);
+ w = utf8_strwidth(desc);
+
+ if (it->kind == FILTER_REFS_REMOTES)
+ w += remote_bonus;
+ if (w > max)
+ max = w;
}
+ return max;
}
-static int print_ref_list(int kinds, int detached, int verbose, int abbrev, struct commit_list *with_commit, const char **pattern)
+static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sorting)
{
int i;
- struct append_ref_cb cb;
- struct ref_list ref_list;
-
- memset(&ref_list, 0, sizeof(ref_list));
- ref_list.kinds = kinds;
- ref_list.verbose = verbose;
- ref_list.abbrev = abbrev;
- ref_list.with_commit = with_commit;
- if (merge_filter != NO_FILTER)
- init_revisions(&ref_list.revs, NULL);
- cb.ref_list = &ref_list;
- cb.pattern = pattern;
- cb.ret = 0;
- for_each_rawref(append_ref, &cb);
- if (merge_filter != NO_FILTER) {
- struct commit *filter;
- filter = lookup_commit_reference_gently(merge_filter_ref, 0);
- if (!filter)
- die(_("object '%s' does not point to a commit"),
- sha1_to_hex(merge_filter_ref));
-
- filter->object.flags |= UNINTERESTING;
- add_pending_object(&ref_list.revs,
- (struct object *) filter, "");
- ref_list.revs.limited = 1;
-
- if (prepare_revision_walk(&ref_list.revs))
- die(_("revision walk setup failed"));
-
- for (i = 0; i < ref_list.index; i++) {
- struct ref_item *item = &ref_list.list[i];
- struct commit *commit = item->commit;
- int is_merged = !!(commit->object.flags & UNINTERESTING);
- item->ignore = is_merged != (merge_filter == SHOW_MERGED);
- }
+ struct ref_array array;
+ int maxwidth = 0;
+ const char *remote_prefix = "";
- for (i = 0; i < ref_list.index; i++) {
- struct ref_item *item = &ref_list.list[i];
- clear_commit_marks(item->commit, ALL_REV_FLAGS);
- }
- clear_commit_marks(filter, ALL_REV_FLAGS);
+ /*
+ * If we are listing more than just remote branches,
+ * then remote branches will have a "remotes/" prefix.
+ * We need to account for this in the width.
+ */
+ if (filter->kind != FILTER_REFS_REMOTES)
+ remote_prefix = "remotes/";
- if (verbose)
- ref_list.maxwidth = calc_maxwidth(&ref_list);
- }
+ memset(&array, 0, sizeof(array));
- qsort(ref_list.list, ref_list.index, sizeof(struct ref_item), ref_cmp);
-
- detached = (detached && (kinds & REF_LOCAL_BRANCH));
- if (detached && match_patterns(pattern, "HEAD"))
- show_detached(&ref_list);
-
- for (i = 0; i < ref_list.index; i++) {
- int current = !detached &&
- (ref_list.list[i].kind == REF_LOCAL_BRANCH) &&
- !strcmp(ref_list.list[i].name, head);
- char *prefix = (kinds != REF_REMOTE_BRANCH &&
- ref_list.list[i].kind == REF_REMOTE_BRANCH)
- ? "remotes/" : "";
- print_ref_item(&ref_list.list[i], ref_list.maxwidth, verbose,
- abbrev, current, prefix);
- }
+ verify_ref_format("%(refname)%(symref)");
+ filter_refs(&array, filter, filter->kind | FILTER_REFS_INCLUDE_BROKEN);
- free_ref_list(&ref_list);
+ if (filter->verbose)
+ maxwidth = calc_maxwidth(&array, strlen(remote_prefix));
- if (cb.ret)
- error(_("some refs could not be read"));
+ /*
+ * If no sorting parameter is given then we default to sorting
+ * by 'refname'. This would give us an alphabetically sorted
+ * array with the 'HEAD' ref at the beginning followed by
+ * local branches 'refs/heads/...' and finally remote-tacking
+ * branches 'refs/remotes/...'.
+ */
+ if (!sorting)
+ sorting = ref_default_sorting();
+ ref_array_sort(sorting, &array);
+
+ for (i = 0; i < array.nr; i++)
+ format_and_print_ref_item(array.items[i], maxwidth, filter, remote_prefix);
- return cb.ret;
+ ref_array_clear(&array);
}
static void rename_branch(const char *oldname, const char *newname, int force)
@@ -746,20 +566,6 @@ static void rename_branch(const char *oldname, const char *newname, int force)
strbuf_release(&newsection);
}
-static int opt_parse_merge_filter(const struct option *opt, const char *arg, int unset)
-{
- merge_filter = ((opt->long_name[0] == 'n')
- ? SHOW_NOT_MERGED
- : SHOW_MERGED);
- if (unset)
- merge_filter = SHOW_NOT_MERGED; /* b/c for --no-merged */
- if (!arg)
- arg = "HEAD";
- if (get_sha1(arg, merge_filter_ref))
- die(_("malformed object name %s"), arg);
- return 0;
-}
-
static const char edit_description[] = "BRANCH_DESCRIPTION";
static int edit_branch_description(const char *branch_name)
@@ -799,17 +605,16 @@ static int edit_branch_description(const char *branch_name)
int cmd_branch(int argc, const char **argv, const char *prefix)
{
int delete = 0, rename = 0, force = 0, list = 0;
- int verbose = 0, abbrev = -1, detached = 0;
int reflog = 0, edit_description = 0;
int quiet = 0, unset_upstream = 0;
const char *new_upstream = NULL;
enum branch_track track;
- int kinds = REF_LOCAL_BRANCH;
- struct commit_list *with_commit = NULL;
+ struct ref_filter filter;
+ static struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;
struct option options[] = {
OPT_GROUP(N_("Generic options")),
- OPT__VERBOSE(&verbose,
+ OPT__VERBOSE(&filter.verbose,
N_("show hash and subject, give twice for upstream branch")),
OPT__QUIET(&quiet, N_("suppress informational messages")),
OPT_SET_INT('t', "track", &track, N_("set up tracking mode (see git-pull(1))"),
@@ -819,25 +624,15 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
OPT_STRING('u', "set-upstream-to", &new_upstream, "upstream", "change the upstream info"),
OPT_BOOL(0, "unset-upstream", &unset_upstream, "Unset the upstream info"),
OPT__COLOR(&branch_use_color, N_("use colored output")),
- OPT_SET_INT('r', "remotes", &kinds, N_("act on remote-tracking branches"),
- REF_REMOTE_BRANCH),
- {
- OPTION_CALLBACK, 0, "contains", &with_commit, N_("commit"),
- N_("print only branches that contain the commit"),
- PARSE_OPT_LASTARG_DEFAULT,
- parse_opt_with_commit, (intptr_t)"HEAD",
- },
- {
- OPTION_CALLBACK, 0, "with", &with_commit, N_("commit"),
- N_("print only branches that contain the commit"),
- PARSE_OPT_HIDDEN | PARSE_OPT_LASTARG_DEFAULT,
- parse_opt_with_commit, (intptr_t) "HEAD",
- },
- OPT__ABBREV(&abbrev),
+ OPT_SET_INT('r', "remotes", &filter.kind, N_("act on remote-tracking branches"),
+ FILTER_REFS_REMOTES),
+ OPT_CONTAINS(&filter.with_commit, N_("print only branches that contain the commit")),
+ OPT_WITH(&filter.with_commit, N_("print only branches that contain the commit")),
+ OPT__ABBREV(&filter.abbrev),
OPT_GROUP(N_("Specific git-branch actions:")),
- OPT_SET_INT('a', "all", &kinds, N_("list both remote-tracking and local branches"),
- REF_REMOTE_BRANCH | REF_LOCAL_BRANCH),
+ OPT_SET_INT('a', "all", &filter.kind, N_("list both remote-tracking and local branches"),
+ FILTER_REFS_REMOTES | FILTER_REFS_BRANCHES),
OPT_BIT('d', "delete", &delete, N_("delete fully merged branch"), 1),
OPT_BIT('D', NULL, &delete, N_("delete branch (even if not merged)"), 2),
OPT_BIT('m', "move", &rename, N_("move/rename a branch and its reflog"), 1),
@@ -847,22 +642,22 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
OPT_BOOL(0, "edit-description", &edit_description,
N_("edit the description for the branch")),
OPT__FORCE(&force, N_("force creation, move/rename, deletion")),
+ OPT_MERGED(&filter, N_("print only branches that are merged")),
+ OPT_NO_MERGED(&filter, N_("print only branches that are not merged")),
+ OPT_COLUMN(0, "column", &colopts, N_("list branches in columns")),
+ OPT_CALLBACK(0 , "sort", sorting_tail, N_("key"),
+ N_("field name to sort on"), &parse_opt_ref_sorting),
{
- OPTION_CALLBACK, 0, "no-merged", &merge_filter_ref,
- N_("commit"), N_("print only not merged branches"),
- PARSE_OPT_LASTARG_DEFAULT | PARSE_OPT_NONEG,
- opt_parse_merge_filter, (intptr_t) "HEAD",
- },
- {
- OPTION_CALLBACK, 0, "merged", &merge_filter_ref,
- N_("commit"), N_("print only merged branches"),
- PARSE_OPT_LASTARG_DEFAULT | PARSE_OPT_NONEG,
- opt_parse_merge_filter, (intptr_t) "HEAD",
+ OPTION_CALLBACK, 0, "points-at", &filter.points_at, N_("object"),
+ N_("print only branches of the object"), 0, parse_opt_object_name
},
- OPT_COLUMN(0, "column", &colopts, N_("list branches in columns")),
OPT_END(),
};
+ memset(&filter, 0, sizeof(filter));
+ filter.kind = FILTER_REFS_BRANCHES;
+ filter.abbrev = -1;
+
if (argc == 2 && !strcmp(argv[1], "-h"))
usage_with_options(builtin_branch_usage, options);
@@ -874,11 +669,9 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
if (!head)
die(_("Failed to resolve HEAD as a valid ref."));
if (!strcmp(head, "HEAD"))
- detached = 1;
+ filter.detached = 1;
else if (!skip_prefix(head, "refs/heads/", &head))
die(_("HEAD not found below refs/heads!"));
- hashcpy(merge_filter_ref, head_sha1);
-
argc = parse_options(argc, argv, prefix, options, builtin_branch_usage,
0);
@@ -886,17 +679,17 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
if (!delete && !rename && !edit_description && !new_upstream && !unset_upstream && argc == 0)
list = 1;
- if (with_commit || merge_filter != NO_FILTER)
+ if (filter.with_commit || filter.merge != REF_FILTER_MERGED_NONE || filter.points_at.nr)
list = 1;
if (!!delete + !!rename + !!new_upstream +
list + unset_upstream > 1)
usage_with_options(builtin_branch_usage, options);
- if (abbrev == -1)
- abbrev = DEFAULT_ABBREV;
+ if (filter.abbrev == -1)
+ filter.abbrev = DEFAULT_ABBREV;
finalize_colopts(&colopts, -1);
- if (verbose) {
+ if (filter.verbose) {
if (explicitly_enable_column(colopts))
die(_("--column and --verbose are incompatible"));
colopts = 0;
@@ -910,20 +703,23 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
if (delete) {
if (!argc)
die(_("branch name required"));
- return delete_branches(argc, argv, delete > 1, kinds, quiet);
+ return delete_branches(argc, argv, delete > 1, filter.kind, quiet);
} else if (list) {
- int ret = print_ref_list(kinds, detached, verbose, abbrev,
- with_commit, argv);
+ /* git branch --local also shows HEAD when it is detached */
+ if ((filter.kind & FILTER_REFS_BRANCHES) && filter.detached)
+ filter.kind |= FILTER_REFS_DETACHED_HEAD;
+ filter.name_patterns = argv;
+ print_ref_list(&filter, sorting);
print_columns(&output, colopts, NULL);
string_list_clear(&output, 0);
- return ret;
+ return 0;
}
else if (edit_description) {
const char *branch_name;
struct strbuf branch_ref = STRBUF_INIT;
if (!argc) {
- if (detached)
+ if (filter.detached)
die(_("Cannot give description to detached HEAD"));
branch_name = head;
} else if (argc == 1)
@@ -1011,7 +807,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
if (!branch)
die(_("no such branch '%s'"), argv[0]);
- if (kinds != REF_LOCAL_BRANCH)
+ if (filter.kind != FILTER_REFS_BRANCHES)
die(_("-a and -r options to 'git branch' do not make sense with a branch name"));
if (track == BRANCH_TRACK_OVERRIDE)
diff --git a/builtin/clean.c b/builtin/clean.c
index df53def63f..d7acb94a95 100644
--- a/builtin/clean.c
+++ b/builtin/clean.c
@@ -159,8 +159,7 @@ static int is_git_repository(struct strbuf *path)
int gitfile_error;
size_t orig_path_len = path->len;
assert(orig_path_len != 0);
- if (path->buf[orig_path_len - 1] != '/')
- strbuf_addch(path, '/');
+ strbuf_complete(path, '/');
strbuf_addstr(path, ".git");
if (read_gitfile_gently(path->buf, &gitfile_error) || is_git_directory(path->buf))
ret = 1;
@@ -206,8 +205,7 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
return res;
}
- if (path->buf[original_len - 1] != '/')
- strbuf_addch(path, '/');
+ strbuf_complete(path, '/');
len = path->len;
while ((e = readdir(dir)) != NULL) {
diff --git a/builtin/clone.c b/builtin/clone.c
index 578da85254..9eaecd9a7c 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -294,9 +294,14 @@ static int add_one_reference(struct string_list_item *item, void *cb_data)
char *ref_git_git = mkpathdup("%s/.git", ref_git);
free(ref_git);
ref_git = ref_git_git;
- } else if (!is_directory(mkpath("%s/objects", ref_git)))
+ } else if (!is_directory(mkpath("%s/objects", ref_git))) {
+ struct strbuf sb = STRBUF_INIT;
+ if (get_common_dir(&sb, ref_git))
+ die(_("reference repository '%s' as a linked checkout is not supported yet."),
+ item->string);
die(_("reference repository '%s' is not a local repository."),
item->string);
+ }
if (!access(mkpath("%s/shallow", ref_git), F_OK))
die(_("reference repository '%s' is shallow"), item->string);
@@ -424,8 +429,10 @@ static void clone_local(const char *src_repo, const char *dest_repo)
} else {
struct strbuf src = STRBUF_INIT;
struct strbuf dest = STRBUF_INIT;
- strbuf_addf(&src, "%s/objects", src_repo);
- strbuf_addf(&dest, "%s/objects", dest_repo);
+ get_common_dir(&src, src_repo);
+ get_common_dir(&dest, dest_repo);
+ strbuf_addstr(&src, "/objects");
+ strbuf_addstr(&dest, "/objects");
copy_or_link_directory(&src, &dest, src_repo, src.len);
strbuf_release(&src);
strbuf_release(&dest);
@@ -1064,8 +1071,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
transport_unlock_pack(transport);
transport_disconnect(transport);
- if (option_dissociate)
+ if (option_dissociate) {
+ close_all_packs();
dissociate_from_references();
+ }
junk_mode = JUNK_LEAVE_REPO;
err = checkout();
diff --git a/builtin/config.c b/builtin/config.c
index 71acc44143..adc772786a 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -246,8 +246,6 @@ free_strings:
static char *normalize_value(const char *key, const char *value)
{
- char *normalized;
-
if (!value)
return NULL;
@@ -258,27 +256,21 @@ static char *normalize_value(const char *key, const char *value)
* "~/foobar/" in the config file, and to expand the ~
* when retrieving the value.
*/
- normalized = xstrdup(value);
- else {
- normalized = xmalloc(64);
- if (types == TYPE_INT) {
- int64_t v = git_config_int64(key, value);
- sprintf(normalized, "%"PRId64, v);
- }
- else if (types == TYPE_BOOL)
- sprintf(normalized, "%s",
- git_config_bool(key, value) ? "true" : "false");
- else if (types == TYPE_BOOL_OR_INT) {
- int is_bool, v;
- v = git_config_bool_or_int(key, value, &is_bool);
- if (!is_bool)
- sprintf(normalized, "%d", v);
- else
- sprintf(normalized, "%s", v ? "true" : "false");
- }
+ return xstrdup(value);
+ if (types == TYPE_INT)
+ return xstrfmt("%"PRId64, git_config_int64(key, value));
+ if (types == TYPE_BOOL)
+ return xstrdup(git_config_bool(key, value) ? "true" : "false");
+ if (types == TYPE_BOOL_OR_INT) {
+ int is_bool, v;
+ v = git_config_bool_or_int(key, value, &is_bool);
+ if (!is_bool)
+ return xstrfmt("%d", v);
+ else
+ return xstrdup(v ? "true" : "false");
}
- return normalized;
+ die("BUG: cannot normalize type %d", types);
}
static int get_color_found;
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 9a3869f4ff..ed84963a57 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -528,36 +528,38 @@ static int update_local_ref(struct ref *ref,
}
if (in_merge_bases(current, updated)) {
- char quickref[83];
+ struct strbuf quickref = STRBUF_INIT;
int r;
- strcpy(quickref, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV));
- strcat(quickref, "..");
- strcat(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV));
+ strbuf_add_unique_abbrev(&quickref, current->object.sha1, DEFAULT_ABBREV);
+ strbuf_addstr(&quickref, "..");
+ strbuf_add_unique_abbrev(&quickref, ref->new_sha1, DEFAULT_ABBREV);
if ((recurse_submodules != RECURSE_SUBMODULES_OFF) &&
(recurse_submodules != RECURSE_SUBMODULES_ON))
check_for_new_submodule_commits(ref->new_sha1);
r = s_update_ref("fast-forward", ref, 1);
strbuf_addf(display, "%c %-*s %-*s -> %s%s",
r ? '!' : ' ',
- TRANSPORT_SUMMARY_WIDTH, quickref,
+ TRANSPORT_SUMMARY_WIDTH, quickref.buf,
REFCOL_WIDTH, remote, pretty_ref,
r ? _(" (unable to update local ref)") : "");
+ strbuf_release(&quickref);
return r;
} else if (force || ref->force) {
- char quickref[84];
+ struct strbuf quickref = STRBUF_INIT;
int r;
- strcpy(quickref, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV));
- strcat(quickref, "...");
- strcat(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV));
+ strbuf_add_unique_abbrev(&quickref, current->object.sha1, DEFAULT_ABBREV);
+ strbuf_addstr(&quickref, "...");
+ strbuf_add_unique_abbrev(&quickref, ref->new_sha1, DEFAULT_ABBREV);
if ((recurse_submodules != RECURSE_SUBMODULES_OFF) &&
(recurse_submodules != RECURSE_SUBMODULES_ON))
check_for_new_submodule_commits(ref->new_sha1);
r = s_update_ref("forced-update", ref, 1);
strbuf_addf(display, "%c %-*s %-*s -> %s (%s)",
r ? '!' : '+',
- TRANSPORT_SUMMARY_WIDTH, quickref,
+ TRANSPORT_SUMMARY_WIDTH, quickref.buf,
REFCOL_WIDTH, remote, pretty_ref,
r ? _("unable to update local ref") : _("forced update"));
+ strbuf_release(&quickref);
return r;
} else {
strbuf_addf(display, "! %-*s %-*s -> %s %s",
@@ -637,8 +639,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
continue;
if (rm->peer_ref) {
- ref = xcalloc(1, sizeof(*ref) + strlen(rm->peer_ref->name) + 1);
- strcpy(ref->name, rm->peer_ref->name);
+ ref = alloc_ref(rm->peer_ref->name);
hashcpy(ref->old_sha1, rm->peer_ref->old_sha1);
hashcpy(ref->new_sha1, rm->old_sha1);
ref->force = rm->peer_ref->force;
@@ -1156,11 +1157,8 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
die(_("--depth and --unshallow cannot be used together"));
else if (!is_repository_shallow())
die(_("--unshallow on a complete repository does not make sense"));
- else {
- static char inf_depth[12];
- sprintf(inf_depth, "%d", INFINITE_DEPTH);
- depth = inf_depth;
- }
+ else
+ depth = xstrfmt("%d", INFINITE_DEPTH);
}
/* no need to be strict, transport_set_option() will validate it again */
diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c
index 7919206187..4e9f6c29bf 100644
--- a/builtin/for-each-ref.c
+++ b/builtin/for-each-ref.c
@@ -7,6 +7,9 @@
static char const * const for_each_ref_usage[] = {
N_("git for-each-ref [<options>] [<pattern>]"),
+ N_("git for-each-ref [--points-at <object>]"),
+ N_("git for-each-ref [(--merged | --no-merged) [<object>]]"),
+ N_("git for-each-ref [--contains [<object>]]"),
NULL
};
@@ -34,9 +37,18 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
OPT_STRING( 0 , "format", &format, N_("format"), N_("format to use for the output")),
OPT_CALLBACK(0 , "sort", sorting_tail, N_("key"),
N_("field name to sort on"), &parse_opt_ref_sorting),
+ OPT_CALLBACK(0, "points-at", &filter.points_at,
+ N_("object"), N_("print only refs which points at the given object"),
+ parse_opt_object_name),
+ OPT_MERGED(&filter, N_("print only refs that are merged")),
+ OPT_NO_MERGED(&filter, N_("print only refs that are not merged")),
+ OPT_CONTAINS(&filter.with_commit, N_("print only refs which contain the commit")),
OPT_END(),
};
+ memset(&array, 0, sizeof(array));
+ memset(&filter, 0, sizeof(filter));
+
parse_options(argc, argv, prefix, opts, for_each_ref_usage, 0);
if (maxcount < 0) {
error("invalid --count argument: `%d'", maxcount);
@@ -55,9 +67,8 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
/* for warn_ambiguous_refs */
git_config(git_default_config, NULL);
- memset(&array, 0, sizeof(array));
- memset(&filter, 0, sizeof(filter));
filter.name_patterns = argv;
+ filter.match_as_path = 1;
filter_refs(&array, &filter, FILTER_REFS_ALL | FILTER_REFS_INCLUDE_BROKEN);
ref_array_sort(sorting, &array);
diff --git a/builtin/fsck.c b/builtin/fsck.c
index b9a74f0cf6..8b8bb42c51 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -40,14 +40,6 @@ static int show_dangling = 1;
#define ERROR_PACK 04
#define ERROR_REFS 010
-#ifdef NO_D_INO_IN_DIRENT
-#define SORT_DIRENT 0
-#define DIRENT_SORT_HINT(de) 0
-#else
-#define SORT_DIRENT 1
-#define DIRENT_SORT_HINT(de) ((de)->d_ino)
-#endif
-
static int fsck_config(const char *var, const char *value, void *cb)
{
if (strcmp(var, "fsck.skiplist") == 0) {
@@ -374,102 +366,6 @@ static int fsck_obj_buffer(const unsigned char *sha1, enum object_type type,
return fsck_obj(obj);
}
-/*
- * This is the sorting chunk size: make it reasonably
- * big so that we can sort well..
- */
-#define MAX_SHA1_ENTRIES (1024)
-
-struct sha1_entry {
- unsigned long ino;
- unsigned char sha1[20];
-};
-
-static struct {
- unsigned long nr;
- struct sha1_entry *entry[MAX_SHA1_ENTRIES];
-} sha1_list;
-
-static int ino_compare(const void *_a, const void *_b)
-{
- const struct sha1_entry *a = _a, *b = _b;
- unsigned long ino1 = a->ino, ino2 = b->ino;
- return ino1 < ino2 ? -1 : ino1 > ino2 ? 1 : 0;
-}
-
-static void fsck_sha1_list(void)
-{
- int i, nr = sha1_list.nr;
-
- if (SORT_DIRENT)
- qsort(sha1_list.entry, nr,
- sizeof(struct sha1_entry *), ino_compare);
- for (i = 0; i < nr; i++) {
- struct sha1_entry *entry = sha1_list.entry[i];
- unsigned char *sha1 = entry->sha1;
-
- sha1_list.entry[i] = NULL;
- if (fsck_sha1(sha1))
- errors_found |= ERROR_OBJECT;
- free(entry);
- }
- sha1_list.nr = 0;
-}
-
-static void add_sha1_list(unsigned char *sha1, unsigned long ino)
-{
- struct sha1_entry *entry = xmalloc(sizeof(*entry));
- int nr;
-
- entry->ino = ino;
- hashcpy(entry->sha1, sha1);
- nr = sha1_list.nr;
- if (nr == MAX_SHA1_ENTRIES) {
- fsck_sha1_list();
- nr = 0;
- }
- sha1_list.entry[nr] = entry;
- sha1_list.nr = ++nr;
-}
-
-static inline int is_loose_object_file(struct dirent *de,
- char *name, unsigned char *sha1)
-{
- if (strlen(de->d_name) != 38)
- return 0;
- memcpy(name + 2, de->d_name, 39);
- return !get_sha1_hex(name, sha1);
-}
-
-static void fsck_dir(int i, char *path)
-{
- DIR *dir = opendir(path);
- struct dirent *de;
- char name[100];
-
- if (!dir)
- return;
-
- if (verbose)
- fprintf(stderr, "Checking directory %s\n", path);
-
- sprintf(name, "%02x", i);
- while ((de = readdir(dir)) != NULL) {
- unsigned char sha1[20];
-
- if (is_dot_or_dotdot(de->d_name))
- continue;
- if (is_loose_object_file(de, name, sha1)) {
- add_sha1_list(sha1, DIRENT_SORT_HINT(de));
- continue;
- }
- if (starts_with(de->d_name, "tmp_obj_"))
- continue;
- fprintf(stderr, "bad sha1 file: %s/%s\n", path, de->d_name);
- }
- closedir(dir);
-}
-
static int default_refs;
static void fsck_handle_reflog_sha1(const char *refname, unsigned char *sha1)
@@ -559,9 +455,28 @@ static void get_default_heads(void)
}
}
+static int fsck_loose(const unsigned char *sha1, const char *path, void *data)
+{
+ if (fsck_sha1(sha1))
+ errors_found |= ERROR_OBJECT;
+ return 0;
+}
+
+static int fsck_cruft(const char *basename, const char *path, void *data)
+{
+ if (!starts_with(basename, "tmp_obj_"))
+ fprintf(stderr, "bad sha1 file: %s\n", path);
+ return 0;
+}
+
+static int fsck_subdir(int nr, const char *path, void *progress)
+{
+ display_progress(progress, nr + 1);
+ return 0;
+}
+
static void fsck_object_dir(const char *path)
{
- int i;
struct progress *progress = NULL;
if (verbose)
@@ -569,14 +484,11 @@ static void fsck_object_dir(const char *path)
if (show_progress)
progress = start_progress(_("Checking object directories"), 256);
- for (i = 0; i < 256; i++) {
- static char dir[4096];
- sprintf(dir, "%s/%02x", path, i);
- fsck_dir(i, dir);
- display_progress(progress, i+1);
- }
+
+ for_each_loose_file_in_objdir(path, fsck_loose, fsck_cruft, fsck_subdir,
+ progress);
+ display_progress(progress, 256);
stop_progress(&progress);
- fsck_sha1_list();
}
static int fsck_head_link(void)
@@ -688,16 +600,18 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
git_config(fsck_config, NULL);
fsck_head_link();
- if (!connectivity_only)
+ if (!connectivity_only) {
fsck_object_dir(get_object_directory());
- prepare_alt_odb();
- for (alt = alt_odb_list; alt; alt = alt->next) {
- char namebuf[PATH_MAX];
- int namelen = alt->name - alt->base;
- memcpy(namebuf, alt->base, namelen);
- namebuf[namelen - 1] = 0;
- fsck_object_dir(namebuf);
+ prepare_alt_odb();
+ for (alt = alt_odb_list; alt; alt = alt->next) {
+ /* directory name, minus trailing slash */
+ size_t namelen = alt->name - alt->base - 1;
+ struct strbuf name = STRBUF_INIT;
+ strbuf_add(&name, alt->base, namelen);
+ fsck_object_dir(name.buf);
+ strbuf_release(&name);
+ }
}
if (check_full) {
diff --git a/builtin/gc.c b/builtin/gc.c
index 0ad8d30b56..eeeb21b1c4 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -44,6 +44,7 @@ static struct argv_array prune_worktrees = ARGV_ARRAY_INIT;
static struct argv_array rerere = ARGV_ARRAY_INIT;
static struct tempfile pidfile;
+static struct lock_file log_lock;
static void git_config_date_string(const char *key, const char **output)
{
@@ -56,6 +57,28 @@ static void git_config_date_string(const char *key, const char **output)
}
}
+static void process_log_file(void)
+{
+ struct stat st;
+ if (!fstat(get_lock_file_fd(&log_lock), &st) && st.st_size)
+ commit_lock_file(&log_lock);
+ else
+ rollback_lock_file(&log_lock);
+}
+
+static void process_log_file_at_exit(void)
+{
+ fflush(stderr);
+ process_log_file();
+}
+
+static void process_log_file_on_signal(int signo)
+{
+ process_log_file();
+ sigchain_pop(signo);
+ raise(signo);
+}
+
static void gc_config(void)
{
const char *value;
@@ -194,7 +217,7 @@ static const char *lock_repo_for_gc(int force, pid_t* ret_pid)
return NULL;
if (gethostname(my_host, sizeof(my_host)))
- strcpy(my_host, "unknown");
+ xsnprintf(my_host, sizeof(my_host), "unknown");
pidfile_path = git_pathdup("gc.pid");
fd = hold_lock_file_for_update(&lock, pidfile_path,
@@ -241,6 +264,24 @@ static const char *lock_repo_for_gc(int force, pid_t* ret_pid)
return NULL;
}
+static int report_last_gc_error(void)
+{
+ struct strbuf sb = STRBUF_INIT;
+ int ret;
+
+ ret = strbuf_read_file(&sb, git_path("gc.log"), 0);
+ if (ret > 0)
+ return error(_("The last gc run reported the following. "
+ "Please correct the root cause\n"
+ "and remove %s.\n"
+ "Automatic cleanup will not be performed "
+ "until the file is removed.\n\n"
+ "%s"),
+ git_path("gc.log"), sb.buf);
+ strbuf_release(&sb);
+ return 0;
+}
+
static int gc_before_repack(void)
{
if (pack_refs && run_command_v_opt(pack_refs_cmd.argv, RUN_GIT_CMD))
@@ -262,6 +303,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
int force = 0;
const char *name;
pid_t pid;
+ int daemonized = 0;
struct option builtin_gc_options[] = {
OPT__QUIET(&quiet, N_("suppress progress reporting")),
@@ -318,13 +360,16 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
fprintf(stderr, _("See \"git help gc\" for manual housekeeping.\n"));
}
if (detach_auto) {
+ if (report_last_gc_error())
+ return -1;
+
if (gc_before_repack())
return -1;
/*
* failure to daemonize is ok, we'll continue
* in foreground
*/
- daemonize();
+ daemonized = !daemonize();
}
} else
add_repack_all_option();
@@ -337,6 +382,15 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
name, (uintmax_t)pid);
}
+ if (daemonized) {
+ hold_lock_file_for_update(&log_lock,
+ git_path("gc.log"),
+ LOCK_DIE_ON_ERROR);
+ dup2(get_lock_file_fd(&log_lock), 2);
+ sigchain_push_common(process_log_file_on_signal);
+ atexit(process_log_file_at_exit);
+ }
+
if (gc_before_repack())
return -1;
diff --git a/builtin/help.c b/builtin/help.c
index 3422e73079..1cd0c1ee44 100644
--- a/builtin/help.c
+++ b/builtin/help.c
@@ -140,17 +140,10 @@ static void exec_man_konqueror(const char *path, const char *page)
/* It's simpler to launch konqueror using kfmclient. */
if (path) {
- const char *file = strrchr(path, '/');
- if (file && !strcmp(file + 1, "konqueror")) {
- char *new = xstrdup(path);
- char *dest = strrchr(new, '/');
-
- /* strlen("konqueror") == strlen("kfmclient") */
- strcpy(dest + 1, "kfmclient");
- path = new;
- }
- if (file)
- filename = file;
+ size_t len;
+ if (strip_suffix(path, "/konqueror", &len))
+ path = xstrfmt("%.*s/kfmclient", (int)len, path);
+ filename = basename((char *)path);
} else
path = "kfmclient";
strbuf_addf(&man_page, "man:%s(1)", page);
@@ -183,7 +176,7 @@ static void add_man_viewer(const char *name)
while (*p)
p = &((*p)->next);
*p = xcalloc(1, (sizeof(**p) + len + 1));
- strncpy((*p)->name, name, len);
+ memcpy((*p)->name, name, len); /* NUL-terminated by xcalloc */
}
static int supported_man_viewer(const char *name, size_t len)
@@ -199,7 +192,7 @@ static void do_add_man_viewer_info(const char *name,
{
struct man_viewer_info_list *new = xcalloc(1, sizeof(*new) + len + 1);
- strncpy(new->name, name, len);
+ memcpy(new->name, name, len); /* NUL-terminated by xcalloc */
new->info = xstrdup(value);
new->next = man_viewer_info_list;
man_viewer_info_list = new;
@@ -295,16 +288,6 @@ static int is_git_command(const char *s)
is_in_cmdlist(&other_cmds, s);
}
-static const char *prepend(const char *prefix, const char *cmd)
-{
- size_t pre_len = strlen(prefix);
- size_t cmd_len = strlen(cmd);
- char *p = xmalloc(pre_len + cmd_len + 1);
- memcpy(p, prefix, pre_len);
- strcpy(p + pre_len, cmd);
- return p;
-}
-
static const char *cmd_to_page(const char *git_cmd)
{
if (!git_cmd)
@@ -312,9 +295,9 @@ static const char *cmd_to_page(const char *git_cmd)
else if (starts_with(git_cmd, "git"))
return git_cmd;
else if (is_git_command(git_cmd))
- return prepend("git-", git_cmd);
+ return xstrfmt("git-%s", git_cmd);
else
- return prepend("git", git_cmd);
+ return xstrfmt("git%s", git_cmd);
}
static void setup_man_path(void)
diff --git a/builtin/index-pack.c b/builtin/index-pack.c
index 3431de2362..1ad1bde696 100644
--- a/builtin/index-pack.c
+++ b/builtin/index-pack.c
@@ -441,7 +441,7 @@ static void *unpack_entry_data(unsigned long offset, unsigned long size,
int hdrlen;
if (!is_delta_type(type)) {
- hdrlen = sprintf(hdr, "%s %lu", typename(type), size) + 1;
+ hdrlen = xsnprintf(hdr, sizeof(hdr), "%s %lu", typename(type), size) + 1;
git_SHA1_Init(&c);
git_SHA1_Update(&c, hdr, hdrlen);
} else
diff --git a/builtin/init-db.c b/builtin/init-db.c
index 69323e186c..f59f40768e 100644
--- a/builtin/init-db.c
+++ b/builtin/init-db.c
@@ -36,10 +36,11 @@ static void safe_create_dir(const char *dir, int share)
die(_("Could not make %s writable by group"), dir);
}
-static void copy_templates_1(char *path, int baselen,
- char *template, int template_baselen,
+static void copy_templates_1(struct strbuf *path, struct strbuf *template,
DIR *dir)
{
+ size_t path_baselen = path->len;
+ size_t template_baselen = template->len;
struct dirent *de;
/* Note: if ".git/hooks" file exists in the repository being
@@ -49,77 +50,64 @@ static void copy_templates_1(char *path, int baselen,
* with the way the namespace under .git/ is organized, should
* be really carefully chosen.
*/
- safe_create_dir(path, 1);
+ safe_create_dir(path->buf, 1);
while ((de = readdir(dir)) != NULL) {
struct stat st_git, st_template;
- int namelen;
int exists = 0;
+ strbuf_setlen(path, path_baselen);
+ strbuf_setlen(template, template_baselen);
+
if (de->d_name[0] == '.')
continue;
- namelen = strlen(de->d_name);
- if ((PATH_MAX <= baselen + namelen) ||
- (PATH_MAX <= template_baselen + namelen))
- die(_("insanely long template name %s"), de->d_name);
- memcpy(path + baselen, de->d_name, namelen+1);
- memcpy(template + template_baselen, de->d_name, namelen+1);
- if (lstat(path, &st_git)) {
+ strbuf_addstr(path, de->d_name);
+ strbuf_addstr(template, de->d_name);
+ if (lstat(path->buf, &st_git)) {
if (errno != ENOENT)
- die_errno(_("cannot stat '%s'"), path);
+ die_errno(_("cannot stat '%s'"), path->buf);
}
else
exists = 1;
- if (lstat(template, &st_template))
- die_errno(_("cannot stat template '%s'"), template);
+ if (lstat(template->buf, &st_template))
+ die_errno(_("cannot stat template '%s'"), template->buf);
if (S_ISDIR(st_template.st_mode)) {
- DIR *subdir = opendir(template);
- int baselen_sub = baselen + namelen;
- int template_baselen_sub = template_baselen + namelen;
+ DIR *subdir = opendir(template->buf);
if (!subdir)
- die_errno(_("cannot opendir '%s'"), template);
- path[baselen_sub++] =
- template[template_baselen_sub++] = '/';
- path[baselen_sub] =
- template[template_baselen_sub] = 0;
- copy_templates_1(path, baselen_sub,
- template, template_baselen_sub,
- subdir);
+ die_errno(_("cannot opendir '%s'"), template->buf);
+ strbuf_addch(path, '/');
+ strbuf_addch(template, '/');
+ copy_templates_1(path, template, subdir);
closedir(subdir);
}
else if (exists)
continue;
else if (S_ISLNK(st_template.st_mode)) {
- char lnk[256];
- int len;
- len = readlink(template, lnk, sizeof(lnk));
- if (len < 0)
- die_errno(_("cannot readlink '%s'"), template);
- if (sizeof(lnk) <= len)
- die(_("insanely long symlink %s"), template);
- lnk[len] = 0;
- if (symlink(lnk, path))
- die_errno(_("cannot symlink '%s' '%s'"), lnk, path);
+ struct strbuf lnk = STRBUF_INIT;
+ if (strbuf_readlink(&lnk, template->buf, 0) < 0)
+ die_errno(_("cannot readlink '%s'"), template->buf);
+ if (symlink(lnk.buf, path->buf))
+ die_errno(_("cannot symlink '%s' '%s'"),
+ lnk.buf, path->buf);
+ strbuf_release(&lnk);
}
else if (S_ISREG(st_template.st_mode)) {
- if (copy_file(path, template, st_template.st_mode))
- die_errno(_("cannot copy '%s' to '%s'"), template,
- path);
+ if (copy_file(path->buf, template->buf, st_template.st_mode))
+ die_errno(_("cannot copy '%s' to '%s'"),
+ template->buf, path->buf);
}
else
- error(_("ignoring template %s"), template);
+ error(_("ignoring template %s"), template->buf);
}
}
static void copy_templates(const char *template_dir)
{
- char path[PATH_MAX];
- char template_path[PATH_MAX];
- int template_len;
+ struct strbuf path = STRBUF_INIT;
+ struct strbuf template_path = STRBUF_INIT;
+ size_t template_len;
DIR *dir;
- const char *git_dir = get_git_dir();
- int len = strlen(git_dir);
char *to_free = NULL;
if (!template_dir)
@@ -132,26 +120,23 @@ static void copy_templates(const char *template_dir)
free(to_free);
return;
}
- template_len = strlen(template_dir);
- if (PATH_MAX <= (template_len+strlen("/config")))
- die(_("insanely long template path %s"), template_dir);
- strcpy(template_path, template_dir);
- if (template_path[template_len-1] != '/') {
- template_path[template_len++] = '/';
- template_path[template_len] = 0;
- }
- dir = opendir(template_path);
+
+ strbuf_addstr(&template_path, template_dir);
+ strbuf_complete(&template_path, '/');
+ template_len = template_path.len;
+
+ dir = opendir(template_path.buf);
if (!dir) {
warning(_("templates not found %s"), template_dir);
goto free_return;
}
/* Make sure that template is from the correct vintage */
- strcpy(template_path + template_len, "config");
+ strbuf_addstr(&template_path, "config");
repository_format_version = 0;
git_config_from_file(check_repository_format_version,
- template_path, NULL);
- template_path[template_len] = 0;
+ template_path.buf, NULL);
+ strbuf_setlen(&template_path, template_len);
if (repository_format_version &&
repository_format_version != GIT_REPO_VERSION) {
@@ -162,17 +147,15 @@ static void copy_templates(const char *template_dir)
goto close_free_return;
}
- memcpy(path, git_dir, len);
- if (len && path[len - 1] != '/')
- path[len++] = '/';
- path[len] = 0;
- copy_templates_1(path, len,
- template_path, template_len,
- dir);
+ strbuf_addstr(&path, get_git_dir());
+ strbuf_complete(&path, '/');
+ copy_templates_1(&path, &template_path, dir);
close_free_return:
closedir(dir);
free_return:
free(to_free);
+ strbuf_release(&path);
+ strbuf_release(&template_path);
}
static int git_init_db_config(const char *k, const char *v, void *cb)
@@ -199,28 +182,20 @@ static int needs_work_tree_config(const char *git_dir, const char *work_tree)
static int create_default_files(const char *template_path)
{
- const char *git_dir = get_git_dir();
- unsigned len = strlen(git_dir);
- static char path[PATH_MAX];
struct stat st1;
+ struct strbuf buf = STRBUF_INIT;
+ char *path;
char repo_version_string[10];
char junk[2];
int reinit;
int filemode;
- if (len > sizeof(path)-50)
- die(_("insane git directory %s"), git_dir);
- memcpy(path, git_dir, len);
-
- if (len && path[len-1] != '/')
- path[len++] = '/';
-
/*
* Create .git/refs/{heads,tags}
*/
- safe_create_dir(git_path("refs"), 1);
- safe_create_dir(git_path("refs/heads"), 1);
- safe_create_dir(git_path("refs/tags"), 1);
+ safe_create_dir(git_path_buf(&buf, "refs"), 1);
+ safe_create_dir(git_path_buf(&buf, "refs/heads"), 1);
+ safe_create_dir(git_path_buf(&buf, "refs/tags"), 1);
/* Just look for `init.templatedir` */
git_config(git_init_db_config, NULL);
@@ -244,16 +219,16 @@ static int create_default_files(const char *template_path)
*/
if (shared_repository) {
adjust_shared_perm(get_git_dir());
- adjust_shared_perm(git_path("refs"));
- adjust_shared_perm(git_path("refs/heads"));
- adjust_shared_perm(git_path("refs/tags"));
+ adjust_shared_perm(git_path_buf(&buf, "refs"));
+ adjust_shared_perm(git_path_buf(&buf, "refs/heads"));
+ adjust_shared_perm(git_path_buf(&buf, "refs/tags"));
}
/*
* Create the default symlink from ".git/HEAD" to the "master"
* branch, if it does not exist yet.
*/
- strcpy(path + len, "HEAD");
+ path = git_path_buf(&buf, "HEAD");
reinit = (!access(path, R_OK)
|| readlink(path, junk, sizeof(junk)-1) != -1);
if (!reinit) {
@@ -262,13 +237,12 @@ static int create_default_files(const char *template_path)
}
/* This forces creation of new config file */
- sprintf(repo_version_string, "%d", GIT_REPO_VERSION);
+ xsnprintf(repo_version_string, sizeof(repo_version_string),
+ "%d", GIT_REPO_VERSION);
git_config_set("core.repositoryformatversion", repo_version_string);
- path[len] = 0;
- strcpy(path + len, "config");
-
/* Check filemode trustability */
+ path = git_path_buf(&buf, "config");
filemode = TEST_FILEMODE;
if (TEST_FILEMODE && !lstat(path, &st1)) {
struct stat st2;
@@ -289,14 +263,13 @@ static int create_default_files(const char *template_path)
/* allow template config file to override the default */
if (log_all_ref_updates == -1)
git_config_set("core.logallrefupdates", "true");
- if (needs_work_tree_config(git_dir, work_tree))
+ if (needs_work_tree_config(get_git_dir(), work_tree))
git_config_set("core.worktree", work_tree);
}
if (!reinit) {
/* Check if symlink is supported in the work tree */
- path[len] = 0;
- strcpy(path + len, "tXXXXXX");
+ path = git_path_buf(&buf, "tXXXXXX");
if (!close(xmkstemp(path)) &&
!unlink(path) &&
!symlink("testing", path) &&
@@ -307,31 +280,35 @@ static int create_default_files(const char *template_path)
git_config_set("core.symlinks", "false");
/* Check if the filesystem is case-insensitive */
- path[len] = 0;
- strcpy(path + len, "CoNfIg");
+ path = git_path_buf(&buf, "CoNfIg");
if (!access(path, F_OK))
git_config_set("core.ignorecase", "true");
- probe_utf8_pathname_composition(path, len);
+ probe_utf8_pathname_composition();
}
+ strbuf_release(&buf);
return reinit;
}
static void create_object_directory(void)
{
- const char *object_directory = get_object_directory();
- int len = strlen(object_directory);
- char *path = xmalloc(len + 40);
+ struct strbuf path = STRBUF_INIT;
+ size_t baselen;
+
+ strbuf_addstr(&path, get_object_directory());
+ baselen = path.len;
+
+ safe_create_dir(path.buf, 1);
- memcpy(path, object_directory, len);
+ strbuf_setlen(&path, baselen);
+ strbuf_addstr(&path, "/pack");
+ safe_create_dir(path.buf, 1);
- safe_create_dir(object_directory, 1);
- strcpy(path+len, "/pack");
- safe_create_dir(path, 1);
- strcpy(path+len, "/info");
- safe_create_dir(path, 1);
+ strbuf_setlen(&path, baselen);
+ strbuf_addstr(&path, "/info");
+ safe_create_dir(path.buf, 1);
- free(path);
+ strbuf_release(&path);
}
int set_git_dir_init(const char *git_dir, const char *real_git_dir,
@@ -414,13 +391,13 @@ int init_db(const char *template_dir, unsigned int flags)
*/
if (shared_repository < 0)
/* force to the mode value */
- sprintf(buf, "0%o", -shared_repository);
+ xsnprintf(buf, sizeof(buf), "0%o", -shared_repository);
else if (shared_repository == PERM_GROUP)
- sprintf(buf, "%d", OLD_PERM_GROUP);
+ xsnprintf(buf, sizeof(buf), "%d", OLD_PERM_GROUP);
else if (shared_repository == PERM_EVERYBODY)
- sprintf(buf, "%d", OLD_PERM_EVERYBODY);
+ xsnprintf(buf, sizeof(buf), "%d", OLD_PERM_EVERYBODY);
else
- die("oops");
+ die("BUG: invalid value for shared_repository");
git_config_set("core.sharedrepository", buf);
git_config_set("receive.denyNonFastforwards", "true");
}
diff --git a/builtin/log.c b/builtin/log.c
index a491d3dea0..dda671d975 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -796,8 +796,7 @@ static int reopen_stdout(struct commit *commit, const char *subject,
if (filename.len >=
PATH_MAX - FORMAT_PATCH_NAME_MAX - suffix_len)
return error(_("name of output directory is too long"));
- if (filename.buf[filename.len - 1] != '/')
- strbuf_addch(&filename, '/');
+ strbuf_complete(&filename, '/');
}
if (rev->numbered_files)
diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c
index 5e9d5450b7..a31024900b 100644
--- a/builtin/ls-remote.c
+++ b/builtin/ls-remote.c
@@ -93,12 +93,8 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
if (argv[i]) {
int j;
pattern = xcalloc(argc - i + 1, sizeof(const char *));
- for (j = i; j < argc; j++) {
- int len = strlen(argv[j]);
- char *p = xmalloc(len + 3);
- sprintf(p, "*/%s", argv[j]);
- pattern[j - i] = p;
- }
+ for (j = i; j < argc; j++)
+ pattern[j - i] = xstrfmt("*/%s", argv[j]);
}
remote = remote_get(dest);
if (!remote) {
diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c
index 3b04a0f082..0e30d86230 100644
--- a/builtin/ls-tree.c
+++ b/builtin/ls-tree.c
@@ -96,12 +96,13 @@ static int show_tree(const unsigned char *sha1, struct strbuf *base,
if (!strcmp(type, blob_type)) {
unsigned long size;
if (sha1_object_info(sha1, &size) == OBJ_BAD)
- strcpy(size_text, "BAD");
+ xsnprintf(size_text, sizeof(size_text),
+ "BAD");
else
- snprintf(size_text, sizeof(size_text),
- "%lu", size);
+ xsnprintf(size_text, sizeof(size_text),
+ "%lu", size);
} else
- strcpy(size_text, "-");
+ xsnprintf(size_text, sizeof(size_text), "-");
printf("%06o %s %s %7s\t", mode, type,
find_unique_abbrev(sha1, abbrev),
size_text);
diff --git a/builtin/mailsplit.c b/builtin/mailsplit.c
index 8e02ea109a..104277acc4 100644
--- a/builtin/mailsplit.c
+++ b/builtin/mailsplit.c
@@ -98,30 +98,37 @@ static int populate_maildir_list(struct string_list *list, const char *path)
{
DIR *dir;
struct dirent *dent;
- char name[PATH_MAX];
+ char *name = NULL;
char *subs[] = { "cur", "new", NULL };
char **sub;
+ int ret = -1;
for (sub = subs; *sub; ++sub) {
- snprintf(name, sizeof(name), "%s/%s", path, *sub);
+ free(name);
+ name = xstrfmt("%s/%s", path, *sub);
if ((dir = opendir(name)) == NULL) {
if (errno == ENOENT)
continue;
error("cannot opendir %s (%s)", name, strerror(errno));
- return -1;
+ goto out;
}
while ((dent = readdir(dir)) != NULL) {
if (dent->d_name[0] == '.')
continue;
- snprintf(name, sizeof(name), "%s/%s", *sub, dent->d_name);
+ free(name);
+ name = xstrfmt("%s/%s", *sub, dent->d_name);
string_list_insert(list, name);
}
closedir(dir);
}
- return 0;
+ ret = 0;
+
+out:
+ free(name);
+ return ret;
}
static int maildir_filename_cmp(const char *a, const char *b)
@@ -148,8 +155,8 @@ static int maildir_filename_cmp(const char *a, const char *b)
static int split_maildir(const char *maildir, const char *dir,
int nr_prec, int skip)
{
- char file[PATH_MAX];
- char name[PATH_MAX];
+ char *file = NULL;
+ FILE *f = NULL;
int ret = -1;
int i;
struct string_list list = STRING_LIST_INIT_DUP;
@@ -160,8 +167,11 @@ static int split_maildir(const char *maildir, const char *dir,
goto out;
for (i = 0; i < list.nr; i++) {
- FILE *f;
- snprintf(file, sizeof(file), "%s/%s", maildir, list.items[i].string);
+ char *name;
+
+ free(file);
+ file = xstrfmt("%s/%s", maildir, list.items[i].string);
+
f = fopen(file, "r");
if (!f) {
error("cannot open mail %s (%s)", file, strerror(errno));
@@ -173,14 +183,19 @@ static int split_maildir(const char *maildir, const char *dir,
goto out;
}
- sprintf(name, "%s/%0*d", dir, nr_prec, ++skip);
+ name = xstrfmt("%s/%0*d", dir, nr_prec, ++skip);
split_one(f, name, 1);
+ free(name);
fclose(f);
+ f = NULL;
}
ret = skip;
out:
+ if (f)
+ fclose(f);
+ free(file);
string_list_clear(&list, 1);
return ret;
}
@@ -188,7 +203,6 @@ out:
static int split_mbox(const char *file, const char *dir, int allow_bare,
int nr_prec, int skip)
{
- char name[PATH_MAX];
int ret = -1;
int peek;
@@ -215,8 +229,9 @@ static int split_mbox(const char *file, const char *dir, int allow_bare,
}
while (!file_done) {
- sprintf(name, "%s/%0*d", dir, nr_prec, ++skip);
+ char *name = xstrfmt("%s/%0*d", dir, nr_prec, ++skip);
file_done = split_one(f, name, allow_bare);
+ free(name);
}
if (f != stdin)
diff --git a/builtin/merge-index.c b/builtin/merge-index.c
index 1a1eafa6fd..1c3427c36c 100644
--- a/builtin/merge-index.c
+++ b/builtin/merge-index.c
@@ -9,7 +9,7 @@ static int merge_entry(int pos, const char *path)
{
int found;
const char *arguments[] = { pgm, "", "", "", path, "", "", "", NULL };
- char hexbuf[4][60];
+ char hexbuf[4][GIT_SHA1_HEXSZ + 1];
char ownbuf[4][60];
if (pos >= active_nr)
@@ -22,8 +22,8 @@ static int merge_entry(int pos, const char *path)
if (strcmp(ce->name, path))
break;
found++;
- strcpy(hexbuf[stage], sha1_to_hex(ce->sha1));
- sprintf(ownbuf[stage], "%o", ce->ce_mode);
+ sha1_to_hex_r(hexbuf[stage], ce->sha1);
+ xsnprintf(ownbuf[stage], sizeof(ownbuf[stage]), "%o", ce->ce_mode);
arguments[stage] = hexbuf[stage];
arguments[stage + 4] = ownbuf[stage];
} while (++pos < active_nr);
diff --git a/builtin/merge-recursive.c b/builtin/merge-recursive.c
index a90f28f34d..491efd556e 100644
--- a/builtin/merge-recursive.c
+++ b/builtin/merge-recursive.c
@@ -14,7 +14,7 @@ static const char *better_branch_name(const char *branch)
if (strlen(branch) != 40)
return branch;
- sprintf(githead_env, "GITHEAD_%s", branch);
+ xsnprintf(githead_env, sizeof(githead_env), "GITHEAD_%s", branch);
name = getenv(githead_env);
return name ? name : branch;
}
diff --git a/builtin/merge.c b/builtin/merge.c
index a0edacab20..a0a9328292 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -1319,13 +1319,13 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
if (verify_signatures) {
for (p = remoteheads; p; p = p->next) {
struct commit *commit = p->item;
- char hex[41];
+ char hex[GIT_SHA1_HEXSZ + 1];
struct signature_check signature_check;
memset(&signature_check, 0, sizeof(signature_check));
check_commit_signature(commit, &signature_check);
- strcpy(hex, find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV));
+ find_unique_abbrev_r(hex, commit->object.sha1, DEFAULT_ABBREV);
switch (signature_check.result) {
case 'G':
break;
@@ -1415,15 +1415,15 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
/* Again the most common case of merging one remote. */
struct strbuf msg = STRBUF_INIT;
struct commit *commit;
- char hex[41];
- strcpy(hex, find_unique_abbrev(head_commit->object.sha1, DEFAULT_ABBREV));
-
- if (verbosity >= 0)
- printf(_("Updating %s..%s\n"),
- hex,
- find_unique_abbrev(remoteheads->item->object.sha1,
- DEFAULT_ABBREV));
+ if (verbosity >= 0) {
+ char from[GIT_SHA1_HEXSZ + 1], to[GIT_SHA1_HEXSZ + 1];
+ find_unique_abbrev_r(from, head_commit->object.sha1,
+ DEFAULT_ABBREV);
+ find_unique_abbrev_r(to, remoteheads->item->object.sha1,
+ DEFAULT_ABBREV);
+ printf(_("Updating %s..%s\n"), from, to);
+ }
strbuf_addstr(&msg, "Fast-forward");
if (have_message)
strbuf_addstr(&msg,
diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index 248a3eb260..0377fc1142 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -55,20 +55,16 @@ copy_data:
parents;
parents = parents->next, parent_number++) {
if (parent_number > 1) {
- int len = strlen(tip_name);
- char *new_name = xmalloc(len +
- 1 + decimal_length(generation) + /* ~<n> */
- 1 + 2 + /* ^NN */
- 1);
-
- if (len > 2 && !strcmp(tip_name + len - 2, "^0"))
- len -= 2;
+ size_t len;
+ char *new_name;
+
+ strip_suffix(tip_name, "^0", &len);
if (generation > 0)
- sprintf(new_name, "%.*s~%d^%d", len, tip_name,
- generation, parent_number);
+ new_name = xstrfmt("%.*s~%d^%d", (int)len, tip_name,
+ generation, parent_number);
else
- sprintf(new_name, "%.*s^%d", len, tip_name,
- parent_number);
+ new_name = xstrfmt("%.*s^%d", (int)len, tip_name,
+ parent_number);
name_rev(parents->item, new_name, 0,
distance + MERGE_TRAVERSAL_WEIGHT, 0);
diff --git a/builtin/notes.c b/builtin/notes.c
index 3608c64785..13c0af9155 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -19,7 +19,7 @@
#include "string-list.h"
#include "notes-merge.h"
#include "notes-utils.h"
-#include "branch.h"
+#include "worktree.h"
static const char * const git_notes_usage[] = {
N_("git notes [--ref <notes-ref>] [list [<object>]]"),
diff --git a/builtin/read-tree.c b/builtin/read-tree.c
index 2379e11069..8c693e7568 100644
--- a/builtin/read-tree.c
+++ b/builtin/read-tree.c
@@ -90,7 +90,7 @@ static int debug_merge(const struct cache_entry * const *stages,
debug_stage("index", stages[0], o);
for (i = 1; i <= o->merge_size; i++) {
char buf[24];
- sprintf(buf, "ent#%d", i);
+ xsnprintf(buf, sizeof(buf), "ent#%d", i);
debug_stage(buf, stages[i], o);
}
return 0;
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index e6b93d0264..bcb624bc05 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -280,10 +280,10 @@ static void rp_warning(const char *err, ...) __attribute__((format (printf, 1, 2
static void report_message(const char *prefix, const char *err, va_list params)
{
- int sz = strlen(prefix);
+ int sz;
char msg[4096];
- strncpy(msg, prefix, sz);
+ sz = xsnprintf(msg, sizeof(msg), "%s", prefix);
sz += vsnprintf(msg + sz, sizeof(msg) - sz, err, params);
if (sz > (sizeof(msg) - 1))
sz = sizeof(msg) - 1;
@@ -1071,8 +1071,11 @@ static void check_aliased_update(struct command *cmd, struct string_list *list)
const char *dst_name;
struct string_list_item *item;
struct command *dst_cmd;
- unsigned char sha1[20];
- char cmd_oldh[41], cmd_newh[41], dst_oldh[41], dst_newh[41];
+ 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);
@@ -1103,10 +1106,10 @@ static void check_aliased_update(struct command *cmd, struct string_list *list)
dst_cmd->skip_update = 1;
- strcpy(cmd_oldh, find_unique_abbrev(cmd->old_sha1, DEFAULT_ABBREV));
- strcpy(cmd_newh, find_unique_abbrev(cmd->new_sha1, DEFAULT_ABBREV));
- strcpy(dst_oldh, find_unique_abbrev(dst_cmd->old_sha1, DEFAULT_ABBREV));
- strcpy(dst_newh, find_unique_abbrev(dst_cmd->new_sha1, DEFAULT_ABBREV));
+ 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,
@@ -1521,15 +1524,18 @@ static const char *unpack(int err_fd, struct shallow_info *si)
if (status)
return "unpack-objects abnormal exit";
} else {
- int s;
- char keep_arg[256];
-
- s = sprintf(keep_arg, "--keep=receive-pack %"PRIuMAX" on ", (uintmax_t) getpid());
- if (gethostname(keep_arg + s, sizeof(keep_arg) - s))
- strcpy(keep_arg + s, "localhost");
+ char hostname[256];
argv_array_pushl(&child.args, "index-pack",
- "--stdin", hdr_arg, keep_arg, NULL);
+ "--stdin", hdr_arg, NULL);
+
+ if (gethostname(hostname, sizeof(hostname)))
+ xsnprintf(hostname, sizeof(hostname), "localhost");
+ argv_array_pushf(&child.args,
+ "--keep=receive-pack %"PRIuMAX" on %s",
+ (uintmax_t)getpid(),
+ hostname);
+
if (fsck_objects)
argv_array_pushf(&child.args, "--strict%s",
fsck_msg_types.buf);
diff --git a/builtin/remote-ext.c b/builtin/remote-ext.c
index 3b8c22cc75..e3cd25d580 100644
--- a/builtin/remote-ext.c
+++ b/builtin/remote-ext.c
@@ -1,6 +1,7 @@
#include "builtin.h"
#include "transport.h"
#include "run-command.h"
+#include "pkt-line.h"
/*
* URL syntax:
@@ -142,36 +143,11 @@ static const char **parse_argv(const char *arg, const char *service)
static void send_git_request(int stdin_fd, const char *serv, const char *repo,
const char *vhost)
{
- size_t bufferspace;
- size_t wpos = 0;
- char *buffer;
-
- /*
- * Request needs 12 bytes extra if there is vhost (xxxx \0host=\0) and
- * 6 bytes extra (xxxx \0) if there is no vhost.
- */
- if (vhost)
- bufferspace = strlen(serv) + strlen(repo) + strlen(vhost) + 12;
+ if (!vhost)
+ packet_write(stdin_fd, "%s %s%c", serv, repo, 0);
else
- bufferspace = strlen(serv) + strlen(repo) + 6;
-
- if (bufferspace > 0xFFFF)
- die("Request too large to send");
- buffer = xmalloc(bufferspace);
-
- /* Make the packet. */
- wpos = sprintf(buffer, "%04x%s %s%c", (unsigned)bufferspace,
- serv, repo, 0);
-
- /* Add vhost if any. */
- if (vhost)
- sprintf(buffer + wpos, "host=%s%c", vhost, 0);
-
- /* Send the request */
- if (write_in_full(stdin_fd, buffer, bufferspace) < 0)
- die_errno("Failed to send request");
-
- free(buffer);
+ packet_write(stdin_fd, "%s %s%chost=%s%c", serv, repo, 0,
+ vhost, 0);
}
static int run_child(const char *arg, const char *service)
diff --git a/builtin/remote.c b/builtin/remote.c
index 181668dedd..e4c3ea130c 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -18,6 +18,7 @@ static const char * const builtin_remote_usage[] = {
N_("git remote prune [-n | --dry-run] <name>"),
N_("git remote [-v | --verbose] update [-p | --prune] [(<group> | <remote>)...]"),
N_("git remote set-branches [--add] <name> <branch>..."),
+ N_("git remote get-url [--push] [--all] <name>"),
N_("git remote set-url [--push] <name> <newurl> [<oldurl>]"),
N_("git remote set-url --add <name> <newurl>"),
N_("git remote set-url --delete <name> <url>"),
@@ -65,6 +66,11 @@ static const char * const builtin_remote_update_usage[] = {
NULL
};
+static const char * const builtin_remote_geturl_usage[] = {
+ N_("git remote get-url [--push] [--all] <name>"),
+ NULL
+};
+
static const char * const builtin_remote_seturl_usage[] = {
N_("git remote set-url [--push] <name> <newurl> [<oldurl>]"),
N_("git remote set-url --add <name> <newurl>"),
@@ -1467,6 +1473,57 @@ static int set_branches(int argc, const char **argv)
return set_remote_branches(argv[0], argv + 1, add_mode);
}
+static int get_url(int argc, const char **argv)
+{
+ int i, push_mode = 0, all_mode = 0;
+ const char *remotename = NULL;
+ struct remote *remote;
+ const char **url;
+ int url_nr;
+ struct option options[] = {
+ OPT_BOOL('\0', "push", &push_mode,
+ N_("query push URLs rather than fetch URLs")),
+ OPT_BOOL('\0', "all", &all_mode,
+ N_("return all URLs")),
+ OPT_END()
+ };
+ argc = parse_options(argc, argv, NULL, options, builtin_remote_geturl_usage, 0);
+
+ if (argc != 1)
+ usage_with_options(builtin_remote_geturl_usage, options);
+
+ remotename = argv[0];
+
+ if (!remote_is_configured(remotename))
+ die(_("No such remote '%s'"), remotename);
+ remote = remote_get(remotename);
+
+ url_nr = 0;
+ if (push_mode) {
+ url = remote->pushurl;
+ url_nr = remote->pushurl_nr;
+ }
+ /* else fetch mode */
+
+ /* Use the fetch URL when no push URLs were found or requested. */
+ if (!url_nr) {
+ url = remote->url;
+ url_nr = remote->url_nr;
+ }
+
+ if (!url_nr)
+ die(_("no URLs configured for remote '%s'"), remotename);
+
+ if (all_mode) {
+ for (i = 0; i < url_nr; i++)
+ printf_ln("%s", url[i]);
+ } else {
+ printf_ln("%s", *url);
+ }
+
+ return 0;
+}
+
static int set_url(int argc, const char **argv)
{
int i, push_mode = 0, add_mode = 0, delete_mode = 0;
@@ -1576,6 +1633,8 @@ int cmd_remote(int argc, const char **argv, const char *prefix)
result = set_head(argc, argv);
else if (!strcmp(argv[0], "set-branches"))
result = set_branches(argc, argv);
+ else if (!strcmp(argv[0], "get-url"))
+ result = get_url(argc, argv);
else if (!strcmp(argv[0], "set-url"))
result = set_url(argc, argv);
else if (!strcmp(argv[0], "show"))
diff --git a/builtin/rerere.c b/builtin/rerere.c
index 88e1359ebc..1bf72423bf 100644
--- a/builtin/rerere.c
+++ b/builtin/rerere.c
@@ -104,9 +104,9 @@ int cmd_rerere(int argc, const char **argv, const char *prefix)
return 0;
for (i = 0; i < merge_rr.nr; i++) {
const char *path = merge_rr.items[i].string;
- const char *name = (const char *)merge_rr.items[i].util;
- if (diff_two(rerere_path(name, "preimage"), path, path, path))
- die("unable to generate diff for %s", name);
+ const struct rerere_id *id = merge_rr.items[i].util;
+ if (diff_two(rerere_path(id, "preimage"), path, path, path))
+ die("unable to generate diff for %s", rerere_path(id, NULL));
}
} else
usage_with_options(rerere_usage, options);
diff --git a/builtin/rev-list.c b/builtin/rev-list.c
index d80d1ed359..491d298fa2 100644
--- a/builtin/rev-list.c
+++ b/builtin/rev-list.c
@@ -217,7 +217,7 @@ static void print_var_int(const char *var, int val)
static int show_bisect_vars(struct rev_list_info *info, int reaches, int all)
{
int cnt, flags = info->flags;
- char hex[41] = "";
+ char hex[GIT_SHA1_HEXSZ + 1] = "";
struct commit_list *tried;
struct rev_info *revs = info->revs;
@@ -242,7 +242,7 @@ static int show_bisect_vars(struct rev_list_info *info, int reaches, int all)
cnt = reaches;
if (revs->commits)
- strcpy(hex, sha1_to_hex(revs->commits->item->object.sha1));
+ sha1_to_hex_r(hex, revs->commits->item->object.sha1);
if (flags & BISECT_SHOW_ALL) {
traverse_commit_list(revs, show_commit, show_object, info);
diff --git a/builtin/show-branch.c b/builtin/show-branch.c
index 408ce70307..092b59b0b3 100644
--- a/builtin/show-branch.c
+++ b/builtin/show-branch.c
@@ -743,6 +743,8 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
fake_av[1] = NULL;
av = fake_av;
ac = 1;
+ if (!*av)
+ die("no branches given, and HEAD is not valid");
}
if (ac != 1)
die("--reflog option needs one branch name");
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
new file mode 100644
index 0000000000..f4c3eff179
--- /dev/null
+++ b/builtin/submodule--helper.c
@@ -0,0 +1,282 @@
+#include "builtin.h"
+#include "cache.h"
+#include "parse-options.h"
+#include "quote.h"
+#include "pathspec.h"
+#include "dir.h"
+#include "utf8.h"
+#include "submodule.h"
+#include "submodule-config.h"
+#include "string-list.h"
+#include "run-command.h"
+
+struct module_list {
+ const struct cache_entry **entries;
+ int alloc, nr;
+};
+#define MODULE_LIST_INIT { NULL, 0, 0 }
+
+static int module_list_compute(int argc, const char **argv,
+ const char *prefix,
+ struct pathspec *pathspec,
+ struct module_list *list)
+{
+ int i, result = 0;
+ char *max_prefix, *ps_matched = NULL;
+ int max_prefix_len;
+ parse_pathspec(pathspec, 0,
+ PATHSPEC_PREFER_FULL |
+ PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP,
+ prefix, argv);
+
+ /* Find common prefix for all pathspec's */
+ max_prefix = common_prefix(pathspec);
+ max_prefix_len = max_prefix ? strlen(max_prefix) : 0;
+
+ if (pathspec->nr)
+ ps_matched = xcalloc(pathspec->nr, 1);
+
+ if (read_cache() < 0)
+ die(_("index file corrupt"));
+
+ for (i = 0; i < active_nr; i++) {
+ const struct cache_entry *ce = active_cache[i];
+
+ if (!S_ISGITLINK(ce->ce_mode) ||
+ !match_pathspec(pathspec, ce->name, ce_namelen(ce),
+ max_prefix_len, ps_matched, 1))
+ continue;
+
+ ALLOC_GROW(list->entries, list->nr + 1, list->alloc);
+ list->entries[list->nr++] = ce;
+ while (i + 1 < active_nr &&
+ !strcmp(ce->name, active_cache[i + 1]->name))
+ /*
+ * Skip entries with the same name in different stages
+ * to make sure an entry is returned only once.
+ */
+ i++;
+ }
+ free(max_prefix);
+
+ if (ps_matched && report_path_error(ps_matched, pathspec, prefix))
+ result = -1;
+
+ free(ps_matched);
+
+ return result;
+}
+
+static int module_list(int argc, const char **argv, const char *prefix)
+{
+ int i;
+ struct pathspec pathspec;
+ struct module_list list = MODULE_LIST_INIT;
+
+ struct option module_list_options[] = {
+ OPT_STRING(0, "prefix", &prefix,
+ N_("path"),
+ N_("alternative anchor for relative paths")),
+ OPT_END()
+ };
+
+ const char *const git_submodule_helper_usage[] = {
+ N_("git submodule--helper list [--prefix=<path>] [<path>...]"),
+ NULL
+ };
+
+ argc = parse_options(argc, argv, prefix, module_list_options,
+ git_submodule_helper_usage, 0);
+
+ if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) {
+ printf("#unmatched\n");
+ return 1;
+ }
+
+ for (i = 0; i < list.nr; i++) {
+ const struct cache_entry *ce = list.entries[i];
+
+ if (ce_stage(ce))
+ printf("%06o %s U\t", ce->ce_mode, sha1_to_hex(null_sha1));
+ else
+ printf("%06o %s %d\t", ce->ce_mode, sha1_to_hex(ce->sha1), ce_stage(ce));
+
+ utf8_fprintf(stdout, "%s\n", ce->name);
+ }
+ return 0;
+}
+
+static int module_name(int argc, const char **argv, const char *prefix)
+{
+ const struct submodule *sub;
+
+ if (argc != 2)
+ usage(_("git submodule--helper name <path>"));
+
+ gitmodules_config();
+ sub = submodule_from_path(null_sha1, argv[1]);
+
+ if (!sub)
+ die(_("no submodule mapping found in .gitmodules for path '%s'"),
+ argv[1]);
+
+ printf("%s\n", sub->name);
+
+ return 0;
+}
+static int clone_submodule(const char *path, const char *gitdir, const char *url,
+ const char *depth, const char *reference, int quiet)
+{
+ struct child_process cp;
+ child_process_init(&cp);
+
+ argv_array_push(&cp.args, "clone");
+ argv_array_push(&cp.args, "--no-checkout");
+ if (quiet)
+ argv_array_push(&cp.args, "--quiet");
+ if (depth && *depth)
+ argv_array_pushl(&cp.args, "--depth", depth, NULL);
+ if (reference && *reference)
+ argv_array_pushl(&cp.args, "--reference", reference, NULL);
+ if (gitdir && *gitdir)
+ argv_array_pushl(&cp.args, "--separate-git-dir", gitdir, NULL);
+
+ argv_array_push(&cp.args, url);
+ argv_array_push(&cp.args, path);
+
+ cp.git_cmd = 1;
+ cp.env = local_repo_env;
+ cp.no_stdin = 1;
+
+ return run_command(&cp);
+}
+
+static int module_clone(int argc, const char **argv, const char *prefix)
+{
+ const char *path = NULL, *name = NULL, *url = NULL;
+ const char *reference = NULL, *depth = NULL;
+ int quiet = 0;
+ FILE *submodule_dot_git;
+ char *sm_gitdir, *cwd, *p;
+ struct strbuf rel_path = STRBUF_INIT;
+ struct strbuf sb = STRBUF_INIT;
+
+ struct option module_clone_options[] = {
+ OPT_STRING(0, "prefix", &prefix,
+ N_("path"),
+ N_("alternative anchor for relative paths")),
+ OPT_STRING(0, "path", &path,
+ N_("path"),
+ N_("where the new submodule will be cloned to")),
+ OPT_STRING(0, "name", &name,
+ N_("string"),
+ N_("name of the new submodule")),
+ OPT_STRING(0, "url", &url,
+ N_("string"),
+ N_("url where to clone the submodule from")),
+ OPT_STRING(0, "reference", &reference,
+ N_("string"),
+ N_("reference repository")),
+ OPT_STRING(0, "depth", &depth,
+ N_("string"),
+ N_("depth for shallow clones")),
+ OPT__QUIET(&quiet, "Suppress output for cloning a submodule"),
+ OPT_END()
+ };
+
+ const char *const git_submodule_helper_usage[] = {
+ N_("git submodule--helper clone [--prefix=<path>] [--quiet] "
+ "[--reference <repository>] [--name <name>] [--url <url>]"
+ "[--depth <depth>] [--] [<path>...]"),
+ NULL
+ };
+
+ argc = parse_options(argc, argv, prefix, module_clone_options,
+ git_submodule_helper_usage, 0);
+
+ strbuf_addf(&sb, "%s/modules/%s", get_git_dir(), name);
+ sm_gitdir = strbuf_detach(&sb, NULL);
+
+ if (!file_exists(sm_gitdir)) {
+ if (safe_create_leading_directories_const(sm_gitdir) < 0)
+ die(_("could not create directory '%s'"), sm_gitdir);
+ if (clone_submodule(path, sm_gitdir, url, depth, reference, quiet))
+ die(_("clone of '%s' into submodule path '%s' failed"),
+ url, path);
+ } else {
+ if (safe_create_leading_directories_const(path) < 0)
+ die(_("could not create directory '%s'"), path);
+ strbuf_addf(&sb, "%s/index", sm_gitdir);
+ unlink_or_warn(sb.buf);
+ strbuf_reset(&sb);
+ }
+
+ /* Write a .git file in the submodule to redirect to the superproject. */
+ if (safe_create_leading_directories_const(path) < 0)
+ die(_("could not create directory '%s'"), path);
+
+ if (path && *path)
+ strbuf_addf(&sb, "%s/.git", path);
+ else
+ strbuf_addstr(&sb, ".git");
+
+ if (safe_create_leading_directories_const(sb.buf) < 0)
+ die(_("could not create leading directories of '%s'"), sb.buf);
+ submodule_dot_git = fopen(sb.buf, "w");
+ if (!submodule_dot_git)
+ die_errno(_("cannot open file '%s'"), sb.buf);
+
+ fprintf(submodule_dot_git, "gitdir: %s\n",
+ relative_path(sm_gitdir, path, &rel_path));
+ if (fclose(submodule_dot_git))
+ die(_("could not close file %s"), sb.buf);
+ strbuf_reset(&sb);
+ strbuf_reset(&rel_path);
+
+ cwd = xgetcwd();
+ /* Redirect the worktree of the submodule in the superproject's config */
+ if (!is_absolute_path(sm_gitdir)) {
+ strbuf_addf(&sb, "%s/%s", cwd, sm_gitdir);
+ free(sm_gitdir);
+ sm_gitdir = strbuf_detach(&sb, NULL);
+ }
+
+ strbuf_addf(&sb, "%s/%s", cwd, path);
+ p = git_pathdup_submodule(path, "config");
+ if (!p)
+ die(_("could not get submodule directory for '%s'"), path);
+ git_config_set_in_file(p, "core.worktree",
+ relative_path(sb.buf, sm_gitdir, &rel_path));
+ strbuf_release(&sb);
+ strbuf_release(&rel_path);
+ free(sm_gitdir);
+ free(cwd);
+ free(p);
+ return 0;
+}
+
+struct cmd_struct {
+ const char *cmd;
+ int (*fn)(int, const char **, const char *);
+};
+
+static struct cmd_struct commands[] = {
+ {"list", module_list},
+ {"name", module_name},
+ {"clone", module_clone},
+};
+
+int cmd_submodule__helper(int argc, const char **argv, const char *prefix)
+{
+ int i;
+ if (argc < 2)
+ die(_("fatal: submodule--helper subcommand must be "
+ "called with a subcommand"));
+
+ for (i = 0; i < ARRAY_SIZE(commands); i++)
+ if (!strcmp(argv[1], commands[i].cmd))
+ return commands[i].fn(argc - 1, argv + 1, prefix);
+
+ die(_("fatal: '%s' is not a valid submodule--helper "
+ "subcommand"), argv[1]);
+}
diff --git a/builtin/tag.c b/builtin/tag.c
index cba0e22666..9e17dca7ca 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -17,271 +17,49 @@
#include "gpg-interface.h"
#include "sha1-array.h"
#include "column.h"
+#include "ref-filter.h"
static const char * const git_tag_usage[] = {
N_("git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] <tagname> [<head>]"),
N_("git tag -d <tagname>..."),
N_("git tag -l [-n[<num>]] [--contains <commit>] [--points-at <object>]"
- "\n\t\t[<pattern>...]"),
+ "\n\t\t[--format=<format>] [--[no-]merged [<commit>]] [<pattern>...]"),
N_("git tag -v <tagname>..."),
NULL
};
-#define STRCMP_SORT 0 /* must be zero */
-#define VERCMP_SORT 1
-#define SORT_MASK 0x7fff
-#define REVERSE_SORT 0x8000
-
-static int tag_sort;
-
-struct tag_filter {
- const char **patterns;
- int lines;
- int sort;
- struct string_list tags;
- struct commit_list *with_commit;
-};
-
-static struct sha1_array points_at;
static unsigned int colopts;
-static int match_pattern(const char **patterns, const char *ref)
-{
- /* no pattern means match everything */
- if (!*patterns)
- return 1;
- for (; *patterns; patterns++)
- if (!wildmatch(*patterns, ref, 0, NULL))
- return 1;
- return 0;
-}
-
-static const unsigned char *match_points_at(const char *refname,
- const unsigned char *sha1)
-{
- const unsigned char *tagged_sha1 = NULL;
- struct object *obj;
-
- if (sha1_array_lookup(&points_at, sha1) >= 0)
- return sha1;
- obj = parse_object(sha1);
- if (!obj)
- die(_("malformed object at '%s'"), refname);
- if (obj->type == OBJ_TAG)
- tagged_sha1 = ((struct tag *)obj)->tagged->sha1;
- if (tagged_sha1 && sha1_array_lookup(&points_at, tagged_sha1) >= 0)
- return tagged_sha1;
- return NULL;
-}
-
-static int in_commit_list(const struct commit_list *want, struct commit *c)
-{
- for (; want; want = want->next)
- if (!hashcmp(want->item->object.sha1, c->object.sha1))
- return 1;
- return 0;
-}
-
-enum contains_result {
- CONTAINS_UNKNOWN = -1,
- CONTAINS_NO = 0,
- CONTAINS_YES = 1
-};
-
-/*
- * Test whether the candidate or one of its parents is contained in the list.
- * Do not recurse to find out, though, but return -1 if inconclusive.
- */
-static enum contains_result contains_test(struct commit *candidate,
- const struct commit_list *want)
-{
- /* was it previously marked as containing a want commit? */
- if (candidate->object.flags & TMP_MARK)
- return 1;
- /* or marked as not possibly containing a want commit? */
- if (candidate->object.flags & UNINTERESTING)
- return 0;
- /* or are we it? */
- if (in_commit_list(want, candidate)) {
- candidate->object.flags |= TMP_MARK;
- return 1;
- }
-
- if (parse_commit(candidate) < 0)
- return 0;
-
- return -1;
-}
-
-/*
- * Mimicking the real stack, this stack lives on the heap, avoiding stack
- * overflows.
- *
- * At each recursion step, the stack items points to the commits whose
- * ancestors are to be inspected.
- */
-struct stack {
- int nr, alloc;
- struct stack_entry {
- struct commit *commit;
- struct commit_list *parents;
- } *stack;
-};
-
-static void push_to_stack(struct commit *candidate, struct stack *stack)
-{
- int index = stack->nr++;
- ALLOC_GROW(stack->stack, stack->nr, stack->alloc);
- stack->stack[index].commit = candidate;
- stack->stack[index].parents = candidate->parents;
-}
-
-static enum contains_result contains(struct commit *candidate,
- const struct commit_list *want)
-{
- struct stack stack = { 0, 0, NULL };
- int result = contains_test(candidate, want);
-
- if (result != CONTAINS_UNKNOWN)
- return result;
-
- push_to_stack(candidate, &stack);
- while (stack.nr) {
- struct stack_entry *entry = &stack.stack[stack.nr - 1];
- struct commit *commit = entry->commit;
- struct commit_list *parents = entry->parents;
-
- if (!parents) {
- commit->object.flags |= UNINTERESTING;
- stack.nr--;
- }
- /*
- * If we just popped the stack, parents->item has been marked,
- * therefore contains_test will return a meaningful 0 or 1.
- */
- else switch (contains_test(parents->item, want)) {
- case CONTAINS_YES:
- commit->object.flags |= TMP_MARK;
- stack.nr--;
- break;
- case CONTAINS_NO:
- entry->parents = parents->next;
- break;
- case CONTAINS_UNKNOWN:
- push_to_stack(parents->item, &stack);
- break;
- }
- }
- free(stack.stack);
- return contains_test(candidate, want);
-}
-
-static void show_tag_lines(const struct object_id *oid, int lines)
+static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting, const char *format)
{
+ struct ref_array array;
+ char *to_free = NULL;
int i;
- unsigned long size;
- enum object_type type;
- char *buf, *sp, *eol;
- size_t len;
-
- buf = read_sha1_file(oid->hash, &type, &size);
- if (!buf)
- die_errno("unable to read object %s", oid_to_hex(oid));
- if (type != OBJ_COMMIT && type != OBJ_TAG)
- goto free_return;
- if (!size)
- die("an empty %s object %s?",
- typename(type), oid_to_hex(oid));
-
- /* skip header */
- sp = strstr(buf, "\n\n");
- if (!sp)
- goto free_return;
-
- /* only take up to "lines" lines, and strip the signature from a tag */
- if (type == OBJ_TAG)
- size = parse_signature(buf, size);
- for (i = 0, sp += 2; i < lines && sp < buf + size; i++) {
- if (i)
- printf("\n ");
- eol = memchr(sp, '\n', size - (sp - buf));
- len = eol ? eol - sp : size - (sp - buf);
- fwrite(sp, len, 1, stdout);
- if (!eol)
- break;
- sp = eol + 1;
- }
-free_return:
- free(buf);
-}
-
-static int show_reference(const char *refname, const struct object_id *oid,
- int flag, void *cb_data)
-{
- struct tag_filter *filter = cb_data;
- if (match_pattern(filter->patterns, refname)) {
- if (filter->with_commit) {
- struct commit *commit;
+ memset(&array, 0, sizeof(array));
- commit = lookup_commit_reference_gently(oid->hash, 1);
- if (!commit)
- return 0;
- if (!contains(commit, filter->with_commit))
- return 0;
- }
-
- if (points_at.nr && !match_points_at(refname, oid->hash))
- return 0;
+ if (filter->lines == -1)
+ filter->lines = 0;
- if (!filter->lines) {
- if (filter->sort)
- string_list_append(&filter->tags, refname);
- else
- printf("%s\n", refname);
- return 0;
- }
- printf("%-15s ", refname);
- show_tag_lines(oid, filter->lines);
- putchar('\n');
+ if (!format) {
+ if (filter->lines) {
+ to_free = xstrfmt("%s %%(contents:lines=%d)",
+ "%(align:15)%(refname:short)%(end)",
+ filter->lines);
+ format = to_free;
+ } else
+ format = "%(refname:short)";
}
- return 0;
-}
+ verify_ref_format(format);
+ filter_refs(&array, filter, FILTER_REFS_TAGS);
+ ref_array_sort(sorting, &array);
-static int sort_by_version(const void *a_, const void *b_)
-{
- const struct string_list_item *a = a_;
- const struct string_list_item *b = b_;
- return versioncmp(a->string, b->string);
-}
+ for (i = 0; i < array.nr; i++)
+ show_ref_array_item(array.items[i], format, 0);
+ ref_array_clear(&array);
+ free(to_free);
-static int list_tags(const char **patterns, int lines,
- struct commit_list *with_commit, int sort)
-{
- struct tag_filter filter;
-
- filter.patterns = patterns;
- filter.lines = lines;
- filter.sort = sort;
- filter.with_commit = with_commit;
- memset(&filter.tags, 0, sizeof(filter.tags));
- filter.tags.strdup_strings = 1;
-
- for_each_tag_ref(show_reference, (void *)&filter);
- if (sort) {
- int i;
- if ((sort & SORT_MASK) == VERCMP_SORT)
- qsort(filter.tags.items, filter.tags.nr,
- sizeof(struct string_list_item), sort_by_version);
- if (sort & REVERSE_SORT)
- for (i = filter.tags.nr - 1; i >= 0; i--)
- printf("%s\n", filter.tags.items[i].string);
- else
- for (i = 0; i < filter.tags.nr; i++)
- printf("%s\n", filter.tags.items[i].string);
- string_list_clear(&filter.tags, 0);
- }
return 0;
}
@@ -348,35 +126,26 @@ static const char tag_template_nocleanup[] =
"Lines starting with '%c' will be kept; you may remove them"
" yourself if you want to.\n");
-/*
- * Parse a sort string, and return 0 if parsed successfully. Will return
- * non-zero when the sort string does not parse into a known type. If var is
- * given, the error message becomes a warning and includes information about
- * the configuration value.
- */
-static int parse_sort_string(const char *var, const char *arg, int *sort)
+/* Parse arg given and add it the ref_sorting array */
+static int parse_sorting_string(const char *arg, struct ref_sorting **sorting_tail)
{
- int type = 0, flags = 0;
+ struct ref_sorting *s;
+ int len;
- if (skip_prefix(arg, "-", &arg))
- flags |= REVERSE_SORT;
+ s = xcalloc(1, sizeof(*s));
+ s->next = *sorting_tail;
+ *sorting_tail = s;
- if (skip_prefix(arg, "version:", &arg) || skip_prefix(arg, "v:", &arg))
- type = VERCMP_SORT;
- else
- type = STRCMP_SORT;
-
- if (strcmp(arg, "refname")) {
- if (!var)
- return error(_("unsupported sort specification '%s'"), arg);
- else {
- warning(_("unsupported sort specification '%s' in variable '%s'"),
- var, arg);
- return -1;
- }
+ if (*arg == '-') {
+ s->reverse = 1;
+ arg++;
}
+ if (skip_prefix(arg, "version:", &arg) ||
+ skip_prefix(arg, "v:", &arg))
+ s->version = 1;
- *sort = (type | flags);
+ len = strlen(arg);
+ s->atom = parse_ref_filter_atom(arg, arg+len);
return 0;
}
@@ -384,11 +153,12 @@ static int parse_sort_string(const char *var, const char *arg, int *sort)
static int git_tag_config(const char *var, const char *value, void *cb)
{
int status;
+ struct ref_sorting **sorting_tail = (struct ref_sorting **)cb;
if (!strcmp(var, "tag.sort")) {
if (!value)
return config_error_nonbool(var);
- parse_sort_string(var, value, &tag_sort);
+ parse_sorting_string(value, sorting_tail);
return 0;
}
@@ -546,30 +316,6 @@ static int strbuf_check_tag_ref(struct strbuf *sb, const char *name)
return check_refname_format(sb->buf, 0);
}
-static int parse_opt_points_at(const struct option *opt __attribute__((unused)),
- const char *arg, int unset)
-{
- unsigned char sha1[20];
-
- if (unset) {
- sha1_array_clear(&points_at);
- return 0;
- }
- if (!arg)
- return error(_("switch 'points-at' requires an object"));
- if (get_sha1(arg, sha1))
- return error(_("malformed object name '%s'"), arg);
- sha1_array_append(&points_at, sha1);
- return 0;
-}
-
-static int parse_opt_sort(const struct option *opt, const char *arg, int unset)
-{
- int *sort = opt->value;
-
- return parse_sort_string(NULL, arg, sort);
-}
-
int cmd_tag(int argc, const char **argv, const char *prefix)
{
struct strbuf buf = STRBUF_INIT;
@@ -578,17 +324,19 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
const char *object_ref, *tag;
struct create_tag_options opt;
char *cleanup_arg = NULL;
- int annotate = 0, force = 0, lines = -1;
int create_reflog = 0;
+ int annotate = 0, force = 0;
int cmdmode = 0;
const char *msgfile = NULL, *keyid = NULL;
struct msg_arg msg = { 0, STRBUF_INIT };
- struct commit_list *with_commit = NULL;
struct ref_transaction *transaction;
struct strbuf err = STRBUF_INIT;
+ struct ref_filter filter;
+ static struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;
+ const char *format = NULL;
struct option options[] = {
OPT_CMDMODE('l', "list", &cmdmode, N_("list tag names"), 'l'),
- { OPTION_INTEGER, 'n', NULL, &lines, N_("n"),
+ { OPTION_INTEGER, 'n', NULL, &filter.lines, N_("n"),
N_("print <n> lines of each tag message"),
PARSE_OPT_OPTARG, NULL, 1 },
OPT_CMDMODE('d', "delete", &cmdmode, N_("delete tags"), 'd'),
@@ -610,32 +358,25 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
OPT_GROUP(N_("Tag listing options")),
OPT_COLUMN(0, "column", &colopts, N_("show tag list in columns")),
+ OPT_CONTAINS(&filter.with_commit, N_("print only tags that contain the commit")),
+ OPT_WITH(&filter.with_commit, N_("print only tags that contain the commit")),
+ OPT_MERGED(&filter, N_("print only tags that are merged")),
+ OPT_NO_MERGED(&filter, N_("print only tags that are not merged")),
+ OPT_CALLBACK(0 , "sort", sorting_tail, N_("key"),
+ N_("field name to sort on"), &parse_opt_ref_sorting),
{
- OPTION_CALLBACK, 0, "sort", &tag_sort, N_("type"), N_("sort tags"),
- PARSE_OPT_NONEG, parse_opt_sort
- },
- {
- OPTION_CALLBACK, 0, "contains", &with_commit, N_("commit"),
- N_("print only tags that contain the commit"),
- PARSE_OPT_LASTARG_DEFAULT,
- parse_opt_with_commit, (intptr_t)"HEAD",
- },
- {
- OPTION_CALLBACK, 0, "with", &with_commit, N_("commit"),
- N_("print only tags that contain the commit"),
- PARSE_OPT_HIDDEN | PARSE_OPT_LASTARG_DEFAULT,
- parse_opt_with_commit, (intptr_t)"HEAD",
- },
- {
- OPTION_CALLBACK, 0, "points-at", NULL, N_("object"),
- N_("print only tags of the object"), 0, parse_opt_points_at
+ OPTION_CALLBACK, 0, "points-at", &filter.points_at, N_("object"),
+ N_("print only tags of the object"), 0, parse_opt_object_name
},
+ OPT_STRING( 0 , "format", &format, N_("format"), N_("format to use for the output")),
OPT_END()
};
- git_config(git_tag_config, NULL);
+ git_config(git_tag_config, sorting_tail);
memset(&opt, 0, sizeof(opt));
+ memset(&filter, 0, sizeof(filter));
+ filter.lines = -1;
argc = parse_options(argc, argv, prefix, options, git_tag_usage, 0);
@@ -652,11 +393,13 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
usage_with_options(git_tag_usage, options);
finalize_colopts(&colopts, -1);
- if (cmdmode == 'l' && lines != -1) {
+ if (cmdmode == 'l' && filter.lines != -1) {
if (explicitly_enable_column(colopts))
die(_("--column and -n are incompatible"));
colopts = 0;
}
+ if (!sorting)
+ sorting = ref_default_sorting();
if (cmdmode == 'l') {
int ret;
if (column_active(colopts)) {
@@ -665,19 +408,20 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
copts.padding = 2;
run_column_filter(colopts, &copts);
}
- if (lines != -1 && tag_sort)
- die(_("--sort and -n are incompatible"));
- ret = list_tags(argv, lines == -1 ? 0 : lines, with_commit, tag_sort);
+ filter.name_patterns = argv;
+ ret = list_tags(&filter, sorting, format);
if (column_active(colopts))
stop_column_filter();
return ret;
}
- if (lines != -1)
+ if (filter.lines != -1)
die(_("-n option is only allowed with -l."));
- if (with_commit)
+ if (filter.with_commit)
die(_("--contains option is only allowed with -l."));
- if (points_at.nr)
+ if (filter.points_at.nr)
die(_("--points-at option is only allowed with -l."));
+ if (filter.merge_commit)
+ die(_("--merged and --no-merged option are only allowed with -l"));
if (cmdmode == 'd')
return for_each_tag_name(argv, delete_tag);
if (cmdmode == 'v')
diff --git a/builtin/unpack-file.c b/builtin/unpack-file.c
index 19200291a2..6fc6bcdf7f 100644
--- a/builtin/unpack-file.c
+++ b/builtin/unpack-file.c
@@ -12,7 +12,7 @@ static char *create_temp_file(unsigned char *sha1)
if (!buf || type != OBJ_BLOB)
die("unable to read blob object %s", sha1_to_hex(sha1));
- strcpy(path, ".merge_file_XXXXXX");
+ xsnprintf(path, sizeof(path), ".merge_file_XXXXXX");
fd = xmkstemp(path);
if (write_in_full(fd, buf, size) != size)
die_errno("unable to write temp-file");
diff --git a/builtin/upload-archive.c b/builtin/upload-archive.c
index 32ab94cd06..dbfe14f3fe 100644
--- a/builtin/upload-archive.c
+++ b/builtin/upload-archive.c
@@ -49,15 +49,14 @@ int cmd_upload_archive_writer(int argc, const char **argv, const char *prefix)
__attribute__((format (printf, 1, 2)))
static void error_clnt(const char *fmt, ...)
{
- char buf[1024];
+ struct strbuf buf = STRBUF_INIT;
va_list params;
- int len;
va_start(params, fmt);
- len = vsprintf(buf, fmt, params);
+ strbuf_vaddf(&buf, fmt, params);
va_end(params);
- send_sideband(1, 3, buf, len, LARGE_PACKET_MAX);
- die("sent error to the client: %s", buf);
+ send_sideband(1, 3, buf.buf, buf.len, LARGE_PACKET_MAX);
+ die("sent error to the client: %s", buf.buf);
}
static ssize_t process_input(int child_fd, int band)
diff --git a/builtin/worktree.c b/builtin/worktree.c
index 71bb770f7a..78d26902a6 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -8,10 +8,13 @@
#include "run-command.h"
#include "sigchain.h"
#include "refs.h"
+#include "utf8.h"
+#include "worktree.h"
static const char * const worktree_usage[] = {
N_("git worktree add [<options>] <path> <branch>"),
N_("git worktree prune [<options>]"),
+ N_("git worktree list [<options>]"),
NULL
};
@@ -359,6 +362,89 @@ static int add(int ac, const char **av, const char *prefix)
return add_worktree(path, branch, &opts);
}
+static void show_worktree_porcelain(struct worktree *wt)
+{
+ printf("worktree %s\n", wt->path);
+ if (wt->is_bare)
+ printf("bare\n");
+ else {
+ printf("HEAD %s\n", sha1_to_hex(wt->head_sha1));
+ if (wt->is_detached)
+ printf("detached\n");
+ else
+ printf("branch %s\n", wt->head_ref);
+ }
+ printf("\n");
+}
+
+static void show_worktree(struct worktree *wt, int path_maxlen, int abbrev_len)
+{
+ struct strbuf sb = STRBUF_INIT;
+ int cur_path_len = strlen(wt->path);
+ int path_adj = cur_path_len - utf8_strwidth(wt->path);
+
+ strbuf_addf(&sb, "%-*s ", 1 + path_maxlen + path_adj, wt->path);
+ if (wt->is_bare)
+ strbuf_addstr(&sb, "(bare)");
+ else {
+ strbuf_addf(&sb, "%-*s ", abbrev_len,
+ find_unique_abbrev(wt->head_sha1, DEFAULT_ABBREV));
+ if (!wt->is_detached)
+ strbuf_addf(&sb, "[%s]", shorten_unambiguous_ref(wt->head_ref, 0));
+ else
+ strbuf_addstr(&sb, "(detached HEAD)");
+ }
+ printf("%s\n", sb.buf);
+
+ strbuf_release(&sb);
+}
+
+static void measure_widths(struct worktree **wt, int *abbrev, int *maxlen)
+{
+ int i;
+
+ for (i = 0; wt[i]; i++) {
+ int sha1_len;
+ int path_len = strlen(wt[i]->path);
+
+ if (path_len > *maxlen)
+ *maxlen = path_len;
+ sha1_len = strlen(find_unique_abbrev(wt[i]->head_sha1, *abbrev));
+ if (sha1_len > *abbrev)
+ *abbrev = sha1_len;
+ }
+}
+
+static int list(int ac, const char **av, const char *prefix)
+{
+ int porcelain = 0;
+
+ struct option options[] = {
+ OPT_BOOL(0, "porcelain", &porcelain, N_("machine-readable output")),
+ OPT_END()
+ };
+
+ ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
+ if (ac)
+ usage_with_options(worktree_usage, options);
+ else {
+ struct worktree **worktrees = get_worktrees();
+ int path_maxlen = 0, abbrev = DEFAULT_ABBREV, i;
+
+ if (!porcelain)
+ measure_widths(worktrees, &abbrev, &path_maxlen);
+
+ for (i = 0; worktrees[i]; i++) {
+ if (porcelain)
+ show_worktree_porcelain(worktrees[i]);
+ else
+ show_worktree(worktrees[i], path_maxlen, abbrev);
+ }
+ free_worktrees(worktrees);
+ }
+ return 0;
+}
+
int cmd_worktree(int ac, const char **av, const char *prefix)
{
struct option options[] = {
@@ -371,5 +457,7 @@ int cmd_worktree(int ac, const char **av, const char *prefix)
return add(ac - 1, av + 1, prefix);
if (!strcmp(av[1], "prune"))
return prune(ac - 1, av + 1, prefix);
+ if (!strcmp(av[1], "list"))
+ return list(ac - 1, av + 1, prefix);
usage_with_options(worktree_usage, options);
}
diff --git a/bulk-checkin.c b/bulk-checkin.c
index 7cffc3a579..4347f5c76a 100644
--- a/bulk-checkin.c
+++ b/bulk-checkin.c
@@ -200,8 +200,8 @@ static int deflate_to_pack(struct bulk_checkin_state *state,
if (seekback == (off_t) -1)
return error("cannot find the current offset");
- header_len = sprintf((char *)obuf, "%s %" PRIuMAX,
- typename(type), (uintmax_t)size) + 1;
+ header_len = xsnprintf((char *)obuf, sizeof(obuf), "%s %" PRIuMAX,
+ typename(type), (uintmax_t)size) + 1;
git_SHA1_Init(&ctx);
git_SHA1_Update(&ctx, obuf, header_len);
diff --git a/cache.h b/cache.h
index 79066e57dc..f735d14dc2 100644
--- a/cache.h
+++ b/cache.h
@@ -443,6 +443,7 @@ extern char *get_object_directory(void);
extern char *get_index_file(void);
extern char *get_graft_file(void);
extern int set_git_dir(const char *path);
+extern int get_common_dir_noenv(struct strbuf *sb, const char *gitdir);
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);
@@ -723,6 +724,8 @@ extern char *mksnpath(char *buf, size_t n, const char *fmt, ...)
__attribute__((format (printf, 3, 4)));
extern void strbuf_git_path(struct strbuf *sb, const char *fmt, ...)
__attribute__((format (printf, 2, 3)));
+extern char *git_path_buf(struct strbuf *buf, const char *fmt, ...)
+ __attribute__((format (printf, 2, 3)));
extern void strbuf_git_path_submodule(struct strbuf *sb, const char *path,
const char *fmt, ...)
__attribute__((format (printf, 3, 4)));
@@ -783,7 +786,24 @@ extern char *sha1_pack_name(const unsigned char *sha1);
*/
extern char *sha1_pack_index_name(const unsigned char *sha1);
-extern const char *find_unique_abbrev(const unsigned char *sha1, int);
+/*
+ * Return an abbreviated sha1 unique within this repository's object database.
+ * 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 `_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
+ * written (excluding the NUL terminator).
+ *
+ * Note that while this version avoids the static buffer, it is not fully
+ * reentrant, as it calls into other non-reentrant git code.
+ */
+extern const char *find_unique_abbrev(const unsigned char *sha1, int len);
+extern int find_unique_abbrev_r(char *hex, const unsigned char *sha1, int len);
+
extern const unsigned char null_sha1[GIT_SHA1_RAWSZ];
static inline int hashcmp(const unsigned char *sha1, const unsigned char *sha2)
@@ -1065,6 +1085,18 @@ extern int for_each_abbrev(const char *prefix, each_abbrev_fn, void *);
extern int get_sha1_hex(const char *hex, unsigned char *sha1);
extern int get_oid_hex(const char *hex, struct object_id *sha1);
+/*
+ * Convert a binary sha1 to its hex equivalent. The `_r` variant is reentrant,
+ * and writes the NUL-terminated output to the buffer `out`, which must be at
+ * least `GIT_SHA1_HEXSZ + 1` bytes, and returns a pointer to out for
+ * convenience.
+ *
+ * The non-`_r` variant returns a static buffer, but uses a ring of 4
+ * buffers, making it safe to make multiple calls for a single statement, like:
+ *
+ * printf("%s -> %s", sha1_to_hex(one), sha1_to_hex(two));
+ */
+extern char *sha1_to_hex_r(char *out, const unsigned char *sha1);
extern char *sha1_to_hex(const unsigned char *sha1); /* static buffer result! */
extern char *oid_to_hex(const struct object_id *oid); /* same static buffer as sha1_to_hex */
@@ -1091,7 +1123,6 @@ struct date_mode {
DATE_NORMAL = 0,
DATE_RELATIVE,
DATE_SHORT,
- DATE_LOCAL,
DATE_ISO8601,
DATE_ISO8601_STRICT,
DATE_RFC2822,
@@ -1099,6 +1130,7 @@ struct date_mode {
DATE_RAW
} type;
const char *strftime_fmt;
+ int local;
};
/*
@@ -1275,10 +1307,11 @@ extern void close_pack_index(struct packed_git *);
extern unsigned char *use_pack(struct packed_git *, struct pack_window **, off_t, unsigned long *);
extern void close_pack_windows(struct packed_git *);
+extern void close_all_packs(void);
extern void unuse_pack(struct pack_window **);
extern void free_pack_by_name(const char *);
extern void clear_delta_base_cache(void);
-extern struct packed_git *add_packed_git(const char *, int, int);
+extern struct packed_git *add_packed_git(const char *path, size_t path_len, int local);
/*
* Return the SHA-1 of the nth object within the specified packfile.
diff --git a/color.c b/color.c
index 9027352ad7..8f85153d0d 100644
--- a/color.c
+++ b/color.c
@@ -145,27 +145,34 @@ int color_parse(const char *value, char *dst)
return color_parse_mem(value, strlen(value), dst);
}
+void color_set(char *dst, const char *color_bytes)
+{
+ xsnprintf(dst, COLOR_MAXLEN, "%s", color_bytes);
+}
+
/*
* Write the ANSI color codes for "c" to "out"; the string should
* already have the ANSI escape code in it. "out" should have enough
* space in it to fit any color.
*/
-static char *color_output(char *out, const struct color *c, char type)
+static char *color_output(char *out, int len, const struct color *c, char type)
{
switch (c->type) {
case COLOR_UNSPECIFIED:
case COLOR_NORMAL:
break;
case COLOR_ANSI:
+ if (len < 2)
+ die("BUG: color parsing ran out of space");
*out++ = type;
*out++ = '0' + c->value;
break;
case COLOR_256:
- out += sprintf(out, "%c8;5;%d", type, c->value);
+ out += xsnprintf(out, len, "%c8;5;%d", type, c->value);
break;
case COLOR_RGB:
- out += sprintf(out, "%c8;2;%d;%d;%d", type,
- c->red, c->green, c->blue);
+ out += xsnprintf(out, len, "%c8;2;%d;%d;%d", type,
+ c->red, c->green, c->blue);
break;
}
return out;
@@ -180,12 +187,13 @@ int color_parse_mem(const char *value, int value_len, char *dst)
{
const char *ptr = value;
int len = value_len;
+ char *end = dst + COLOR_MAXLEN;
unsigned int attr = 0;
struct color fg = { COLOR_UNSPECIFIED };
struct color bg = { COLOR_UNSPECIFIED };
if (!strncasecmp(value, "reset", len)) {
- strcpy(dst, GIT_COLOR_RESET);
+ xsnprintf(dst, end - dst, GIT_COLOR_RESET);
return 0;
}
@@ -224,12 +232,19 @@ int color_parse_mem(const char *value, int value_len, char *dst)
goto bad;
}
+#undef OUT
+#define OUT(x) do { \
+ if (dst == end) \
+ die("BUG: color parsing ran out of space"); \
+ *dst++ = (x); \
+} while(0)
+
if (attr || !color_empty(&fg) || !color_empty(&bg)) {
int sep = 0;
int i;
- *dst++ = '\033';
- *dst++ = '[';
+ OUT('\033');
+ OUT('[');
for (i = 0; attr; i++) {
unsigned bit = (1 << i);
@@ -237,27 +252,28 @@ int color_parse_mem(const char *value, int value_len, char *dst)
continue;
attr &= ~bit;
if (sep++)
- *dst++ = ';';
- dst += sprintf(dst, "%d", i);
+ OUT(';');
+ dst += xsnprintf(dst, end - dst, "%d", i);
}
if (!color_empty(&fg)) {
if (sep++)
- *dst++ = ';';
+ OUT(';');
/* foreground colors are all in the 3x range */
- dst = color_output(dst, &fg, '3');
+ dst = color_output(dst, end - dst, &fg, '3');
}
if (!color_empty(&bg)) {
if (sep++)
- *dst++ = ';';
+ OUT(';');
/* background colors are all in the 4x range */
- dst = color_output(dst, &bg, '4');
+ dst = color_output(dst, end - dst, &bg, '4');
}
- *dst++ = 'm';
+ OUT('m');
}
- *dst = 0;
+ OUT(0);
return 0;
bad:
return error(_("invalid color value: %.*s"), value_len, value);
+#undef OUT
}
int git_config_colorbool(const char *var, const char *value)
diff --git a/color.h b/color.h
index 7fe77fb55e..e155d13f78 100644
--- a/color.h
+++ b/color.h
@@ -75,6 +75,13 @@ extern int color_stdout_is_tty;
int git_color_config(const char *var, const char *value, void *cb);
int git_color_default_config(const char *var, const char *value, void *cb);
+/*
+ * Set the color buffer (which must be COLOR_MAXLEN bytes)
+ * to the raw color bytes; this is useful for initializing
+ * default color variables.
+ */
+void color_set(char *dst, const char *color_bytes);
+
int git_config_colorbool(const char *var, const char *value);
int want_color(int var);
int color_parse(const char *value, char *dst);
diff --git a/compat/hstrerror.c b/compat/hstrerror.c
index 069c555da4..b85a2fa956 100644
--- a/compat/hstrerror.c
+++ b/compat/hstrerror.c
@@ -16,6 +16,6 @@ const char *githstrerror(int err)
case TRY_AGAIN:
return "Non-authoritative \"host not found\", or SERVERFAIL";
}
- sprintf(buffer, "Name resolution error %d", err);
+ snprintf(buffer, sizeof(buffer), "Name resolution error %d", err);
return buffer;
}
diff --git a/compat/inet_ntop.c b/compat/inet_ntop.c
index 90b7cc45f3..68307262be 100644
--- a/compat/inet_ntop.c
+++ b/compat/inet_ntop.c
@@ -53,11 +53,11 @@ inet_ntop4(const u_char *src, char *dst, size_t size)
nprinted = snprintf(tmp, sizeof(tmp), fmt, src[0], src[1], src[2], src[3]);
if (nprinted < 0)
return (NULL); /* we assume "errno" was set by "snprintf()" */
- if ((size_t)nprinted > size) {
+ if ((size_t)nprinted >= size) {
errno = ENOSPC;
return (NULL);
}
- strcpy(dst, tmp);
+ strlcpy(dst, tmp, size);
return (dst);
}
@@ -154,7 +154,7 @@ inet_ntop6(const u_char *src, char *dst, size_t size)
errno = ENOSPC;
return (NULL);
}
- strcpy(dst, tmp);
+ strlcpy(dst, tmp, size);
return (dst);
}
#endif
diff --git a/compat/mingw.c b/compat/mingw.c
index f74da235f5..a168800ae0 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -2133,9 +2133,11 @@ int uname(struct utsname *buf)
{
DWORD v = GetVersion();
memset(buf, 0, sizeof(*buf));
- strcpy(buf->sysname, "Windows");
- sprintf(buf->release, "%u.%u", v & 0xff, (v >> 8) & 0xff);
+ xsnprintf(buf->sysname, sizeof(buf->sysname), "Windows");
+ xsnprintf(buf->release, sizeof(buf->release),
+ "%u.%u", v & 0xff, (v >> 8) & 0xff);
/* assuming NT variants only.. */
- sprintf(buf->version, "%u", (v >> 16) & 0x7fff);
+ xsnprintf(buf->version, sizeof(buf->version),
+ "%u", (v >> 16) & 0x7fff);
return 0;
}
diff --git a/compat/nedmalloc/nedmalloc.c b/compat/nedmalloc/nedmalloc.c
index 609ebba125..a0a16eb1bb 100644
--- a/compat/nedmalloc/nedmalloc.c
+++ b/compat/nedmalloc/nedmalloc.c
@@ -957,8 +957,9 @@ char *strdup(const char *s1)
{
char *s2 = 0;
if (s1) {
- s2 = malloc(strlen(s1) + 1);
- strcpy(s2, s1);
+ size_t len = strlen(s1) + 1;
+ s2 = malloc(len);
+ memcpy(s2, s1, len);
}
return s2;
}
diff --git a/compat/precompose_utf8.c b/compat/precompose_utf8.c
index 95fe849e42..079070ff1d 100644
--- a/compat/precompose_utf8.c
+++ b/compat/precompose_utf8.c
@@ -36,24 +36,26 @@ static size_t has_non_ascii(const char *s, size_t maxlen, size_t *strlen_c)
}
-void probe_utf8_pathname_composition(char *path, int len)
+void probe_utf8_pathname_composition(void)
{
+ struct strbuf path = STRBUF_INIT;
static const char *auml_nfc = "\xc3\xa4";
static const char *auml_nfd = "\x61\xcc\x88";
int output_fd;
if (precomposed_unicode != -1)
return; /* We found it defined in the global config, respect it */
- strcpy(path + len, auml_nfc);
- output_fd = open(path, O_CREAT|O_EXCL|O_RDWR, 0600);
+ git_path_buf(&path, "%s", auml_nfc);
+ output_fd = open(path.buf, O_CREAT|O_EXCL|O_RDWR, 0600);
if (output_fd >= 0) {
close(output_fd);
- strcpy(path + len, auml_nfd);
- precomposed_unicode = access(path, R_OK) ? 0 : 1;
+ git_path_buf(&path, "%s", auml_nfd);
+ precomposed_unicode = access(path.buf, R_OK) ? 0 : 1;
git_config_set("core.precomposeunicode", precomposed_unicode ? "true" : "false");
- strcpy(path + len, auml_nfc);
- if (unlink(path))
- die_errno(_("failed to unlink '%s'"), path);
+ git_path_buf(&path, "%s", auml_nfc);
+ if (unlink(path.buf))
+ die_errno(_("failed to unlink '%s'"), path.buf);
}
+ strbuf_release(&path);
}
@@ -139,9 +141,8 @@ struct dirent_prec_psx *precompose_utf8_readdir(PREC_DIR *prec_dir)
size_t inleft = namelenz;
char *outpos = &prec_dir->dirent_nfc->d_name[0];
size_t outsz = prec_dir->dirent_nfc->max_name_len;
- size_t cnt;
errno = 0;
- cnt = iconv(prec_dir->ic_precompose, &cp, &inleft, &outpos, &outsz);
+ iconv(prec_dir->ic_precompose, &cp, &inleft, &outpos, &outsz);
if (errno || inleft) {
/*
* iconv() failed and errno could be E2BIG, EILSEQ, EINVAL, EBADF
diff --git a/compat/precompose_utf8.h b/compat/precompose_utf8.h
index 3b73585fc5..a94e7c4342 100644
--- a/compat/precompose_utf8.h
+++ b/compat/precompose_utf8.h
@@ -27,7 +27,7 @@ typedef struct {
} PREC_DIR;
void precompose_argv(int argc, const char **argv);
-void probe_utf8_pathname_composition(char *, int);
+void probe_utf8_pathname_composition(void);
PREC_DIR *precompose_utf8_opendir(const char *dirname);
struct dirent_prec_psx *precompose_utf8_readdir(PREC_DIR *dirp);
diff --git a/compat/winansi.c b/compat/winansi.c
index efc5bb3a4b..ceff55bd67 100644
--- a/compat/winansi.c
+++ b/compat/winansi.c
@@ -539,7 +539,7 @@ void winansi_init(void)
return;
/* create a named pipe to communicate with the console thread */
- sprintf(name, "\\\\.\\pipe\\winansi%lu", GetCurrentProcessId());
+ xsnprintf(name, sizeof(name), "\\\\.\\pipe\\winansi%lu", GetCurrentProcessId());
hwrite = CreateNamedPipe(name, PIPE_ACCESS_OUTBOUND,
PIPE_TYPE_BYTE | PIPE_WAIT, 1, BUFFER_SIZE, 0, 0, NULL);
if (hwrite == INVALID_HANDLE_VALUE)
diff --git a/config.mak.uname b/config.mak.uname
index 943c43965e..f34dcaad20 100644
--- a/config.mak.uname
+++ b/config.mak.uname
@@ -166,7 +166,6 @@ endif
ifeq ($(uname_O),Cygwin)
ifeq ($(shell expr "$(uname_R)" : '1\.[1-6]\.'),4)
NO_D_TYPE_IN_DIRENT = YesPlease
- NO_D_INO_IN_DIRENT = YesPlease
NO_STRCASESTR = YesPlease
NO_MEMMEM = YesPlease
NO_MKSTEMPS = YesPlease
@@ -370,7 +369,6 @@ ifeq ($(uname_S),Windows)
NO_POSIX_GOODIES = UnfortunatelyYes
NATIVE_CRLF = YesPlease
DEFAULT_HELP_FORMAT = html
- NO_D_INO_IN_DIRENT = YesPlease
CC = compat/vcbuild/scripts/clink.pl
AR = compat/vcbuild/scripts/lib.pl
@@ -520,7 +518,6 @@ ifneq (,$(findstring MINGW,$(uname_S)))
NO_INET_NTOP = YesPlease
NO_POSIX_GOODIES = UnfortunatelyYes
DEFAULT_HELP_FORMAT = html
- NO_D_INO_IN_DIRENT = YesPlease
COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -D_USE_32BIT_TIME_T -DNOGDI -Icompat -Icompat/win32
COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\"
COMPAT_OBJS += compat/mingw.o compat/winansi.o \
diff --git a/configure.ac b/configure.ac
index 14012fad72..3fcca618d2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -767,13 +767,6 @@ elif test x$ac_cv_member_struct_stat_st_mtim_tv_nsec != xyes; then
GIT_CONF_SUBST([NO_NSEC])
fi
#
-# Define NO_D_INO_IN_DIRENT if you don't have d_ino in your struct dirent.
-AC_CHECK_MEMBER(struct dirent.d_ino,
-[NO_D_INO_IN_DIRENT=],
-[NO_D_INO_IN_DIRENT=YesPlease],
-[#include <dirent.h>])
-GIT_CONF_SUBST([NO_D_INO_IN_DIRENT])
-#
# Define NO_D_TYPE_IN_DIRENT if your platform defines DT_UNKNOWN but lacks
# d_type in struct dirent (latest Cygwin -- will be fixed soonish).
AC_CHECK_MEMBER(struct dirent.d_type,
diff --git a/connect.c b/connect.c
index d3283b8a4f..108f5ab60e 100644
--- a/connect.c
+++ b/connect.c
@@ -333,7 +333,7 @@ static const char *ai_name(const struct addrinfo *ai)
static char addr[NI_MAXHOST];
if (getnameinfo(ai->ai_addr, ai->ai_addrlen, addr, sizeof(addr), NULL, 0,
NI_NUMERICHOST) != 0)
- strcpy(addr, "(unknown)");
+ xsnprintf(addr, sizeof(addr), "(unknown)");
return addr;
}
diff --git a/contrib/hooks/multimail/CHANGES b/contrib/hooks/multimail/CHANGES
index 6bb12306b8..bc77e66b85 100644
--- a/contrib/hooks/multimail/CHANGES
+++ b/contrib/hooks/multimail/CHANGES
@@ -1,3 +1,44 @@
+Release 1.2.0
+=============
+
+* It is now possible to exclude some refs (e.g. exclude some branches
+ or tags). See refFilterDoSendRegex, refFilterDontSendRegex,
+ refFilterInclusionRegex and refFilterExclusionRegex.
+
+* New commitEmailFormat option which can be set to "html" to generate
+ simple colorized diffs using HTML for the commit emails.
+
+* git-multimail can now be ran as a Gerrit ref-updated hook, or from
+ Atlassian BitBucket Server (formerly known as Atlassian Stash).
+
+* The From: field is now more customizeable. It can be set
+ independently for refchange emails and commit emails (see
+ fromCommit, fromRefChange). The special values pusher and author can
+ be used in these configuration variable.
+
+* A new command-line option, --version, was added. The version is also
+ available in the X-Git-Multimail-Version header of sent emails.
+
+* Set X-Git-NotificationType header to differentiate the various types
+ of notifications. Current values are: diff, ref_changed_plus_diff,
+ ref_changed.
+
+* Preliminary support for Python 3. The testsuite passes with Python 3,
+ but it has not received as much testing as the Python 2 version yet.
+
+* Several encoding-related fixes. UTF-8 characters work in more
+ situations (but non-ascii characters in email address are still not
+ supported).
+
+* The testsuite and its documentation has been greatly improved.
+
+Plus all the bugfixes from version 1.1.1.
+
+This version has been tested with Python 2.4 and 2.6 to 3.5, and Git
+v1.7.10-406-gdc801e7, git-1.8.2.3 and 2.6.0. Git versions prior to
+v1.7.10-406-gdc801e7 probably work, but cannot run the testsuite
+properly.
+
Release 1.1.1 (bugfix-only release)
===================================
diff --git a/contrib/hooks/multimail/CONTRIBUTING.rst b/contrib/hooks/multimail/CONTRIBUTING.rst
new file mode 100644
index 0000000000..09efdb059c
--- /dev/null
+++ b/contrib/hooks/multimail/CONTRIBUTING.rst
@@ -0,0 +1,30 @@
+git-multimail is an open-source project, built by volunteers. We would
+welcome your help!
+
+The current maintainers are Michael Haggerty <mhagger@alum.mit.edu>
+and Matthieu Moy <matthieu.moy@grenoble-inp.fr>.
+
+Please note that although a copy of git-multimail is distributed in
+the "contrib" section of the main Git project, development takes place
+in a separate git-multimail repository on GitHub:
+
+ https://github.com/git-multimail/git-multimail
+
+Whenever enough changes to git-multimail have accumulated, a new
+code-drop of git-multimail will be submitted for inclusion in the Git
+project.
+
+We use the GitHub issue tracker to keep track of bugs and feature
+requests, and we use GitHub pull requests to exchange patches (though,
+if you prefer, you can send patches via the Git mailing list with CC
+to the maintainers). Please sign off your patches as per the `Git
+project practice
+<https://github.com/git/git/blob/master/Documentation/SubmittingPatches#L234>`__.
+
+General discussion of git-multimail can take place on the main Git
+mailing list,
+
+ git@vger.kernel.org
+
+Please CC emails regarding git-multimail to the maintainers so that we
+don't overlook them.
diff --git a/contrib/hooks/multimail/README b/contrib/hooks/multimail/README
index e552c90c45..55120685f0 100644
--- a/contrib/hooks/multimail/README
+++ b/contrib/hooks/multimail/README
@@ -1,5 +1,5 @@
-git-multimail Version 1.1.1
-===========================
+git-multimail (version 1.2.0)
+=============================
.. image:: https://travis-ci.org/git-multimail/git-multimail.svg?branch=master
:target: https://travis-ci.org/git-multimail/git-multimail
@@ -53,11 +53,13 @@ By default, for each push received by the repository, git-multimail:
+ [git] 07/08: Merge branch 'mm/api-credentials-doc'
+ [git] 08/08: Git 1.7.11-rc2
- Each commit appears in exactly one commit email, the first time
- that it is pushed to the repository. If a commit is later merged
- into another branch, then a one-line summary of the commit is
- included in the reference change email (as usual), but no
- additional commit email is generated.
+ By default, each commit appears in exactly one commit email, the
+ first time that it is pushed to the repository. If a commit is later
+ merged into another branch, then a one-line summary of the commit
+ is included in the reference change email (as usual), but no
+ additional commit email is generated. See
+ `multimailhook.refFilter(Inclusion|Exclusion|DoSend|DontSend)Regex`
+ below to configure which branches and tags are watched by the hook.
By default, reference change emails have their "Reply-To" field set
to the person who pushed the change, and commit emails have their
@@ -73,21 +75,8 @@ Requirements
------------
* Python 2.x, version 2.4 or later. No non-standard Python modules
- are required. git-multimail does *not* currently work with Python
- 3.x.
-
- The example scripts invoke Python using the following shebang line
- (following PEP 394 [1]_)::
-
- #! /usr/bin/env python2
-
- If your system's Python2 interpreter is not in your PATH or is not
- called ``python2``, you can change the lines accordingly. Or you can
- invoke the Python interpreter explicitly, for example via a tiny
- shell script like::
-
- #! /bin/sh
- /usr/local/bin/python /path/to/git_multimail.py "$@"
+ are required. git-multimail has preliminary support for Python 3
+ (but it has been better tested with Python 2).
* The ``git`` command must be in your PATH. git-multimail is known to
work with Git versions back to 1.7.1. (Earlier versions have not
@@ -146,7 +135,9 @@ following ``git config`` settings:
multimailhook.environment
- This describes the general environment of the repository.
+ This describes the general environment of the repository. In most
+ cases, you do not need to specify a value for this variable:
+ `git-multimail` will autodetect which environment to use.
Currently supported values:
* generic
@@ -161,18 +152,57 @@ multimailhook.environment
optionally read from gitolite.conf (see multimailhook.from).
For more information about gitolite and git-multimail, read
- doc/gitolite.rst
+ `<doc/gitolite.rst>`__
+
+ * stash
+
+ Environment to use when ``git-multimail`` is ran as an Atlassian
+ BitBucket Server (formerly known as Atlassian Stash) hook.
+
+ **Warning:** this mode was provided by a third-party contributor
+ and never tested by the git-multimail maintainers. It is
+ provided as-is and may or may not work for you.
+
+ This value is automatically assumed when the stash-specific
+ flags (``--stash-user`` and ``--stash-repo``) are specified on
+ the command line. When this environment is active, the username
+ and repo come from these two command line flags, which must be
+ specified.
+
+ * gerrit
+
+ Environment to use when ``git-multimail`` is ran as a
+ ``ref-updated`` Gerrit hook.
+
+ This value is used when the gerrit-specific command line flags
+ (``--oldrev``, ``--newrev``, ``--refname``, ``--project``) for
+ gerrit's ref-updated hook are present. When this environment is
+ active, the username of the pusher is taken from the
+ ``--submitter`` argument if that command line option is passed,
+ otherwise 'Gerrit' is used. The repository name is taken from
+ the ``--project`` option on the command line, which must be passed.
+
+ For more information about gerrit and git-multimail, read
+ `<doc/gerrit.rst>`__
- If neither of these environments is suitable for your setup, then
- you can implement a Python class that inherits from Environment
- and instantiate it via a script that looks like the example
+ If none of these environments is suitable for your setup, then you
+ can implement a Python class that inherits from Environment and
+ instantiate it via a script that looks like the example
post-receive script.
The environment value can be specified on the command line using
- the --environment option. If it is not specified on the command
- line or by multimailhook.environment, then it defaults to
- ``gitolite`` if the environment contains variables $GL_USER and
- $GL_REPO; otherwise ``generic``.
+ the ``--environment`` option. If it is not specified on the
+ command line or by ``multimailhook.environment``, the value is
+ guessed as follows:
+
+ * If stash-specific (respectively gerrit-specific) command flags
+ are present on the command-line, then ``stash`` (respectively
+ ``gerrit``) is used.
+
+ * If the environment variables $GL_USER and $GL_REPO are set, then
+ ``gitolite`` is used.
+
+ * If none of the above apply, then ``generic`` is used.
multimailhook.repoName
@@ -196,8 +226,8 @@ multimailhook.refchangeList
reference changes should be sent, as RFC 2822 email addresses
separated by commas. This configuration option can be
multivalued. The default is the value in
- multimailhook.mailingList. Set this value to the empty string to
- prevent reference change emails from being sent even if
+ multimailhook.mailingList. Set this value to "none" (or the empty
+ string) to prevent reference change emails from being sent even if
multimailhook.mailingList is set.
multimailhook.announceList
@@ -206,9 +236,9 @@ multimailhook.announceList
tags should be sent, as RFC 2822 email addresses separated by
commas. This configuration option can be multivalued. The
default is the value in multimailhook.refchangeList or
- multimailhook.mailingList. Set this value to the empty string to
- prevent annotated tag announcement emails from being sent even if
- one of the other values is set.
+ multimailhook.mailingList. Set this value to "none" (or the empty
+ string) to prevent annotated tag announcement emails from being sent
+ even if one of the other values is set.
multimailhook.commitList
@@ -216,7 +246,7 @@ multimailhook.commitList
commits should be sent, as RFC 2822 email addresses separated by
commas. This configuration option can be multivalued. The
default is the value in multimailhook.mailingList. Set this value
- to the empty string to prevent notification emails about
+ to "none" (or the empty string) to prevent notification emails about
individual commits from being sent even if
multimailhook.mailingList is set.
@@ -230,6 +260,20 @@ multimailhook.announceShortlog
not so straightforward, then the shortlog might be confusing
rather than useful. Default is false.
+multimailhook.commitEmailFormat
+
+ The format of email messages for the individual commits, can be "text" or
+ "html". In the latter case, the emails will include diffs using colorized
+ HTML instead of plain text used by default. Note that this currently the
+ ref change emails are always sent in plain text.
+
+ Note that when using "html", the formatting is done by parsing the
+ output of ``git log`` with ``-p``. When using
+ ``multimailhook.commitLogOpts`` to specify a ``--format`` for
+ ``git log``, one may get false positive (e.g. lines in the body of
+ the message starting with ``+++`` or ``---`` colored in red or
+ green).
+
multimailhook.refchangeShowGraph
If this option is set to true, then summary emails about reference
@@ -305,7 +349,7 @@ multimailhook.mailer
* multimailhook.smtpEncryption
- Set the security type. Allowed values: none, ssl.
+ Set the security type. Allowed values: none, ssl, tls.
Default=none.
* multimailhook.smtpServerDebugLevel
@@ -313,9 +357,26 @@ multimailhook.mailer
Integer number. Set to greater than 0 to activate debugging.
multimailhook.from
+multimailhook.fromCommit
+multimailhook.fromRefchange
+
+ If set, use this value in the From: field of generated emails.
+ ``fromCommit`` is used for commit emails, ``fromRefchange`` is
+ used for refchange emails, and ``from`` is used as fall-back in
+ all cases.
+
+ The value for these variables can be either:
+
+ - An email address, which will be used directly.
+
+ - The value ``pusher``, in which case the pusher's address (if
+ available) will be used.
- If set, use this value in the From: field of generated emails. If
- unset, the value of the From: header is determined as follows:
+ - The value ``author`` (meaningful only for replyToCommit), in which
+ case the commit author's address will be used.
+
+ If config values are unset, the value of the From: header is
+ determined as follows:
1. (gitolite environment only) Parse gitolite.conf, looking for a
block of comments that looks like this::
@@ -425,6 +486,15 @@ multimailhook.commitLogOpts
--stat -p --cc``. Shell quoting is allowed; see
multimailhook.logOpts for details.
+multimailhook.dateSubstitute
+
+ String to use as a substitute for ``Date:`` in the output of ``git
+ log`` while formatting commit messages. This is usefull to avoid
+ emitting a line that can be interpreted by mailers as the start of
+ a cited message (Zimbra webmail in particular). Defaults to
+ ``CommitDate: ``. Set to an empty string or ``none`` to deactivate
+ the behavior.
+
multimailhook.emailDomain
Domain name appended to the username of the person doing the push
@@ -440,21 +510,13 @@ multimailhook.replyToRefchange
Addresses to use in the Reply-To: field for commit emails
(replyToCommit) and refchange emails (replyToRefchange).
multimailhook.replyTo is used as default when replyToCommit or
- replyToRefchange is not set. The value for these variables can be
- either:
-
- - An email address, which will be used directly.
-
- - The value `pusher`, in which case the pusher's address (if
- available) will be used. This is the default for refchange
- emails.
+ replyToRefchange is not set. The shortcuts ``pusher`` and
+ ``author`` are allowed with the same semantics as for
+ ``multimailhook.from``. In addition, the value ``none`` can be
+ used to omit the ``Reply-To:`` field.
- - The value `author` (meaningful only for replyToCommit), in which
- case the commit author's address will be used. This is the
- default for commit emails.
-
- - The value `none`, in which case the Reply-To: field will be
- omitted.
+ The default is ``pusher`` for refchange emails, and ``author`` for
+ commit emails.
multimailhook.quiet
@@ -478,6 +540,63 @@ multimailhook.combineWhenSingleCommit
single email.
Default: true
+multimailhook.refFilterInclusionRegex
+multimailhook.refFilterExclusionRegex
+multimailhook.refFilterDoSendRegex
+multimailhook.refFilterDontSendRegex
+
+ **Warning:** these options are experimental. They should work, but
+ the user-interface is not stable yet (in particular, the option
+ names may change). If you want to participate in stabilizing the
+ feature, please contact the maintainers and/or send pull-requests.
+
+ Regular expressions that can be used to limit refs for which email
+ updates will be sent. It is an error to specify both an inclusion
+ and an exclusion regex. If a ``refFilterInclusionRegex`` is
+ specified, emails will only be sent for refs which match this
+ regex. If a ``refFilterExclusionRegex`` regex is specified,
+ emails will be sent for all refs except those that match this
+ regex (or that match a predefined regex specific to the
+ environment, such as "^refs/notes" for most environments and
+ "^refs/notes|^refs/changes" for the gerrit environment).
+
+ The expressions are matched against the complete refname, and is
+ considered to match if any substring matches. For example, to
+ filter-out all tags, set ``refFilterExclusionRegex`` to
+ ``^refs/tags/`` (note the leading ``^`` but no trailing ``$``). If
+ you set ``refFilterExclusionRegex`` to ``master``, then any ref
+ containing ``master`` will be excluded (the ``master`` branch, but
+ also ``refs/tags/master`` or ``refs/heads/foo-master-bar``).
+
+ ``refFilterDoSendRegex`` and ``refFilterDontSendRegex`` are
+ analogous to ``refFilterInclusionRegex`` and
+ ``refFilterExclusionRegex`` with one difference: with
+ ``refFilterDoSendRegex`` and ``refFilterDontSendRegex``, commits
+ introduced by one excluded ref will not be considered as new when
+ they reach an included ref. Typically, if you add a branch ``foo``
+ to ``refFilterDontSendRegex``, push commits to this branch, and
+ later merge branch ``foo`` into ``master``, then the notification
+ email for ``master`` will contain a commit email only for the
+ merge commit. If you include ``foo`` in
+ ``refFilterExclusionRegex``, then at the time of merge, you will
+ receive one commit email per commit in the branch.
+
+ These variables can be multi-valued, like::
+
+ [multimailhook]
+ refFilterExclusionRegex = ^refs/tags/
+ refFilterExclusionRegex = ^refs/heads/master$
+
+ You can also provide a whitespace-separated list like::
+
+ [multimailhook]
+ refFilterExclusionRegex = ^refs/tags/ ^refs/heads/master$
+
+ Both examples exclude tags and the master branch, and are
+ equivalent to::
+
+ [multimailhook]
+ refFilterExclusionRegex = ^refs/tags/|^refs/heads/master$
Email filtering aids
--------------------
@@ -547,35 +666,8 @@ consider sharing them with the community!
Getting involved
----------------
-git-multimail is an open-source project, built by volunteers. We would
-welcome your help!
-
-The current maintainers are Michael Haggerty <mhagger@alum.mit.edu>
-and Matthieu Moy <matthieu.moy@grenoble-inp.fr>.
-
-Please note that although a copy of git-multimail is distributed in
-the "contrib" section of the main Git project, development takes place
-in a separate git-multimail repository on GitHub:
-
- https://github.com/git-multimail/git-multimail
-
-Whenever enough changes to git-multimail have accumulated, a new
-code-drop of git-multimail will be submitted for inclusion in the Git
-project.
-
-We use the GitHub issue tracker to keep track of bugs and feature
-requests, and we use GitHub pull requests to exchange patches (though,
-if you prefer, you can send patches via the Git mailing list with CC
-to the maintainers). Please sign off your patches as per the Git
-project practice.
-
-General discussion of git-multimail can take place on the main Git
-mailing list,
-
- git@vger.kernel.org
-
-Please CC emails regarding git-multimail to the maintainers so that we
-don't overlook them.
+Please, read `<CONTRIBUTING.rst>`__ for instructions on how to
+contribute to git-multimail.
Footnotes
diff --git a/contrib/hooks/multimail/README.Git b/contrib/hooks/multimail/README.Git
index f5d59a8d31..300a2a4d2d 100644
--- a/contrib/hooks/multimail/README.Git
+++ b/contrib/hooks/multimail/README.Git
@@ -6,10 +6,10 @@ website:
https://github.com/git-multimail/git-multimail
The version in this directory was obtained from the upstream project
-on July 03 2015 and consists of the "git-multimail" subdirectory from
+on October 11 2015 and consists of the "git-multimail" subdirectory from
revision
- 6d6c9eb62a054143322cfaecde3949189c065b46 refs/tags/1.1.1
+ c0791b9ef5821a746fc3475c25765e640452eaae refs/tags/1.2.0
Please see the README file in this directory for information about how
to report bugs or contribute to git-multimail.
diff --git a/contrib/hooks/multimail/doc/gerrit.rst b/contrib/hooks/multimail/doc/gerrit.rst
new file mode 100644
index 0000000000..8011d05dec
--- /dev/null
+++ b/contrib/hooks/multimail/doc/gerrit.rst
@@ -0,0 +1,56 @@
+Setting up git-multimail on Gerrit
+==================================
+
+Gerrit has its own email-sending system, but you may prefer using
+``git-multimail`` instead. It supports Gerrit natively as a Gerrit
+``ref-updated`` hook (Warning: `Gerrit hooks
+<https://gerrit-review.googlesource.com/Documentation/config-hooks.html>`__
+are distinct from Git hooks). Setting up ``git-multimail`` on a Gerrit
+installation can be done following the instructions below.
+
+The explanations show an easy way to set up ``git-multimail``,
+but leave ``git-multimail`` installed and unconfigured for a while. If
+you run Gerrit on a production server, it is advised that you
+execute the step "Set up the hook" last to avoid confusing your users
+in the meantime.
+
+Set up the hook
+---------------
+
+Create a directory ``$site_path/hooks/`` if it does not exist (if you
+don't know what ``$site_path`` is, run ``gerrit.sh status`` and look
+for a ``GERRIT_SITE`` line). Either copy ``git_multimail.py`` to
+``$site_path/hooks/ref-updated`` or create a wrapper script like
+this::
+
+ #! /bin/sh
+ exec /path/to/git_multimail.py "$@"
+
+In both cases, make sure the file is named exactly
+``$site_path/hooks/ref-updated`` and is executable.
+
+(Alternatively, you may configure the ``[hooks]`` section of
+gerrit.config)
+
+Configuration
+-------------
+
+Log on the gerrit server and edit ``$site_path/git/$project/config``
+to configure ``git-multimail``.
+
+Troubleshooting
+---------------
+
+Warning: this will disable ``git-multimail`` during the debug, and
+could confuse your users. Don't run on a production server.
+
+To debug configuration issues with ``git-multimail``, you can add the
+``--stdout`` option when calling ``git_multimail.py`` like this::
+
+ #!/bin/sh
+ exec /path/to/git-multimail/git-multimail/git_multimail.py \
+ --stdout "$@" >> /tmp/log.txt
+
+and try pushing from a test repository. You should see the source of
+the email that would have been sent in the output of ``git push`` in
+the file ``/tmp/log.txt``.
diff --git a/contrib/hooks/multimail/doc/gitolite.rst b/contrib/hooks/multimail/doc/gitolite.rst
new file mode 100644
index 0000000000..00aedd9c57
--- /dev/null
+++ b/contrib/hooks/multimail/doc/gitolite.rst
@@ -0,0 +1,109 @@
+Setting up git-multimail on gitolite
+====================================
+
+``git-multimail`` supports gitolite 3 natively.
+The explanations below show an easy way to set up ``git-multimail``,
+but leave ``git-multimail`` installed and unconfigured for a while. If
+you run gitolite on a production server, it is advised that you
+execute the step "Set up the hook" last to avoid confusing your users
+in the meantime.
+
+Set up the hook
+---------------
+
+Log in as your gitolite user.
+
+Create a file ``.gitolite/hooks/common/post-receive`` on your gitolite
+account containing (adapt the path, obviously)::
+
+ #!/bin/sh
+ exec /path/to/git-multimail/git-multimail/git_multimail.py "$@"
+
+Make sure it's executable (``chmod +x``). Record the hook in
+gitolite::
+
+ gitolite setup
+
+Configuration
+-------------
+
+First, you have to allow the admin to set Git configuration variables.
+
+As gitolite user, edit the line containing ``GIT_CONFIG_KEYS`` in file
+``.gitolite.rc``, to make it look like::
+
+ GIT_CONFIG_KEYS => 'multimailhook\..*',
+
+You can now log out and return to your normal user.
+
+In the ``gitolite-admin`` clone, edit the file ``conf/gitolite.conf``
+and add::
+
+ repo @all
+ # Not strictly needed as git_multimail.py will chose gitolite if
+ # $GL_USER is set.
+ config multimailhook.environment = gitolite
+ config multimailhook.mailingList = # Where emails should be sent
+ config multimailhook.from = # From address to use
+
+Obviously, you can customize all parameters on a per-repository basis by
+adding these ``config multimailhook.*`` lines in the section
+corresponding to a repository or set of repositories.
+
+To activate ``git-multimail`` on a per-repository basis, do not set
+``multimailhook.mailingList`` in the ``@all`` section and set it only
+for repositories for which you want ``git-multimail``.
+
+Alternatively, you can set up the ``From:`` field on a per-user basis
+by adding a ``BEGIN USER EMAILS``/``END USER EMAILS`` section (see
+``../README``).
+
+Specificities of Gitolite for Configuration
+-------------------------------------------
+
+Empty configuration variables
+.............................
+
+With gitolite, the syntax ``config multimailhook.commitList = ""``
+unsets the variable instead of setting it to an empty string (see
+`here
+<http://gitolite.com/gitolite/git-config.html#an-important-warning-about-deleting-a-config-line>`__).
+As a result, there is no way to set a variable to the empty string.
+In all most places where an empty value is required, git-multimail
+now allows to specify special ``"none"`` value (case-sensitive) to
+mean the same.
+
+Alternatively, one can use ``" "`` (a single space) instead of ``""``.
+In most cases (in particular ``multimailhook.*List`` variables), this
+will be equivalent to an empty string.
+
+If you have a use-case where ``"none"`` is not an acceptable value and
+you need ``" "`` or ``""`` instead, please report it as a bug to
+git-multimail.
+
+Allowing Regular Expressions in Configuration
+.............................................
+
+gitolite has a mechanism to prevent unsafe configuration variable
+values, which prevent characters like ``|`` commonly used in regular
+expressions. If you do not need the safety feature of gitolite and
+need to use regular expressions in your configuration (e.g. for
+``multimailhook.refFilter*`` variables), set
+`UNSAFE_PATT
+<http://gitolite.com/gitolite/git-config.html#unsafe-patt>`__ to a
+less restrictive value.
+
+Troubleshooting
+---------------
+
+Warning: this will disable ``git-multimail`` during the debug, and
+could confuse your users. Don't run on a production server.
+
+To debug configuration issues with ``git-multimail``, you can add the
+``--stdout`` option when calling ``git_multimail.py`` like this::
+
+ #!/bin/sh
+ exec /path/to/git-multimail/git-multimail/git_multimail.py --stdout "$@"
+
+and try pushing from a test repository. You should see the source of
+the email that would have been sent in the output of ``git push``.
diff --git a/contrib/hooks/multimail/git_multimail.py b/contrib/hooks/multimail/git_multimail.py
index c06ce7a515..0180dba431 100755
--- a/contrib/hooks/multimail/git_multimail.py
+++ b/contrib/hooks/multimail/git_multimail.py
@@ -1,4 +1,6 @@
-#! /usr/bin/env python2
+#! /usr/bin/env python
+
+__version__ = '1.2.0'
# Copyright (c) 2015 Matthieu Moy and others
# Copyright (c) 2012-2014 Michael Haggerty and others
@@ -56,8 +58,54 @@ import shlex
import optparse
import smtplib
import time
+import cgi
+
+PYTHON3 = sys.version_info >= (3, 0)
+
+if sys.version_info <= (2, 5):
+ def all(iterable):
+ for element in iterable:
+ if not element:
+ return False
+ return True
+
+
+def is_ascii(s):
+ return all(ord(c) < 128 and ord(c) > 0 for c in s)
+
+
+if PYTHON3:
+ def str_to_bytes(s):
+ return s.encode(ENCODING)
+
+ def bytes_to_str(s):
+ return s.decode(ENCODING)
+
+ unicode = str
+
+ def write_str(f, msg):
+ # Try outputing with the default encoding. If it fails,
+ # try UTF-8.
+ try:
+ f.buffer.write(msg.encode(sys.getdefaultencoding()))
+ except UnicodeEncodeError:
+ f.buffer.write(msg.encode(ENCODING))
+else:
+ def str_to_bytes(s):
+ return s
+
+ def bytes_to_str(s):
+ return s
+
+ def write_str(f, msg):
+ f.write(msg)
+
+ def next(it):
+ return it.next()
+
try:
+ from email.charset import Charset
from email.utils import make_msgid
from email.utils import getaddresses
from email.utils import formataddr
@@ -65,6 +113,7 @@ try:
from email.header import Header
except ImportError:
# Prior to Python 2.5, the email module used different names:
+ from email.Charset import Charset
from email.Utils import make_msgid
from email.Utils import getaddresses
from email.Utils import formataddr
@@ -109,7 +158,7 @@ Date: %(send_date)s
To: %(recipients)s
Subject: %(subject)s
MIME-Version: 1.0
-Content-Type: text/plain; charset=%(charset)s
+Content-Type: text/%(contenttype)s; charset=%(charset)s
Content-Transfer-Encoding: 8bit
Message-ID: %(msgid)s
From: %(fromaddr)s
@@ -120,6 +169,8 @@ X-Git-Refname: %(refname)s
X-Git-Reftype: %(refname_type)s
X-Git-Oldrev: %(oldrev)s
X-Git-Newrev: %(newrev)s
+X-Git-NotificationType: ref_changed
+X-Git-Multimail-Version: %(multimail_version)s
Auto-Submitted: auto-generated
"""
@@ -238,7 +289,7 @@ To: %(recipients)s
Cc: %(cc_recipients)s
Subject: %(emailprefix)s%(num)02d/%(tot)02d: %(oneline)s
MIME-Version: 1.0
-Content-Type: text/plain; charset=%(charset)s
+Content-Type: text/%(contenttype)s; charset=%(charset)s
Content-Transfer-Encoding: 8bit
From: %(fromaddr)s
Reply-To: %(reply_to)s
@@ -249,6 +300,8 @@ X-Git-Repo: %(repo_shortname)s
X-Git-Refname: %(refname)s
X-Git-Reftype: %(refname_type)s
X-Git-Rev: %(rev)s
+X-Git-NotificationType: diff
+X-Git-Multimail-Version: %(multimail_version)s
Auto-Submitted: auto-generated
"""
@@ -270,7 +323,7 @@ Date: %(send_date)s
To: %(recipients)s
Subject: %(subject)s
MIME-Version: 1.0
-Content-Type: text/plain; charset=%(charset)s
+Content-Type: text/%(contenttype)s; charset=%(charset)s
Content-Transfer-Encoding: 8bit
Message-ID: %(msgid)s
From: %(fromaddr)s
@@ -282,6 +335,8 @@ X-Git-Reftype: %(refname_type)s
X-Git-Oldrev: %(oldrev)s
X-Git-Newrev: %(newrev)s
X-Git-Rev: %(rev)s
+X-Git-NotificationType: ref_changed_plus_diff
+X-Git-Multimail-Version: %(multimail_version)s
Auto-Submitted: auto-generated
"""
@@ -352,12 +407,14 @@ def read_git_output(args, input=None, keepends=False, **kw):
def read_output(cmd, input=None, keepends=False, **kw):
if input:
stdin = subprocess.PIPE
+ input = str_to_bytes(input)
else:
stdin = None
p = subprocess.Popen(
cmd, stdin=stdin, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kw
)
(out, err) = p.communicate(input)
+ out = bytes_to_str(out)
retcode = p.wait()
if retcode:
raise CommandError(cmd, retcode)
@@ -418,26 +475,37 @@ def git_log(spec, **kw):
def header_encode(text, header_name=None):
"""Encode and line-wrap the value of an email header field."""
- try:
- if isinstance(text, str):
- text = text.decode(ENCODING, 'replace')
- return Header(text, header_name=header_name).encode()
- except UnicodeEncodeError:
- return Header(text, header_name=header_name, charset=CHARSET,
- errors='replace').encode()
+ # Convert to unicode, if required.
+ if not isinstance(text, unicode):
+ text = unicode(text, 'utf-8')
+
+ if is_ascii(text):
+ charset = 'ascii'
+ else:
+ charset = 'utf-8'
+
+ return Header(text, header_name=header_name, charset=Charset(charset)).encode()
def addr_header_encode(text, header_name=None):
"""Encode and line-wrap the value of an email header field containing
email addresses."""
- return Header(
- ', '.join(
- formataddr((header_encode(name), emailaddr))
- for name, emailaddr in getaddresses([text])
- ),
- header_name=header_name
- ).encode()
+ # Convert to unicode, if required.
+ if not isinstance(text, unicode):
+ text = unicode(text, 'utf-8')
+
+ text = ', '.join(
+ formataddr((header_encode(name), emailaddr))
+ for name, emailaddr in getaddresses([text])
+ )
+
+ if is_ascii(text):
+ charset = 'ascii'
+ else:
+ charset = 'utf-8'
+
+ return Header(text, header_name=header_name, charset=Charset(charset)).encode()
class Config(object):
@@ -496,7 +564,8 @@ class Config(object):
['config', '--get-all', '--null', '%s.%s' % (self.section, name)],
env=self.env, keepends=True,
))
- except CommandError, e:
+ except CommandError:
+ t, e, traceback = sys.exc_info()
if e.retcode == 1:
# "the section or key is invalid"; i.e., there is no
# value for the specified key.
@@ -504,18 +573,6 @@ class Config(object):
else:
raise
- def get_recipients(self, name, default=None):
- """Read a recipients list from the configuration.
-
- Return the result as a comma-separated list of email
- addresses, or default if the option is unset. If the setting
- has multiple values, concatenate them with comma separators."""
-
- lines = self.get_all(name, default=None)
- if lines is None:
- return default
- return ', '.join(line.strip() for line in lines)
-
def set(self, name, value):
read_git_output(
['config', '%s.%s' % (self.section, name), value],
@@ -542,7 +599,8 @@ class Config(object):
['config', '--unset-all', '%s.%s' % (self.section, name)],
env=self.env,
)
- except CommandError, e:
+ except CommandError:
+ t, e, traceback = sys.exc_info()
if e.retcode == 5:
# The name doesn't exist, which is what we wanted anyway...
pass
@@ -636,7 +694,7 @@ class GitObject(object):
if not self.sha1:
raise ValueError('Empty commit has no summary')
- return iter(generate_summaries('--no-walk', self.sha1)).next()
+ return next(iter(generate_summaries('--no-walk', self.sha1)))
def __eq__(self, other):
return isinstance(other, GitObject) and self.sha1 == other.sha1
@@ -647,6 +705,10 @@ class GitObject(object):
def __nonzero__(self):
return bool(self.sha1)
+ def __bool__(self):
+ """Python 2 backward compatibility"""
+ return self.__nonzero__()
+
def __str__(self):
return self.sha1 or ZEROS
@@ -661,6 +723,12 @@ class Change(object):
def __init__(self, environment):
self.environment = environment
self._values = None
+ self._contains_html_diff = False
+
+ def _contains_diff(self):
+ # We do contain a diff, should it be rendered in HTML?
+ if self.environment.commit_email_format == "html":
+ self._contains_html_diff = True
def _compute_values(self):
"""Return a dictionary {keyword: expansion} for this Change.
@@ -670,7 +738,12 @@ class Change(object):
get_values(). The return value should always be a new
dictionary."""
- return self.environment.get_values()
+ values = self.environment.get_values()
+ fromaddr = self.environment.get_fromaddr(change=self)
+ if fromaddr is not None:
+ values['fromaddr'] = fromaddr
+ values['multimail_version'] = get_version()
+ return values
def get_values(self, **extra_values):
"""Return a dictionary {keyword: expansion} for this Change.
@@ -713,12 +786,18 @@ class Change(object):
skip lines that contain references to unknown variables."""
values = self.get_values(**extra_values)
+ if self._contains_html_diff:
+ values['contenttype'] = 'html'
+ else:
+ values['contenttype'] = 'plain'
+
for line in template.splitlines():
- (name, value) = line.split(':', 1)
+ (name, value) = line.split(': ', 1)
try:
value = value % values
- except KeyError, e:
+ except KeyError:
+ t, e, traceback = sys.exc_info()
if DEBUG:
self.environment.log_warning(
'Warning: unknown variable %r in the following line; line skipped:\n'
@@ -764,6 +843,24 @@ class Change(object):
raise NotImplementedError()
+ def _wrap_for_html(self, lines):
+ """Wrap the lines in HTML <pre> tag when using HTML format.
+
+ Escape special HTML characters and add <pre> and </pre> tags around
+ the given lines if we should be generating HTML as indicated by
+ self._contains_html_diff being set to true.
+ """
+ if self._contains_html_diff:
+ yield "<pre style='margin:0'>\n"
+
+ for line in lines:
+ yield cgi.escape(line)
+
+ yield '</pre>\n'
+ else:
+ for line in lines:
+ yield line
+
def generate_email(self, push, body_filter=None, extra_header_values={}):
"""Generate an email describing this change.
@@ -779,18 +876,76 @@ class Change(object):
for line in self.generate_email_header(**extra_header_values):
yield line
yield '\n'
- for line in self.generate_email_intro():
+ for line in self._wrap_for_html(self.generate_email_intro()):
yield line
body = self.generate_email_body(push)
if body_filter is not None:
body = body_filter(body)
+
+ diff_started = False
+ if self._contains_html_diff:
+ # "white-space: pre" is the default, but we need to
+ # specify it again in case the message is viewed in a
+ # webmail which wraps it in an element setting white-space
+ # to something else (Zimbra does this and sets
+ # white-space: pre-line).
+ yield '<pre style="white-space: pre; background: #F8F8F8">'
for line in body:
+ if self._contains_html_diff:
+ # This is very, very naive. It would be much better to really
+ # parse the diff, i.e. look at how many lines do we have in
+ # the hunk headers instead of blindly highlighting everything
+ # that looks like it might be part of a diff.
+ bgcolor = ''
+ fgcolor = ''
+ if line.startswith('--- a/'):
+ diff_started = True
+ bgcolor = 'e0e0ff'
+ elif line.startswith('diff ') or line.startswith('index '):
+ diff_started = True
+ fgcolor = '808080'
+ elif diff_started:
+ if line.startswith('+++ '):
+ bgcolor = 'e0e0ff'
+ elif line.startswith('@@'):
+ bgcolor = 'e0e0e0'
+ elif line.startswith('+'):
+ bgcolor = 'e0ffe0'
+ elif line.startswith('-'):
+ bgcolor = 'ffe0e0'
+ elif line.startswith('commit '):
+ fgcolor = '808000'
+ elif line.startswith(' '):
+ fgcolor = '404040'
+
+ # Chop the trailing LF, we don't want it inside <pre>.
+ line = cgi.escape(line[:-1])
+
+ if bgcolor or fgcolor:
+ style = 'display:block; white-space:pre;'
+ if bgcolor:
+ style += 'background:#' + bgcolor + ';'
+ if fgcolor:
+ style += 'color:#' + fgcolor + ';'
+ # Use a <span style='display:block> to color the
+ # whole line. The newline must be inside the span
+ # to display properly both in Firefox and in
+ # text-based browser.
+ line = "<span style='%s'>%s\n</span>" % (style, line)
+ else:
+ line = line + '\n'
+
yield line
+ if self._contains_html_diff:
+ yield '</pre>'
- for line in self.generate_email_footer():
+ for line in self._wrap_for_html(self.generate_email_footer()):
yield line
+ def get_alt_fromaddr(self):
+ return None
+
class Revision(Change):
"""A Change consisting of a single git commit."""
@@ -867,14 +1022,25 @@ class Revision(Change):
def generate_email_body(self, push):
"""Show this revision."""
- return read_git_lines(
- ['log'] + self.environment.commitlogopts + ['-1', self.rev.sha1],
- keepends=True,
- )
+ for line in read_git_lines(
+ ['log'] + self.environment.commitlogopts + ['-1', self.rev.sha1],
+ keepends=True,
+ ):
+ if line.startswith('Date: ') and self.environment.date_substitute:
+ yield self.environment.date_substitute + line[len('Date: '):]
+ else:
+ yield line
def generate_email_footer(self):
return self.expand_lines(REVISION_FOOTER_TEMPLATE)
+ def generate_email(self, push, body_filter=None, extra_header_values={}):
+ self._contains_diff()
+ return Change.generate_email(self, push, body_filter, extra_header_values)
+
+ def get_alt_fromaddr(self):
+ return self.environment.from_commit
+
class ReferenceChange(Change):
"""A Change to a Git reference.
@@ -1096,10 +1262,10 @@ class ReferenceChange(Change):
yield '\n'
yield 'Detailed log of new commits:\n\n'
for line in read_git_lines(
- ['log', '--no-walk']
- + self.logopts
- + new_commits_list
- + ['--'],
+ ['log', '--no-walk'] +
+ self.logopts +
+ new_commits_list +
+ ['--'],
keepends=True,
):
yield line
@@ -1253,9 +1419,9 @@ class ReferenceChange(Change):
yield '\n'
yield 'Summary of changes:\n'
for line in read_git_lines(
- ['diff-tree']
- + self.diffopts
- + ['%s..%s' % (self.old.commit_sha1, self.new.commit_sha1,)],
+ ['diff-tree'] +
+ self.diffopts +
+ ['%s..%s' % (self.old.commit_sha1, self.new.commit_sha1,)],
keepends=True,
):
yield line
@@ -1316,6 +1482,9 @@ class ReferenceChange(Change):
)
yield '\n'
+ def get_alt_fromaddr(self):
+ return self.environment.from_refchange
+
class BranchChange(ReferenceChange):
refname_type = 'branch'
@@ -1397,9 +1566,9 @@ class BranchChange(ReferenceChange):
# commit is a non-merge commit, though it may make sense to
# combine if it is a merge as well.
if not (
- len(new_commits) == 1
- and len(new_commits[0][1]) == 1
- and new_commits[0][0] in known_added_sha1s
+ len(new_commits) == 1 and
+ len(new_commits[0][1]) == 1 and
+ new_commits[0][0] in known_added_sha1s
):
return None
@@ -1432,6 +1601,7 @@ class BranchChange(ReferenceChange):
values['subject'] = self.expand(COMBINED_REFCHANGE_REVISION_SUBJECT_TEMPLATE, **values)
self._single_revision = revision
+ self._contains_diff()
self.header_template = COMBINED_HEADER_TEMPLATE
self.intro_template = COMBINED_INTRO_TEMPLATE
self.footer_template = COMBINED_FOOTER_TEMPLATE
@@ -1690,17 +1860,18 @@ class SendMailer(Mailer):
def send(self, lines, to_addrs):
try:
p = subprocess.Popen(self.command, stdin=subprocess.PIPE)
- except OSError, e:
+ except OSError:
sys.stderr.write(
- '*** Cannot execute command: %s\n' % ' '.join(self.command)
- + '*** %s\n' % str(e)
- + '*** Try setting multimailhook.mailer to "smtp"\n'
+ '*** Cannot execute command: %s\n' % ' '.join(self.command) +
+ '*** %s\n' % sys.exc_info()[1] +
+ '*** Try setting multimailhook.mailer to "smtp"\n' +
'*** to send emails without using the sendmail command.\n'
)
sys.exit(1)
try:
+ lines = (str_to_bytes(line) for line in lines)
p.stdin.writelines(lines)
- except Exception, e:
+ except Exception:
sys.stderr.write(
'*** Error while generating commit email\n'
'*** - mail sending aborted.\n'
@@ -1710,7 +1881,7 @@ class SendMailer(Mailer):
p.terminate()
except AttributeError:
pass
- raise e
+ raise
else:
p.stdin.close()
retcode = p.wait()
@@ -1770,11 +1941,11 @@ class SMTPMailer(Mailer):
"*** Setting debug on for SMTP server connection (%s) ***\n"
% self.smtpserverdebuglevel)
self.smtp.set_debuglevel(self.smtpserverdebuglevel)
- except Exception, e:
+ except Exception:
sys.stderr.write(
'*** Error establishing SMTP connection to %s ***\n'
% self.smtpserver)
- sys.stderr.write('*** %s\n' % str(e))
+ sys.stderr.write('*** %s\n' % sys.exc_info()[1])
sys.exit(1)
def __del__(self):
@@ -1784,16 +1955,15 @@ class SMTPMailer(Mailer):
def send(self, lines, to_addrs):
try:
if self.username or self.password:
- sys.stderr.write("*** Authenticating as %s ***\n" % self.username)
self.smtp.login(self.username, self.password)
msg = ''.join(lines)
# turn comma-separated list into Python list if needed.
if isinstance(to_addrs, basestring):
to_addrs = [email for (name, email) in getaddresses([to_addrs])]
self.smtp.sendmail(self.envelopesender, to_addrs, msg)
- except Exception, e:
+ except Exception:
sys.stderr.write('*** Error sending email ***\n')
- sys.stderr.write('*** %s\n' % str(e))
+ sys.stderr.write('*** %s\n' % sys.exc_info()[1])
self.smtp.quit()
sys.exit(1)
@@ -1809,9 +1979,10 @@ class OutputMailer(Mailer):
self.f = f
def send(self, lines, to_addrs):
- self.f.write(self.SEPARATOR)
- self.f.writelines(lines)
- self.f.write(self.SEPARATOR)
+ write_str(self.f, self.SEPARATOR)
+ for line in lines:
+ write_str(self.f, line)
+ write_str(self.f, self.SEPARATOR)
def get_git_dir():
@@ -1877,11 +2048,13 @@ class Environment(object):
Return the address to be used as the 'From' email address
in the email envelope.
- get_fromaddr()
+ get_fromaddr(change=None)
Return the 'From' email address used in the email 'From:'
- headers. (May be a full RFC 2822 email address like 'Joe
- User <user@example.com>'.)
+ headers. If the change is known when this function is
+ called, it is passed in as the 'change' parameter. (May
+ be a full RFC 2822 email address like 'Joe User
+ <user@example.com>'.)
get_administrator()
@@ -1901,12 +2074,29 @@ class Environment(object):
get_reply_to_commit() is used for individual commit
emails.
+ get_ref_filter_regex()
+
+ Return a tuple -- a compiled regex, and a boolean indicating
+ whether the regex picks refs to include (if False, the regex
+ matches on refs to exclude).
+
+ get_default_ref_ignore_regex()
+
+ Return a regex that should be ignored for both what emails
+ to send and when computing what commits are considered new
+ to the repository. Default is "^refs/notes/".
+
They should also define the following attributes:
announce_show_shortlog (bool)
True iff announce emails should include a shortlog.
+ commit_email_format (string)
+
+ If "html", generate commit emails in HTML instead of plain text
+ used by default.
+
refchange_showgraph (bool)
True iff refchanges emails should include a detailed graph.
@@ -1939,6 +2129,11 @@ class Environment(object):
commit mail. The value should be a list of strings
representing words to be passed to the command.
+ date_substitute (string)
+
+ String to be used in substitution for 'Date:' at start of
+ line in the output of 'git log'.
+
quiet (bool)
On success do not write to stderr
@@ -1950,6 +2145,13 @@ class Environment(object):
True if a combined email should be produced when a single
new commit is pushed to a branch, False otherwise.
+ from_refchange, from_commit (strings)
+
+ Addresses to use for the From: field for refchange emails
+ and commit emails respectively. Set from
+ multimailhook.fromRefchange and multimailhook.fromCommit
+ by ConfigEnvironmentMixin.
+
"""
REPO_NAME_RE = re.compile(r'^(?P<name>.+?)(?:\.git)$')
@@ -1957,6 +2159,7 @@ class Environment(object):
def __init__(self, osenv=None):
self.osenv = osenv or os.environ
self.announce_show_shortlog = False
+ self.commit_email_format = "text"
self.maxcommitemails = 500
self.diffopts = ['--stat', '--summary', '--find-copies-harder']
self.graphopts = ['--oneline', '--decorate']
@@ -1964,6 +2167,7 @@ class Environment(object):
self.refchange_showgraph = False
self.refchange_showlog = False
self.commitlogopts = ['-C', '--stat', '-p', '--cc']
+ self.date_substitute = 'AuthorDate: '
self.quiet = False
self.stdout = False
self.combine_when_single_commit = True
@@ -1972,7 +2176,6 @@ class Environment(object):
'administrator',
'charset',
'emailprefix',
- 'fromaddr',
'pusher',
'pusher_email',
'repo_path',
@@ -1998,7 +2201,7 @@ class Environment(object):
def get_pusher_email(self):
return None
- def get_fromaddr(self):
+ def get_fromaddr(self, change=None):
config = Config('user')
fromname = config.get('name', default='')
fromemail = config.get('email', default='')
@@ -2080,6 +2283,15 @@ class Environment(object):
def get_reply_to_commit(self, revision):
return revision.author
+ def get_default_ref_ignore_regex(self):
+ # The commit messages of git notes are essentially meaningless
+ # and "filenames" in git notes commits are an implementational
+ # detail that might surprise users at first. As such, we
+ # would need a completely different method for handling emails
+ # of git notes in order for them to be of benefit for users,
+ # which we simply do not have right now.
+ return "^refs/notes/"
+
def filter_body(self, lines):
"""Filter the lines intended for an email body.
@@ -2095,19 +2307,19 @@ class Environment(object):
"""Write the string msg on a log file or on stderr.
Sends the text to stderr by default, override to change the behavior."""
- sys.stderr.write(msg)
+ write_str(sys.stderr, msg)
def log_warning(self, msg):
"""Write the string msg on a log file or on stderr.
Sends the text to stderr by default, override to change the behavior."""
- sys.stderr.write(msg)
+ write_str(sys.stderr, msg)
def log_error(self, msg):
"""Write the string msg on a log file or on stderr.
Sends the text to stderr by default, override to change the behavior."""
- sys.stderr.write(msg)
+ write_str(sys.stderr, msg)
class ConfigEnvironmentMixin(Environment):
@@ -2128,6 +2340,14 @@ class ConfigEnvironmentMixin(Environment):
class ConfigOptionsEnvironmentMixin(ConfigEnvironmentMixin):
"""An Environment that reads most of its information from "git config"."""
+ @staticmethod
+ def forbid_field_values(name, value, forbidden):
+ for forbidden_val in forbidden:
+ if value is not None and value.lower() == forbidden:
+ raise ConfigurationException(
+ '"%s" is not an allowed setting for %s' % (value, name)
+ )
+
def __init__(self, config, **kw):
super(ConfigOptionsEnvironmentMixin, self).__init__(
config=config, **kw
@@ -2144,14 +2364,26 @@ class ConfigOptionsEnvironmentMixin(ConfigEnvironmentMixin):
if val is not None:
setattr(self, var, val)
+ commit_email_format = config.get('commitEmailFormat')
+ if commit_email_format is not None:
+ if commit_email_format != "html" and commit_email_format != "text":
+ self.log_warning(
+ '*** Unknown value for multimailhook.commitEmailFormat: %s\n' %
+ commit_email_format +
+ '*** Expected either "text" or "html". Ignoring.\n'
+ )
+ else:
+ self.commit_email_format = commit_email_format
+
maxcommitemails = config.get('maxcommitemails')
if maxcommitemails is not None:
try:
self.maxcommitemails = int(maxcommitemails)
except ValueError:
self.log_warning(
- '*** Malformed value for multimailhook.maxCommitEmails: %s\n' % maxcommitemails
- + '*** Expected a number. Ignoring.\n'
+ '*** Malformed value for multimailhook.maxCommitEmails: %s\n'
+ % maxcommitemails +
+ '*** Expected a number. Ignoring.\n'
)
diffopts = config.get('diffopts')
@@ -2170,32 +2402,44 @@ class ConfigOptionsEnvironmentMixin(ConfigEnvironmentMixin):
if commitlogopts is not None:
self.commitlogopts = shlex.split(commitlogopts)
+ date_substitute = config.get('dateSubstitute')
+ if date_substitute == 'none':
+ self.date_substitute = None
+ elif date_substitute is not None:
+ self.date_substitute = date_substitute
+
reply_to = config.get('replyTo')
self.__reply_to_refchange = config.get('replyToRefchange', default=reply_to)
- if (
- self.__reply_to_refchange is not None
- and self.__reply_to_refchange.lower() == 'author'
- ):
- raise ConfigurationException(
- '"author" is not an allowed setting for replyToRefchange'
- )
+ self.forbid_field_values('replyToRefchange',
+ self.__reply_to_refchange,
+ ['author'])
self.__reply_to_commit = config.get('replyToCommit', default=reply_to)
+ from_addr = self.config.get('from')
+ self.from_refchange = config.get('fromRefchange')
+ self.forbid_field_values('fromRefchange',
+ self.from_refchange,
+ ['author', 'none'])
+ self.from_commit = config.get('fromCommit')
+ self.forbid_field_values('fromCommit',
+ self.from_commit,
+ ['none'])
+
combine = config.get_bool('combineWhenSingleCommit')
if combine is not None:
self.combine_when_single_commit = combine
def get_administrator(self):
return (
- self.config.get('administrator')
- or self.get_sender()
- or super(ConfigOptionsEnvironmentMixin, self).get_administrator()
+ self.config.get('administrator') or
+ self.get_sender() or
+ super(ConfigOptionsEnvironmentMixin, self).get_administrator()
)
def get_repo_shortname(self):
return (
- self.config.get('reponame')
- or super(ConfigOptionsEnvironmentMixin, self).get_repo_shortname()
+ self.config.get('reponame') or
+ super(ConfigOptionsEnvironmentMixin, self).get_repo_shortname()
)
def get_emailprefix(self):
@@ -2212,33 +2456,42 @@ class ConfigOptionsEnvironmentMixin(ConfigEnvironmentMixin):
def get_sender(self):
return self.config.get('envelopesender')
- def get_fromaddr(self):
+ def process_addr(self, addr, change):
+ if addr.lower() == 'author':
+ if hasattr(change, 'author'):
+ return change.author
+ else:
+ return None
+ elif addr.lower() == 'pusher':
+ return self.get_pusher_email()
+ elif addr.lower() == 'none':
+ return None
+ else:
+ return addr
+
+ def get_fromaddr(self, change=None):
fromaddr = self.config.get('from')
+ if change:
+ alt_fromaddr = change.get_alt_fromaddr()
+ if alt_fromaddr:
+ fromaddr = alt_fromaddr
+ if fromaddr:
+ fromaddr = self.process_addr(fromaddr, change)
if fromaddr:
return fromaddr
- return super(ConfigOptionsEnvironmentMixin, self).get_fromaddr()
+ return super(ConfigOptionsEnvironmentMixin, self).get_fromaddr(change)
def get_reply_to_refchange(self, refchange):
if self.__reply_to_refchange is None:
return super(ConfigOptionsEnvironmentMixin, self).get_reply_to_refchange(refchange)
- elif self.__reply_to_refchange.lower() == 'pusher':
- return self.get_pusher_email()
- elif self.__reply_to_refchange.lower() == 'none':
- return None
else:
- return self.__reply_to_refchange
+ return self.process_addr(self.__reply_to_refchange, refchange)
def get_reply_to_commit(self, revision):
if self.__reply_to_commit is None:
return super(ConfigOptionsEnvironmentMixin, self).get_reply_to_commit(revision)
- elif self.__reply_to_commit.lower() == 'author':
- return revision.author
- elif self.__reply_to_commit.lower() == 'pusher':
- return self.get_pusher_email()
- elif self.__reply_to_commit.lower() == 'none':
- return None
else:
- return self.__reply_to_commit
+ return self.process_addr(self.__reply_to_commit, revision)
def get_scancommitforcc(self):
return self.config.get('scancommitforcc')
@@ -2270,12 +2523,14 @@ class FilterLinesEnvironmentMixin(Environment):
def filter_body(self, lines):
lines = super(FilterLinesEnvironmentMixin, self).filter_body(lines)
if self.__strict_utf8:
- lines = (line.decode(ENCODING, 'replace') for line in lines)
+ if not PYTHON3:
+ lines = (line.decode(ENCODING, 'replace') for line in lines)
# Limit the line length in Unicode-space to avoid
# splitting characters:
if self.__emailmaxlinelength:
lines = limit_linelength(lines, self.__emailmaxlinelength)
- lines = (line.encode(ENCODING, 'replace') for line in lines)
+ if not PYTHON3:
+ lines = (line.encode(ENCODING, 'replace') for line in lines)
elif self.__emailmaxlinelength:
lines = limit_linelength(lines, self.__emailmaxlinelength)
@@ -2404,10 +2659,10 @@ class StaticRecipientsEnvironmentMixin(Environment):
# actual *contents* of the change being reported, we only
# choose based on the *type* of the change. Therefore we can
# compute them once and for all:
- if not (refchange_recipients
- or announce_recipients
- or revision_recipients
- or scancommitforcc):
+ if not (refchange_recipients or
+ announce_recipients or
+ revision_recipients or
+ scancommitforcc):
raise ConfigurationException('No email recipients configured!')
self.__refchange_recipients = refchange_recipients
self.__announce_recipients = announce_recipients
@@ -2457,13 +2712,104 @@ class ConfigRecipientsEnvironmentMixin(
found, raise a ConfigurationException."""
for name in names:
- retval = config.get_recipients(name)
- if retval is not None:
- return retval
+ lines = config.get_all(name)
+ if lines is not None:
+ lines = [line.strip() for line in lines]
+ # Single "none" is a special value equivalen to empty string.
+ if lines == ['none']:
+ lines = ['']
+ return ', '.join(lines)
else:
return ''
+class StaticRefFilterEnvironmentMixin(Environment):
+ """Set branch filter statically based on constructor parameters."""
+
+ def __init__(self, ref_filter_incl_regex, ref_filter_excl_regex,
+ ref_filter_do_send_regex, ref_filter_dont_send_regex,
+ **kw):
+ super(StaticRefFilterEnvironmentMixin, self).__init__(**kw)
+
+ if ref_filter_incl_regex and ref_filter_excl_regex:
+ raise ConfigurationException(
+ "Cannot specify both a ref inclusion and exclusion regex.")
+ self.__is_inclusion_filter = bool(ref_filter_incl_regex)
+ default_exclude = self.get_default_ref_ignore_regex()
+ if ref_filter_incl_regex:
+ ref_filter_regex = ref_filter_incl_regex
+ elif ref_filter_excl_regex:
+ ref_filter_regex = ref_filter_excl_regex + '|' + default_exclude
+ else:
+ ref_filter_regex = default_exclude
+ try:
+ self.__compiled_regex = re.compile(ref_filter_regex)
+ except Exception:
+ raise ConfigurationException(
+ 'Invalid Ref Filter Regex "%s": %s' % (ref_filter_regex, sys.exc_info()[1]))
+
+ if ref_filter_do_send_regex and ref_filter_dont_send_regex:
+ raise ConfigurationException(
+ "Cannot specify both a ref doSend and dontSend regex.")
+ if ref_filter_do_send_regex or ref_filter_dont_send_regex:
+ self.__is_do_send_filter = bool(ref_filter_do_send_regex)
+ if ref_filter_incl_regex:
+ ref_filter_send_regex = ref_filter_incl_regex
+ elif ref_filter_excl_regex:
+ ref_filter_send_regex = ref_filter_excl_regex
+ else:
+ ref_filter_send_regex = '.*'
+ self.__is_do_send_filter = True
+ try:
+ self.__send_compiled_regex = re.compile(ref_filter_send_regex)
+ except Exception:
+ raise ConfigurationException(
+ 'Invalid Ref Filter Regex "%s": %s' %
+ (ref_filter_send_regex, sys.exc_info()[1]))
+ else:
+ self.__send_compiled_regex = self.__compiled_regex
+ self.__is_do_send_filter = self.__is_inclusion_filter
+
+ def get_ref_filter_regex(self, send_filter=False):
+ if send_filter:
+ return self.__send_compiled_regex, self.__is_do_send_filter
+ else:
+ return self.__compiled_regex, self.__is_inclusion_filter
+
+
+class ConfigRefFilterEnvironmentMixin(
+ ConfigEnvironmentMixin,
+ StaticRefFilterEnvironmentMixin
+ ):
+ """Determine branch filtering statically based on config."""
+
+ def _get_regex(self, config, key):
+ """Get a list of whitespace-separated regex. The refFilter* config
+ variables are multivalued (hence the use of get_all), and we
+ allow each entry to be a whitespace-separated list (hence the
+ split on each line). The whole thing is glued into a single regex."""
+ values = config.get_all(key)
+ if values is None:
+ return values
+ items = []
+ for line in values:
+ for i in line.split():
+ items.append(i)
+ if items == []:
+ return None
+ return '|'.join(items)
+
+ def __init__(self, config, **kw):
+ super(ConfigRefFilterEnvironmentMixin, self).__init__(
+ config=config,
+ ref_filter_incl_regex=self._get_regex(config, 'refFilterInclusionRegex'),
+ ref_filter_excl_regex=self._get_regex(config, 'refFilterExclusionRegex'),
+ ref_filter_do_send_regex=self._get_regex(config, 'refFilterDoSendRegex'),
+ ref_filter_dont_send_regex=self._get_regex(config, 'refFilterDontSendRegex'),
+ **kw
+ )
+
+
class ProjectdescEnvironmentMixin(Environment):
"""Make a "projectdesc" value available for templates.
@@ -2499,6 +2845,7 @@ class GenericEnvironment(
ComputeFQDNEnvironmentMixin,
ConfigFilterLinesEnvironmentMixin,
ConfigRecipientsEnvironmentMixin,
+ ConfigRefFilterEnvironmentMixin,
PusherDomainEnvironmentMixin,
ConfigOptionsEnvironmentMixin,
GenericEnvironmentMixin,
@@ -2513,14 +2860,14 @@ class GitoliteEnvironmentMixin(Environment):
# repo_shortname (though it's probably not as good as a value
# the user might have explicitly put in his config).
return (
- self.osenv.get('GL_REPO', None)
- or super(GitoliteEnvironmentMixin, self).get_repo_shortname()
+ self.osenv.get('GL_REPO', None) or
+ super(GitoliteEnvironmentMixin, self).get_repo_shortname()
)
def get_pusher(self):
return self.osenv.get('GL_USER', 'unknown user')
- def get_fromaddr(self):
+ def get_fromaddr(self, change=None):
GL_USER = self.osenv.get('GL_USER')
if GL_USER is not None:
# Find the path to gitolite.conf. Note that gitolite v3
@@ -2536,9 +2883,9 @@ class GitoliteEnvironmentMixin(Environment):
f = open(GL_CONF, 'rU')
try:
in_user_emails_section = False
- re_template = r'^\s*#\s*{}\s*$'
+ re_template = r'^\s*#\s*%s\s*$'
re_begin, re_user, re_end = (
- re.compile(re_template.format(x))
+ re.compile(re_template % x)
for x in (
r'BEGIN\s+USER\s+EMAILS',
re.escape(GL_USER) + r'\s+(.*)',
@@ -2557,7 +2904,7 @@ class GitoliteEnvironmentMixin(Environment):
return m.group(1)
finally:
f.close()
- return super(GitoliteEnvironmentMixin, self).get_fromaddr()
+ return super(GitoliteEnvironmentMixin, self).get_fromaddr(change)
class IncrementalDateTime(object):
@@ -2570,8 +2917,9 @@ class IncrementalDateTime(object):
def __init__(self):
self.time = time.time()
+ self.next = self.__next__ # Python 2 backward compatibility
- def next(self):
+ def __next__(self):
formatted = formatdate(self.time, True)
self.time += 1
return formatted
@@ -2583,6 +2931,7 @@ class GitoliteEnvironment(
ComputeFQDNEnvironmentMixin,
ConfigFilterLinesEnvironmentMixin,
ConfigRecipientsEnvironmentMixin,
+ ConfigRefFilterEnvironmentMixin,
PusherDomainEnvironmentMixin,
ConfigOptionsEnvironmentMixin,
GitoliteEnvironmentMixin,
@@ -2591,6 +2940,117 @@ class GitoliteEnvironment(
pass
+class StashEnvironmentMixin(Environment):
+ def __init__(self, user=None, repo=None, **kw):
+ super(StashEnvironmentMixin, self).__init__(**kw)
+ self.__user = user
+ self.__repo = repo
+
+ def get_repo_shortname(self):
+ return self.__repo
+
+ def get_pusher(self):
+ return re.match('(.*?)\s*<', self.__user).group(1)
+
+ def get_pusher_email(self):
+ return self.__user
+
+ def get_fromaddr(self, change=None):
+ return self.__user
+
+
+class StashEnvironment(
+ StashEnvironmentMixin,
+ ProjectdescEnvironmentMixin,
+ ConfigMaxlinesEnvironmentMixin,
+ ComputeFQDNEnvironmentMixin,
+ ConfigFilterLinesEnvironmentMixin,
+ ConfigRecipientsEnvironmentMixin,
+ ConfigRefFilterEnvironmentMixin,
+ PusherDomainEnvironmentMixin,
+ ConfigOptionsEnvironmentMixin,
+ Environment,
+ ):
+ pass
+
+
+class GerritEnvironmentMixin(Environment):
+ def __init__(self, project=None, submitter=None, update_method=None, **kw):
+ super(GerritEnvironmentMixin, self).__init__(**kw)
+ self.__project = project
+ self.__submitter = submitter
+ self.__update_method = update_method
+ "Make an 'update_method' value available for templates."
+ self.COMPUTED_KEYS += ['update_method']
+
+ def get_repo_shortname(self):
+ return self.__project
+
+ def get_pusher(self):
+ if self.__submitter:
+ if self.__submitter.find('<') != -1:
+ # Submitter has a configured email, we transformed
+ # __submitter into an RFC 2822 string already.
+ return re.match('(.*?)\s*<', self.__submitter).group(1)
+ else:
+ # Submitter has no configured email, it's just his name.
+ return self.__submitter
+ else:
+ # If we arrive here, this means someone pushed "Submit" from
+ # the gerrit web UI for the CR (or used one of the programmatic
+ # APIs to do the same, such as gerrit review) and the
+ # merge/push was done by the Gerrit user. It was technically
+ # triggered by someone else, but sadly we have no way of
+ # determining who that someone else is at this point.
+ return 'Gerrit' # 'unknown user'?
+
+ def get_pusher_email(self):
+ if self.__submitter:
+ return self.__submitter
+ else:
+ return super(GerritEnvironmentMixin, self).get_pusher_email()
+
+ def get_fromaddr(self, change=None):
+ if self.__submitter and self.__submitter.find('<') != -1:
+ return self.__submitter
+ else:
+ return super(GerritEnvironmentMixin, self).get_fromaddr(change)
+
+ def get_default_ref_ignore_regex(self):
+ default = super(GerritEnvironmentMixin, self).get_default_ref_ignore_regex()
+ return default + '|^refs/changes/|^refs/cache-automerge/|^refs/meta/'
+
+ def get_revision_recipients(self, revision):
+ # Merge commits created by Gerrit when users hit "Submit this patchset"
+ # in the Web UI (or do equivalently with REST APIs or the gerrit review
+ # command) are not something users want to see an individual email for.
+ # Filter them out.
+ committer = read_git_output(['log', '--no-walk', '--format=%cN',
+ revision.rev.sha1])
+ if committer == 'Gerrit Code Review':
+ return []
+ else:
+ return super(GerritEnvironmentMixin, self).get_revision_recipients(revision)
+
+ def get_update_method(self):
+ return self.__update_method
+
+
+class GerritEnvironment(
+ GerritEnvironmentMixin,
+ ProjectdescEnvironmentMixin,
+ ConfigMaxlinesEnvironmentMixin,
+ ComputeFQDNEnvironmentMixin,
+ ConfigFilterLinesEnvironmentMixin,
+ ConfigRecipientsEnvironmentMixin,
+ ConfigRefFilterEnvironmentMixin,
+ PusherDomainEnvironmentMixin,
+ ConfigOptionsEnvironmentMixin,
+ Environment,
+ ):
+ pass
+
+
class Push(object):
"""Represent an entire push (i.e., a group of ReferenceChanges).
@@ -2673,10 +3133,11 @@ class Push(object):
])
)
- def __init__(self, changes, ignore_other_refs=False):
+ def __init__(self, environment, changes, ignore_other_refs=False):
self.changes = sorted(changes, key=self._sort_key)
self.__other_ref_sha1s = None
self.__cached_commits_spec = {}
+ self.environment = environment
if ignore_other_refs:
self.__other_ref_sha1s = set()
@@ -2703,10 +3164,14 @@ class Push(object):
'%(objectname) %(objecttype) %(refname)\n'
'%(*objectname) %(*objecttype) %(refname)'
)
+ ref_filter_regex, is_inclusion_filter = \
+ self.environment.get_ref_filter_regex()
for line in read_git_lines(
['for-each-ref', '--format=%s' % (fmt,)]):
(sha1, type, name) = line.split(' ', 2)
- if sha1 and type == 'commit' and name not in updated_refs:
+ if (sha1 and type == 'commit' and
+ name not in updated_refs and
+ include_ref(name, ref_filter_regex, is_inclusion_filter)):
sha1s.add(sha1)
self.__other_ref_sha1s = sha1s
@@ -2856,7 +3321,7 @@ class Push(object):
if not change.environment.quiet:
change.environment.log_msg(
'Sending notification emails to: %s\n' % (change.recipients,))
- extra_values = {'send_date': send_date.next()}
+ extra_values = {'send_date': next(send_date)}
rev = change.send_single_combined_email(sha1s)
if rev:
@@ -2876,9 +3341,9 @@ class Push(object):
max_emails = change.environment.maxcommitemails
if max_emails and len(sha1s) > max_emails:
change.environment.log_warning(
- '*** Too many new commits (%d), not sending commit emails.\n' % len(sha1s)
- + '*** Try setting multimailhook.maxCommitEmails to a greater value\n'
- + '*** Currently, multimailhook.maxCommitEmails=%d\n' % max_emails
+ '*** Too many new commits (%d), not sending commit emails.\n' % len(sha1s) +
+ '*** Try setting multimailhook.maxCommitEmails to a greater value\n' +
+ '*** Currently, multimailhook.maxCommitEmails=%d\n' % max_emails
)
return
@@ -2889,7 +3354,7 @@ class Push(object):
rev.recipients = rev.cc_recipients
rev.cc_recipients = None
if rev.recipients:
- extra_values = {'send_date': send_date.next()}
+ extra_values = {'send_date': next(send_date)}
mailer.send(
rev.generate_email(self, body_filter, extra_values),
rev.recipients,
@@ -2904,18 +3369,33 @@ class Push(object):
)
+def include_ref(refname, ref_filter_regex, is_inclusion_filter):
+ does_match = bool(ref_filter_regex.search(refname))
+ if is_inclusion_filter:
+ return does_match
+ else: # exclusion filter -- we include the ref if the regex doesn't match
+ return not does_match
+
+
def run_as_post_receive_hook(environment, mailer):
+ ref_filter_regex, is_inclusion_filter = environment.get_ref_filter_regex(True)
changes = []
for line in sys.stdin:
(oldrev, newrev, refname) = line.strip().split(' ', 2)
+ if not include_ref(refname, ref_filter_regex, is_inclusion_filter):
+ continue
changes.append(
ReferenceChange.create(environment, oldrev, newrev, refname)
)
- push = Push(changes)
- push.send_emails(mailer, body_filter=environment.filter_body)
+ if changes:
+ push = Push(environment, changes)
+ push.send_emails(mailer, body_filter=environment.filter_body)
def run_as_update_hook(environment, mailer, refname, oldrev, newrev, force_send=False):
+ ref_filter_regex, is_inclusion_filter = environment.get_ref_filter_regex(True)
+ if not include_ref(refname, ref_filter_regex, is_inclusion_filter):
+ return
changes = [
ReferenceChange.create(
environment,
@@ -2924,7 +3404,7 @@ def run_as_update_hook(environment, mailer, refname, oldrev, newrev, force_send=
refname,
),
]
- push = Push(changes, force_send)
+ push = Push(environment, changes, force_send)
push.send_emails(mailer, body_filter=environment.filter_body)
@@ -2953,8 +3433,8 @@ def choose_mailer(config, environment):
mailer = SendMailer(command=command, envelopesender=environment.get_sender())
else:
environment.log_error(
- 'fatal: multimailhook.mailer is set to an incorrect value: "%s"\n' % mailer
- + 'please use one of "smtp" or "sendmail".\n'
+ 'fatal: multimailhook.mailer is set to an incorrect value: "%s"\n' % mailer +
+ 'please use one of "smtp" or "sendmail".\n'
)
sys.exit(1)
return mailer
@@ -2963,14 +3443,18 @@ def choose_mailer(config, environment):
KNOWN_ENVIRONMENTS = {
'generic': GenericEnvironmentMixin,
'gitolite': GitoliteEnvironmentMixin,
+ 'stash': StashEnvironmentMixin,
+ 'gerrit': GerritEnvironmentMixin,
}
-def choose_environment(config, osenv=None, env=None, recipients=None):
+def choose_environment(config, osenv=None, env=None, recipients=None,
+ hook_info=None):
if not osenv:
osenv = os.environ
environment_mixins = [
+ ConfigRefFilterEnvironmentMixin,
ProjectdescEnvironmentMixin,
ConfigMaxlinesEnvironmentMixin,
ComputeFQDNEnvironmentMixin,
@@ -2992,7 +3476,15 @@ def choose_environment(config, osenv=None, env=None, recipients=None):
else:
env = 'generic'
- environment_mixins.append(KNOWN_ENVIRONMENTS[env])
+ environment_mixins.insert(0, KNOWN_ENVIRONMENTS[env])
+
+ if env == 'stash':
+ environment_kw['user'] = hook_info['stash_user']
+ environment_kw['repo'] = hook_info['stash_repo']
+ elif env == 'gerrit':
+ environment_kw['project'] = hook_info['project']
+ environment_kw['submitter'] = hook_info['submitter']
+ environment_kw['update_method'] = hook_info['update_method']
if recipients:
environment_mixins.insert(0, StaticRecipientsEnvironmentMixin)
@@ -3011,6 +3503,116 @@ def choose_environment(config, osenv=None, env=None, recipients=None):
return environment_klass(**environment_kw)
+def get_version():
+ oldcwd = os.getcwd()
+ try:
+ try:
+ os.chdir(os.path.dirname(os.path.realpath(__file__)))
+ git_version = read_git_output(['describe', '--tags', 'HEAD'])
+ if git_version == __version__:
+ return git_version
+ else:
+ return '%s (%s)' % (__version__, git_version)
+ except:
+ pass
+ finally:
+ os.chdir(oldcwd)
+ return __version__
+
+
+def compute_gerrit_options(options, args, required_gerrit_options):
+ if None in required_gerrit_options:
+ raise SystemExit("Error: Specify all of --oldrev, --newrev, --refname, "
+ "and --project; or none of them.")
+
+ if options.environment not in (None, 'gerrit'):
+ raise SystemExit("Non-gerrit environments incompatible with --oldrev, "
+ "--newrev, --refname, and --project")
+ options.environment = 'gerrit'
+
+ if args:
+ raise SystemExit("Error: Positional parameters not allowed with "
+ "--oldrev, --newrev, and --refname.")
+
+ # Gerrit oddly omits 'refs/heads/' in the refname when calling
+ # ref-updated hook; put it back.
+ git_dir = get_git_dir()
+ if (not os.path.exists(os.path.join(git_dir, options.refname)) and
+ os.path.exists(os.path.join(git_dir, 'refs', 'heads',
+ options.refname))):
+ options.refname = 'refs/heads/' + options.refname
+
+ # Convert each string option unicode for Python3.
+ if PYTHON3:
+ opts = ['environment', 'recipients', 'oldrev', 'newrev', 'refname',
+ 'project', 'submitter', 'stash-user', 'stash-repo']
+ for opt in opts:
+ if not hasattr(options, opt):
+ continue
+ obj = getattr(options, opt)
+ if obj:
+ enc = obj.encode('utf-8', 'surrogateescape')
+ dec = enc.decode('utf-8', 'replace')
+ setattr(options, opt, dec)
+
+ # New revisions can appear in a gerrit repository either due to someone
+ # pushing directly (in which case options.submitter will be set), or they
+ # can press "Submit this patchset" in the web UI for some CR (in which
+ # case options.submitter will not be set and gerrit will not have provided
+ # us the information about who pressed the button).
+ #
+ # Note for the nit-picky: I'm lumping in REST API calls and the ssh
+ # gerrit review command in with "Submit this patchset" button, since they
+ # have the same effect.
+ if options.submitter:
+ update_method = 'pushed'
+ # The submitter argument is almost an RFC 2822 email address; change it
+ # from 'User Name (email@domain)' to 'User Name <email@domain>' so it is
+ options.submitter = options.submitter.replace('(', '<').replace(')', '>')
+ else:
+ update_method = 'submitted'
+ # Gerrit knew who submitted this patchset, but threw that information
+ # away when it invoked this hook. However, *IF* Gerrit created a
+ # merge to bring the patchset in (project 'Submit Type' is either
+ # "Always Merge", or is "Merge if Necessary" and happens to be
+ # necessary for this particular CR), then it will have the committer
+ # of that merge be 'Gerrit Code Review' and the author will be the
+ # person who requested the submission of the CR. Since this is fairly
+ # likely for most gerrit installations (of a reasonable size), it's
+ # worth the extra effort to try to determine the actual submitter.
+ rev_info = read_git_lines(['log', '--no-walk', '--merges',
+ '--format=%cN%n%aN <%aE>', options.newrev])
+ if rev_info and rev_info[0] == 'Gerrit Code Review':
+ options.submitter = rev_info[1]
+
+ # We pass back refname, oldrev, newrev as args because then the
+ # gerrit ref-updated hook is much like the git update hook
+ return (options,
+ [options.refname, options.oldrev, options.newrev],
+ {'project': options.project, 'submitter': options.submitter,
+ 'update_method': update_method})
+
+
+def check_hook_specific_args(options, args):
+ # First check for stash arguments
+ if (options.stash_user is None) != (options.stash_repo is None):
+ raise SystemExit("Error: Specify both of --stash-user and "
+ "--stash-repo or neither.")
+ if options.stash_user:
+ options.environment = 'stash'
+ return options, args, {'stash_user': options.stash_user,
+ 'stash_repo': options.stash_repo}
+
+ # Finally, check for gerrit specific arguments
+ required_gerrit_options = (options.oldrev, options.newrev, options.refname,
+ options.project)
+ if required_gerrit_options != (None,) * 4:
+ return compute_gerrit_options(options, args, required_gerrit_options)
+
+ # No special options in use, just return what we started with
+ return options, args, {}
+
+
def main(args):
parser = optparse.OptionParser(
description=__doc__,
@@ -3019,7 +3621,7 @@ def main(args):
parser.add_option(
'--environment', '--env', action='store', type='choice',
- choices=['generic', 'gitolite'], default=None,
+ choices=list(KNOWN_ENVIRONMENTS.keys()), default=None,
help=(
'Choose type of environment is in use. Default is taken from '
'multimailhook.environment if set; otherwise "generic".'
@@ -3048,8 +3650,58 @@ def main(args):
'detection in this mode.'
),
)
+ parser.add_option(
+ '-c', metavar="<name>=<value>", action='append',
+ help=(
+ 'Pass a configuration parameter through to git. The value given '
+ 'will override values from configuration files. See the -c option '
+ 'of git(1) for more details. (Only works with git >= 1.7.3)'
+ ),
+ )
+ parser.add_option(
+ '--version', '-v', action='store_true', default=False,
+ help=(
+ "Display git-multimail's version"
+ ),
+ )
+ # The following options permit this script to be run as a gerrit
+ # ref-updated hook. See e.g.
+ # code.google.com/p/gerrit/source/browse/Documentation/config-hooks.txt
+ # We suppress help for these items, since these are specific to gerrit,
+ # and we don't want users directly using them any way other than how the
+ # gerrit ref-updated hook is called.
+ parser.add_option('--oldrev', action='store', help=optparse.SUPPRESS_HELP)
+ parser.add_option('--newrev', action='store', help=optparse.SUPPRESS_HELP)
+ parser.add_option('--refname', action='store', help=optparse.SUPPRESS_HELP)
+ parser.add_option('--project', action='store', help=optparse.SUPPRESS_HELP)
+ parser.add_option('--submitter', action='store', help=optparse.SUPPRESS_HELP)
+
+ # The following allow this to be run as a stash asynchronous post-receive
+ # hook (almost identical to a git post-receive hook but triggered also for
+ # merges of pull requests from the UI). We suppress help for these items,
+ # since these are specific to stash.
+ parser.add_option('--stash-user', action='store', help=optparse.SUPPRESS_HELP)
+ parser.add_option('--stash-repo', action='store', help=optparse.SUPPRESS_HELP)
(options, args) = parser.parse_args(args)
+ (options, args, hook_info) = check_hook_specific_args(options, args)
+
+ if options.version:
+ sys.stdout.write('git-multimail version ' + get_version() + '\n')
+ return
+
+ if options.c:
+ parameters = os.environ.get('GIT_CONFIG_PARAMETERS', '')
+ if parameters:
+ parameters += ' '
+ # git expects GIT_CONFIG_PARAMETERS to be of the form
+ # "'name1=value1' 'name2=value2' 'name3=value3'"
+ # including everything inside the double quotes (but not the double
+ # quotes themselves). Spacing is critical. Also, if a value contains
+ # a literal single quote that quote must be represented using the
+ # four character sequence: '\''
+ parameters += ' '.join("'" + x.replace("'", "'\\''") + "'" for x in options.c)
+ os.environ['GIT_CONFIG_PARAMETERS'] = parameters
config = Config('multimailhook')
@@ -3058,6 +3710,7 @@ def main(args):
config, osenv=os.environ,
env=options.environment,
recipients=options.recipients,
+ hook_info=hook_info,
)
if options.show_env:
@@ -3080,9 +3733,20 @@ def main(args):
run_as_update_hook(environment, mailer, refname, oldrev, newrev, options.force_send)
else:
run_as_post_receive_hook(environment, mailer)
- except ConfigurationException, e:
- sys.exit(str(e))
-
+ except ConfigurationException:
+ sys.exit(sys.exc_info()[1])
+ except Exception:
+ t, e, tb = sys.exc_info()
+ import traceback
+ sys.stdout.write('\n')
+ sys.stdout.write('Exception \'' + t.__name__ +
+ '\' raised. Please report this as a bug to\n')
+ sys.stdout.write('https://github.com/git-multimail/git-multimail/issues\n')
+ sys.stdout.write('with the information below:\n\n')
+ sys.stdout.write('git-multimail version ' + get_version() + '\n')
+ sys.stdout.write('Python version ' + sys.version + '\n')
+ traceback.print_exc(file=sys.stdout)
+ sys.exit(1)
if __name__ == '__main__':
main(sys.argv[1:])
diff --git a/contrib/hooks/multimail/migrate-mailhook-config b/contrib/hooks/multimail/migrate-mailhook-config
index d0e9b39201..992657bbdc 100755
--- a/contrib/hooks/multimail/migrate-mailhook-config
+++ b/contrib/hooks/multimail/migrate-mailhook-config
@@ -1,4 +1,4 @@
-#! /usr/bin/env python2
+#! /usr/bin/env python
"""Migrate a post-receive-email configuration to be usable with git_multimail.py.
diff --git a/contrib/hooks/multimail/post-receive.example b/contrib/hooks/multimail/post-receive.example
index 43f7b6b635..9975df7107 100755
--- a/contrib/hooks/multimail/post-receive.example
+++ b/contrib/hooks/multimail/post-receive.example
@@ -1,4 +1,4 @@
-#! /usr/bin/env python2
+#! /usr/bin/env python
"""Example post-receive hook based on git-multimail.
@@ -42,7 +42,6 @@ import os
import git_multimail
-
# It is possible to modify the output templates here; e.g.:
#git_multimail.FOOTER_TEMPLATE = """\
@@ -61,8 +60,9 @@ config = git_multimail.Config('multimailhook')
try:
environment = git_multimail.GenericEnvironment(config=config)
#environment = git_multimail.GitoliteEnvironment(config=config)
-except git_multimail.ConfigurationException, e:
- sys.exit(str(e))
+except git_multimail.ConfigurationException:
+ sys.stderr.write('*** %s\n' % sys.exc_info()[1])
+ sys.exit(1)
# Choose the method of sending emails based on the git config:
diff --git a/contrib/subtree/git-subtree.sh b/contrib/subtree/git-subtree.sh
index 9f06571851..308b777b0a 100755
--- a/contrib/subtree/git-subtree.sh
+++ b/contrib/subtree/git-subtree.sh
@@ -648,7 +648,7 @@ cmd_split()
debug "Merging split branch into HEAD..."
latest_old=$(cache_get latest_old)
git merge -s ours \
- -m "$(rejoin_msg $dir $latest_old $latest_new)" \
+ -m "$(rejoin_msg "$dir" $latest_old $latest_new)" \
$latest_new >&2 || exit $?
fi
if [ -n "$branch" ]; then
@@ -735,7 +735,7 @@ cmd_push()
refspec=$2
echo "git push using: " $repository $refspec
localrev=$(git subtree split --prefix="$prefix") || die
- git push $repository $localrev:refs/heads/$refspec
+ git push "$repository" $localrev:refs/heads/$refspec
else
die "'$dir' must already exist. Try 'git subtree add'."
fi
diff --git a/contrib/subtree/t/t7900-subtree.sh b/contrib/subtree/t/t7900-subtree.sh
index 90519823be..dfbe443dea 100755
--- a/contrib/subtree/t/t7900-subtree.sh
+++ b/contrib/subtree/t/t7900-subtree.sh
@@ -1,6 +1,7 @@
#!/bin/sh
#
# Copyright (c) 2012 Avery Pennaraum
+# Copyright (c) 2015 Alexey Shumkin
#
test_description='Basic porcelain support for subtrees
@@ -32,25 +33,6 @@ check_equal()
fi
}
-fixnl()
-{
- t=""
- while read x; do
- t="$t$x "
- done
- echo $t
-}
-
-multiline()
-{
- while read x; do
- set -- $x
- for d in "$@"; do
- echo "$d"
- done
- done
-}
-
undo()
{
git reset --hard HEAD~
@@ -62,11 +44,11 @@ last_commit_message()
}
test_expect_success 'init subproj' '
- test_create_repo subproj
+ test_create_repo "sub proj"
'
# To the subproject!
-cd subproj
+cd ./"sub proj"
test_expect_success 'add sub1' '
create sub1 &&
@@ -106,39 +88,39 @@ test_expect_success 'add main4' '
'
test_expect_success 'fetch subproj history' '
- git fetch ./subproj sub1 &&
+ git fetch ./"sub proj" sub1 &&
git branch sub1 FETCH_HEAD
'
test_expect_success 'no subtree exists in main tree' '
- test_must_fail git subtree merge --prefix=subdir sub1
+ test_must_fail git subtree merge --prefix="sub dir" sub1
'
test_expect_success 'no pull from non-existant subtree' '
- test_must_fail git subtree pull --prefix=subdir ./subproj sub1
+ test_must_fail git subtree pull --prefix="sub dir" ./"sub proj" sub1
'
test_expect_success 'check if --message works for add' '
- git subtree add --prefix=subdir --message="Added subproject" sub1 &&
+ git subtree add --prefix="sub dir" --message="Added subproject" sub1 &&
check_equal ''"$(last_commit_message)"'' "Added subproject" &&
undo
'
test_expect_success 'check if --message works as -m and --prefix as -P' '
- git subtree add -P subdir -m "Added subproject using git subtree" sub1 &&
+ git subtree add -P "sub dir" -m "Added subproject using git subtree" sub1 &&
check_equal ''"$(last_commit_message)"'' "Added subproject using git subtree" &&
undo
'
test_expect_success 'check if --message works with squash too' '
- git subtree add -P subdir -m "Added subproject with squash" --squash sub1 &&
+ git subtree add -P "sub dir" -m "Added subproject with squash" --squash sub1 &&
check_equal ''"$(last_commit_message)"'' "Added subproject with squash" &&
undo
'
test_expect_success 'add subproj to mainline' '
- git subtree add --prefix=subdir/ FETCH_HEAD &&
- check_equal ''"$(last_commit_message)"'' "Add '"'subdir/'"' from commit '"'"'''"$(git rev-parse sub1)"'''"'"'"
+ git subtree add --prefix="sub dir"/ FETCH_HEAD &&
+ check_equal ''"$(last_commit_message)"'' "Add '"'sub dir/'"' from commit '"'"'''"$(git rev-parse sub1)"'''"'"'"
'
# this shouldn't actually do anything, since FETCH_HEAD is already a parent
@@ -147,7 +129,7 @@ test_expect_success 'merge fetched subproj' '
'
test_expect_success 'add main-sub5' '
- create subdir/main-sub5 &&
+ create "sub dir/main-sub5" &&
git commit -m "main-sub5"
'
@@ -157,29 +139,29 @@ test_expect_success 'add main6' '
'
test_expect_success 'add main-sub7' '
- create subdir/main-sub7 &&
+ create "sub dir/main-sub7" &&
git commit -m "main-sub7"
'
test_expect_success 'fetch new subproj history' '
- git fetch ./subproj sub2 &&
+ git fetch ./"sub proj" sub2 &&
git branch sub2 FETCH_HEAD
'
test_expect_success 'check if --message works for merge' '
- git subtree merge --prefix=subdir -m "Merged changes from subproject" sub2 &&
+ git subtree merge --prefix="sub dir" -m "Merged changes from subproject" sub2 &&
check_equal ''"$(last_commit_message)"'' "Merged changes from subproject" &&
undo
'
test_expect_success 'check if --message for merge works with squash too' '
- git subtree merge --prefix subdir -m "Merged changes from subproject using squash" --squash sub2 &&
+ git subtree merge --prefix "sub dir" -m "Merged changes from subproject using squash" --squash sub2 &&
check_equal ''"$(last_commit_message)"'' "Merged changes from subproject using squash" &&
undo
'
test_expect_success 'merge new subproj history into subdir' '
- git subtree merge --prefix=subdir FETCH_HEAD &&
+ git subtree merge --prefix="sub dir" FETCH_HEAD &&
git branch pre-split &&
check_equal ''"$(last_commit_message)"'' "Merge commit '"'"'"$(git rev-parse sub2)"'"'"' into mainline" &&
undo
@@ -208,53 +190,53 @@ test_expect_success 'Check that the <prefix> exists for a split' '
'
test_expect_success 'check if --message works for split+rejoin' '
- spl1=''"$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin)"'' &&
+ spl1=''"$(git subtree split --annotate='"'*'"' --prefix "sub dir" --onto FETCH_HEAD --message "Split & rejoin" --rejoin)"'' &&
git branch spl1 "$spl1" &&
check_equal ''"$(last_commit_message)"'' "Split & rejoin" &&
undo
'
test_expect_success 'check split with --branch' '
- spl1=$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin) &&
+ spl1=$(git subtree split --annotate='"'*'"' --prefix "sub dir" --onto FETCH_HEAD --message "Split & rejoin" --rejoin) &&
undo &&
- git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --branch splitbr1 &&
+ git subtree split --annotate='"'*'"' --prefix "sub dir" --onto FETCH_HEAD --branch splitbr1 &&
check_equal ''"$(git rev-parse splitbr1)"'' "$spl1"
'
test_expect_success 'check hash of split' '
- spl1=$(git subtree split --prefix subdir) &&
- git subtree split --prefix subdir --branch splitbr1test &&
+ spl1=$(git subtree split --prefix "sub dir") &&
+ git subtree split --prefix "sub dir" --branch splitbr1test &&
check_equal ''"$(git rev-parse splitbr1test)"'' "$spl1" &&
new_hash=$(git rev-parse splitbr1test~2) &&
check_equal ''"$new_hash"'' "$subdir_hash"
'
test_expect_success 'check split with --branch for an existing branch' '
- spl1=''"$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin)"'' &&
+ spl1=''"$(git subtree split --annotate='"'*'"' --prefix "sub dir" --onto FETCH_HEAD --message "Split & rejoin" --rejoin)"'' &&
undo &&
git branch splitbr2 sub1 &&
- git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --branch splitbr2 &&
+ git subtree split --annotate='"'*'"' --prefix "sub dir" --onto FETCH_HEAD --branch splitbr2 &&
check_equal ''"$(git rev-parse splitbr2)"'' "$spl1"
'
test_expect_success 'check split with --branch for an incompatible branch' '
- test_must_fail git subtree split --prefix subdir --onto FETCH_HEAD --branch subdir
+ test_must_fail git subtree split --prefix "sub dir" --onto FETCH_HEAD --branch subdir
'
test_expect_success 'check split+rejoin' '
- spl1=''"$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin)"'' &&
+ spl1=''"$(git subtree split --annotate='"'*'"' --prefix "sub dir" --onto FETCH_HEAD --message "Split & rejoin" --rejoin)"'' &&
undo &&
- git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --rejoin &&
- check_equal ''"$(last_commit_message)"'' "Split '"'"'subdir/'"'"' into commit '"'"'"$spl1"'"'"'"
+ git subtree split --annotate='"'*'"' --prefix "sub dir" --onto FETCH_HEAD --rejoin &&
+ check_equal ''"$(last_commit_message)"'' "Split '"'"'sub dir/'"'"' into commit '"'"'"$spl1"'"'"'"
'
test_expect_success 'add main-sub8' '
- create subdir/main-sub8 &&
+ create "sub dir/main-sub8" &&
git commit -m "main-sub8"
'
# To the subproject!
-cd ./subproj
+cd ./"sub proj"
test_expect_success 'merge split into subproj' '
git fetch .. spl1 &&
@@ -271,22 +253,22 @@ test_expect_success 'add sub9' '
cd ..
test_expect_success 'split for sub8' '
- split2=''"$(git subtree split --annotate='"'*'"' --prefix subdir/ --rejoin)"'' &&
+ split2=''"$(git subtree split --annotate='"'*'"' --prefix "sub dir/" --rejoin)"'' &&
git branch split2 "$split2"
'
test_expect_success 'add main-sub10' '
- create subdir/main-sub10 &&
+ create "sub dir/main-sub10" &&
git commit -m "main-sub10"
'
test_expect_success 'split for sub10' '
- spl3=''"$(git subtree split --annotate='"'*'"' --prefix subdir --rejoin)"'' &&
+ spl3=''"$(git subtree split --annotate='"'*'"' --prefix "sub dir" --rejoin)"'' &&
git branch spl3 "$spl3"
'
# To the subproject!
-cd ./subproj
+cd ./"sub proj"
test_expect_success 'merge split into subproj' '
git fetch .. spl3 &&
@@ -295,42 +277,64 @@ test_expect_success 'merge split into subproj' '
git branch subproj-merge-spl3
'
-chkm="main4 main6"
-chkms="main-sub10 main-sub5 main-sub7 main-sub8"
-chkms_sub=$(echo $chkms | multiline | sed 's,^,subdir/,' | fixnl)
-chks="sub1 sub2 sub3 sub9"
-chks_sub=$(echo $chks | multiline | sed 's,^,subdir/,' | fixnl)
+chkm="main4
+main6"
+chkms="main-sub10
+main-sub5
+main-sub7
+main-sub8"
+chkms_sub=$(cat <<TXT | sed 's,^,sub dir/,'
+$chkms
+TXT
+)
+chks="sub1
+sub2
+sub3
+sub9"
+chks_sub=$(cat <<TXT | sed 's,^,sub dir/,'
+$chks
+TXT
+)
test_expect_success 'make sure exactly the right set of files ends up in the subproj' '
- subfiles=''"$(git ls-files | fixnl)"'' &&
- check_equal "$subfiles" "$chkms $chks"
+ subfiles="$(git ls-files)" &&
+ check_equal "$subfiles" "$chkms
+$chks"
'
-
test_expect_success 'make sure the subproj history *only* contains commits that affect the subdir' '
- allchanges=''"$(git log --name-only --pretty=format:'"''"' | sort | fixnl)"'' &&
- check_equal "$allchanges" "$chkms $chks"
+ allchanges=''"$(git log --name-only --pretty=format:'"''"' | sort | sed "/^$/d")"'' &&
+ check_equal "$allchanges" "$chkms
+$chks"
'
# Back to mainline
cd ..
test_expect_success 'pull from subproj' '
- git fetch ./subproj subproj-merge-spl3 &&
+ git fetch ./"sub proj" subproj-merge-spl3 &&
git branch subproj-merge-spl3 FETCH_HEAD &&
- git subtree pull --prefix=subdir ./subproj subproj-merge-spl3
+ git subtree pull --prefix="sub dir" ./"sub proj" subproj-merge-spl3
'
test_expect_success 'make sure exactly the right set of files ends up in the mainline' '
- mainfiles=''"$(git ls-files | fixnl)"'' &&
- check_equal "$mainfiles" "$chkm $chkms_sub $chks_sub"
+ mainfiles=$(git ls-files) &&
+ check_equal "$mainfiles" "$chkm
+$chkms_sub
+$chks_sub"
'
test_expect_success 'make sure each filename changed exactly once in the entire history' '
# main-sub?? and /subdir/main-sub?? both change, because those are the
# changes that were split into their own history. And subdir/sub?? never
# change, since they were *only* changed in the subtree branch.
- allchanges=''"$(git log --name-only --pretty=format:'"''"' | sort | fixnl)"'' &&
- check_equal "$allchanges" ''"$(echo $chkms $chkm $chks $chkms_sub | multiline | sort | fixnl)"''
+ allchanges=''"$(git log --name-only --pretty=format:'"''"' | sort | sed "/^$/d")"'' &&
+ check_equal "$allchanges" ''"$(cat <<TXT | sort
+$chkms
+$chkm
+$chks
+$chkms_sub
+TXT
+)"''
'
test_expect_success 'make sure the --rejoin commits never make it into subproj' '
@@ -377,7 +381,7 @@ cd ../main
test_expect_success 'add sub as subdir in main' '
git fetch ../sub master &&
git branch sub2 FETCH_HEAD &&
- git subtree add --prefix subdir sub2
+ git subtree add --prefix "sub dir" sub2
'
cd ../sub
@@ -392,16 +396,16 @@ cd ../main
test_expect_success 'merge from sub' '
git fetch ../sub master &&
git branch sub3 FETCH_HEAD &&
- git subtree merge --prefix subdir sub3
+ git subtree merge --prefix "sub dir" sub3
'
test_expect_success 'add main-sub4' '
- create subdir/main-sub4 &&
+ create "sub dir/main-sub4" &&
git commit -m "main-sub4"
'
test_expect_success 'split for main-sub4 without --onto' '
- git subtree split --prefix subdir --branch mainsub4
+ git subtree split --prefix "sub dir" --branch mainsub4
'
# at this point, the new commit parent should be sub3 if it is not,
@@ -468,4 +472,50 @@ test_expect_success 'verify one file change per commit' '
))
'
+# test push
+
+cd ../..
+
+mkdir test-push
+
+cd test-push
+
+test_expect_success 'init main' '
+ test_create_repo main
+'
+
+test_expect_success 'init sub' '
+ test_create_repo "sub project"
+'
+
+cd ./"sub project"
+
+test_expect_success 'add subproject' '
+ create "sub project" &&
+ git commit -m "Sub project: 1" &&
+ git branch sub-branch-1
+'
+
+cd ../main
+
+test_expect_success 'make first commit and add subproject' '
+ create "main-1" &&
+ git commit -m "main: 1" &&
+ git subtree add "../sub project" --prefix "sub dir" --message "Added subproject" sub-branch-1 &&
+ check_equal "$(last_commit_message)" "Added subproject"
+'
+
+test_expect_success 'make second commit to a subproject file and push it into a sub project' '
+ create "sub dir/sub1" &&
+ git commit -m "Sub project: 2" &&
+ git subtree push "../sub project" --prefix "sub dir" sub-branch-1
+'
+
+cd ../"sub project"
+
+test_expect_success 'Test second commit is pushed' '
+ git checkout sub-branch-1 &&
+ check_equal "$(last_commit_message)" "Sub project: 2"
+'
+
test_done
diff --git a/convert.c b/convert.c
index f3bd3e93fb..814e814438 100644
--- a/convert.c
+++ b/convert.c
@@ -1289,7 +1289,8 @@ static struct stream_filter *ident_filter(const unsigned char *sha1)
{
struct ident_filter *ident = xmalloc(sizeof(*ident));
- sprintf(ident->ident, ": %s $", sha1_to_hex(sha1));
+ xsnprintf(ident->ident, sizeof(ident->ident),
+ ": %s $", sha1_to_hex(sha1));
strbuf_init(&ident->left, 0);
ident->filter.vtbl = &ident_vtbl;
ident->state = 0;
diff --git a/daemon.c b/daemon.c
index f9eb296888..56679a15fe 100644
--- a/daemon.c
+++ b/daemon.c
@@ -811,8 +811,6 @@ static char **cld_argv;
static void handle(int incoming, struct sockaddr *addr, socklen_t addrlen)
{
struct child_process cld = CHILD_PROCESS_INIT;
- char addrbuf[300] = "REMOTE_ADDR=", portbuf[300];
- char *env[] = { addrbuf, portbuf, NULL };
if (max_connections && live_children >= max_connections) {
kill_some_child();
@@ -826,27 +824,23 @@ static void handle(int incoming, struct sockaddr *addr, socklen_t addrlen)
}
if (addr->sa_family == AF_INET) {
+ char buf[128] = "";
struct sockaddr_in *sin_addr = (void *) addr;
- inet_ntop(addr->sa_family, &sin_addr->sin_addr, addrbuf + 12,
- sizeof(addrbuf) - 12);
- snprintf(portbuf, sizeof(portbuf), "REMOTE_PORT=%d",
- ntohs(sin_addr->sin_port));
+ inet_ntop(addr->sa_family, &sin_addr->sin_addr, buf, sizeof(buf));
+ argv_array_pushf(&cld.env_array, "REMOTE_ADDR=%s", buf);
+ argv_array_pushf(&cld.env_array, "REMOTE_PORT=%d",
+ ntohs(sin_addr->sin_port));
#ifndef NO_IPV6
} else if (addr->sa_family == AF_INET6) {
+ char buf[128] = "";
struct sockaddr_in6 *sin6_addr = (void *) addr;
-
- char *buf = addrbuf + 12;
- *buf++ = '['; *buf = '\0'; /* stpcpy() is cool */
- inet_ntop(AF_INET6, &sin6_addr->sin6_addr, buf,
- sizeof(addrbuf) - 13);
- strcat(buf, "]");
-
- snprintf(portbuf, sizeof(portbuf), "REMOTE_PORT=%d",
- ntohs(sin6_addr->sin6_port));
+ inet_ntop(AF_INET6, &sin6_addr->sin6_addr, buf, sizeof(buf));
+ argv_array_pushf(&cld.env_array, "REMOTE_ADDR=[%s]", buf);
+ argv_array_pushf(&cld.env_array, "REMOTE_PORT=%d",
+ ntohs(sin6_addr->sin6_port));
#endif
}
- cld.env = (const char **)env;
cld.argv = (const char **)cld_argv;
cld.in = incoming;
cld.out = dup(incoming);
@@ -901,7 +895,7 @@ static const char *ip2str(int family, struct sockaddr *sin, socklen_t len)
inet_ntop(family, &((struct sockaddr_in*)sin)->sin_addr, ip, len);
break;
default:
- strcpy(ip, "<unknown>");
+ xsnprintf(ip, sizeof(ip), "<unknown>");
}
return ip;
}
@@ -916,7 +910,7 @@ static int setup_named_sock(char *listen_addr, int listen_port, struct socketlis
int gai;
long flags;
- sprintf(pbuf, "%d", listen_port);
+ xsnprintf(pbuf, sizeof(pbuf), "%d", listen_port);
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
diff --git a/date.c b/date.c
index 8f9156909b..7c9f76998a 100644
--- a/date.c
+++ b/date.c
@@ -166,6 +166,7 @@ struct date_mode *date_mode_from_type(enum date_mode_type type)
if (type == DATE_STRFTIME)
die("BUG: cannot create anonymous strftime date_mode struct");
mode.type = type;
+ mode.local = 0;
return &mode;
}
@@ -174,6 +175,9 @@ const char *show_date(unsigned long time, int tz, const struct date_mode *mode)
struct tm *tm;
static struct strbuf timebuf = STRBUF_INIT;
+ if (mode->local)
+ tz = local_tzoffset(time);
+
if (mode->type == DATE_RAW) {
strbuf_reset(&timebuf);
strbuf_addf(&timebuf, "%lu %+05d", time, tz);
@@ -189,9 +193,6 @@ const char *show_date(unsigned long time, int tz, const struct date_mode *mode)
return timebuf.buf;
}
- if (mode->type == DATE_LOCAL)
- tz = local_tzoffset(time);
-
tm = time_to_tm(time, tz);
if (!tm) {
tm = time_to_tm(0, 0);
@@ -232,7 +233,7 @@ const char *show_date(unsigned long time, int tz, const struct date_mode *mode)
tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec,
tm->tm_year + 1900,
- (mode->type == DATE_LOCAL) ? 0 : ' ',
+ mode->local ? 0 : ' ',
tz);
return timebuf.buf;
}
@@ -770,31 +771,50 @@ int parse_date(const char *date, struct strbuf *result)
return 0;
}
+static enum date_mode_type parse_date_type(const char *format, const char **end)
+{
+ if (skip_prefix(format, "relative", end))
+ return DATE_RELATIVE;
+ if (skip_prefix(format, "iso8601-strict", end) ||
+ skip_prefix(format, "iso-strict", end))
+ return DATE_ISO8601_STRICT;
+ if (skip_prefix(format, "iso8601", end) ||
+ skip_prefix(format, "iso", end))
+ return DATE_ISO8601;
+ if (skip_prefix(format, "rfc2822", end) ||
+ skip_prefix(format, "rfc", end))
+ return DATE_RFC2822;
+ if (skip_prefix(format, "short", end))
+ return DATE_SHORT;
+ if (skip_prefix(format, "default", end))
+ return DATE_NORMAL;
+ if (skip_prefix(format, "raw", end))
+ return DATE_RAW;
+ if (skip_prefix(format, "format", end))
+ return DATE_STRFTIME;
+
+ die("unknown date format %s", format);
+}
+
void parse_date_format(const char *format, struct date_mode *mode)
{
- if (!strcmp(format, "relative"))
- mode->type = DATE_RELATIVE;
- else if (!strcmp(format, "iso8601") ||
- !strcmp(format, "iso"))
- mode->type = DATE_ISO8601;
- else if (!strcmp(format, "iso8601-strict") ||
- !strcmp(format, "iso-strict"))
- mode->type = DATE_ISO8601_STRICT;
- else if (!strcmp(format, "rfc2822") ||
- !strcmp(format, "rfc"))
- mode->type = DATE_RFC2822;
- else if (!strcmp(format, "short"))
- mode->type = DATE_SHORT;
- else if (!strcmp(format, "local"))
- mode->type = DATE_LOCAL;
- else if (!strcmp(format, "default"))
- mode->type = DATE_NORMAL;
- else if (!strcmp(format, "raw"))
- mode->type = DATE_RAW;
- else if (skip_prefix(format, "format:", &format)) {
- mode->type = DATE_STRFTIME;
- mode->strftime_fmt = xstrdup(format);
- } else
+ const char *p;
+
+ /* historical alias */
+ if (!strcmp(format, "local"))
+ format = "default-local";
+
+ mode->type = parse_date_type(format, &p);
+ mode->local = 0;
+
+ if (skip_prefix(p, "-local", &p))
+ mode->local = 1;
+
+ if (mode->type == DATE_STRFTIME) {
+ if (!skip_prefix(p, ":", &p))
+ die("date format missing colon separator: %s", format);
+ mode->strftime_fmt = xstrdup(p);
+ } else if (*p)
die("unknown date format %s", format);
}
diff --git a/diff-no-index.c b/diff-no-index.c
index 0320605a84..8e0fd270b5 100644
--- a/diff-no-index.c
+++ b/diff-no-index.c
@@ -136,15 +136,13 @@ static int queue_diff(struct diff_options *o,
if (name1) {
strbuf_addstr(&buffer1, name1);
- if (buffer1.len && buffer1.buf[buffer1.len - 1] != '/')
- strbuf_addch(&buffer1, '/');
+ strbuf_complete(&buffer1, '/');
len1 = buffer1.len;
}
if (name2) {
strbuf_addstr(&buffer2, name2);
- if (buffer2.len && buffer2.buf[buffer2.len - 1] != '/')
- strbuf_addch(&buffer2, '/');
+ strbuf_complete(&buffer2, '/');
len2 = buffer2.len;
}
diff --git a/diff.c b/diff.c
index 46260ed7a1..835a12e84d 100644
--- a/diff.c
+++ b/diff.c
@@ -322,7 +322,7 @@ static struct diff_tempfile {
*/
const char *name;
- char hex[41];
+ char hex[GIT_SHA1_HEXSZ + 1];
char mode[10];
/*
@@ -2882,9 +2882,8 @@ static void prep_temp_blob(const char *path, struct diff_tempfile *temp,
die_errno("unable to write temp-file");
close_tempfile(&temp->tempfile);
temp->name = get_tempfile_path(&temp->tempfile);
- strcpy(temp->hex, sha1_to_hex(sha1));
- temp->hex[40] = 0;
- sprintf(temp->mode, "%06o", mode);
+ sha1_to_hex_r(temp->hex, sha1);
+ xsnprintf(temp->mode, sizeof(temp->mode), "%06o", mode);
strbuf_release(&buf);
strbuf_release(&template);
free(path_dup);
@@ -2901,8 +2900,8 @@ static struct diff_tempfile *prepare_temp_file(const char *name,
* a '+' entry produces this for file-1.
*/
temp->name = "/dev/null";
- strcpy(temp->hex, ".");
- strcpy(temp->mode, ".");
+ xsnprintf(temp->hex, sizeof(temp->hex), ".");
+ xsnprintf(temp->mode, sizeof(temp->mode), ".");
return temp;
}
@@ -2930,16 +2929,16 @@ static struct diff_tempfile *prepare_temp_file(const char *name,
/* we can borrow from the file in the work tree */
temp->name = name;
if (!one->sha1_valid)
- strcpy(temp->hex, sha1_to_hex(null_sha1));
+ sha1_to_hex_r(temp->hex, null_sha1);
else
- strcpy(temp->hex, sha1_to_hex(one->sha1));
+ sha1_to_hex_r(temp->hex, one->sha1);
/* Even though we may sometimes borrow the
* contents from the work tree, we always want
* one->mode. mode is trustworthy even when
* !(one->sha1_valid), as long as
* DIFF_FILE_VALID(one).
*/
- sprintf(temp->mode, "%06o", one->mode);
+ xsnprintf(temp->mode, sizeof(temp->mode), "%06o", one->mode);
}
return temp;
}
@@ -4085,9 +4084,9 @@ const char *diff_unique_abbrev(const unsigned char *sha1, int len)
if (abblen < 37) {
static char hex[41];
if (len < abblen && abblen <= len + 2)
- sprintf(hex, "%s%.*s", abbrev, len+3-abblen, "..");
+ xsnprintf(hex, sizeof(hex), "%s%.*s", abbrev, len+3-abblen, "..");
else
- sprintf(hex, "%s...", abbrev);
+ xsnprintf(hex, sizeof(hex), "%s...", abbrev);
return hex;
}
return sha1_to_hex(sha1);
diff --git a/dir.c b/dir.c
index 118ba59a27..109ceeaed3 100644
--- a/dir.c
+++ b/dir.c
@@ -882,6 +882,25 @@ int match_pathname(const char *pathname, int pathlen,
*/
if (!patternlen && !namelen)
return 1;
+ /*
+ * This can happen when we ignore some exclude rules
+ * on directories in other to see if negative rules
+ * may match. E.g.
+ *
+ * /abc
+ * !/abc/def/ghi
+ *
+ * The pattern of interest is "/abc". On the first
+ * try, we should match path "abc" with this pattern
+ * in the "if" statement right above, but the caller
+ * ignores it.
+ *
+ * On the second try with paths within "abc",
+ * e.g. "abc/xyz", we come here and try to match it
+ * with "/abc".
+ */
+ if (!patternlen && namelen && *name == '/')
+ return 1;
}
return fnmatch_icase_mem(pattern, patternlen,
@@ -890,6 +909,48 @@ int match_pathname(const char *pathname, int pathlen,
}
/*
+ * Return non-zero if pathname is a directory and an ancestor of the
+ * literal path in a (negative) pattern. This is used to keep
+ * descending in "foo" and "foo/bar" when the pattern is
+ * "!foo/bar/.gitignore". "foo/notbar" will not be descended however.
+ */
+static int match_neg_path(const char *pathname, int pathlen, int *dtype,
+ const char *base, int baselen,
+ const char *pattern, int prefix, int patternlen,
+ int flags)
+{
+ assert((flags & EXC_FLAG_NEGATIVE) && !(flags & EXC_FLAG_NODIR));
+
+ if (*dtype == DT_UNKNOWN)
+ *dtype = get_dtype(NULL, pathname, pathlen);
+ if (*dtype != DT_DIR)
+ return 0;
+
+ if (*pattern == '/') {
+ pattern++;
+ patternlen--;
+ prefix--;
+ }
+
+ if (baselen) {
+ if (((pathlen < baselen && base[pathlen] == '/') ||
+ pathlen == baselen) &&
+ !strncmp_icase(pathname, base, pathlen))
+ return 1;
+ pathname += baselen + 1;
+ pathlen -= baselen + 1;
+ }
+
+
+ if (prefix &&
+ ((pathlen < prefix && pattern[pathlen] == '/') &&
+ !strncmp_icase(pathname, pattern, pathlen)))
+ return 1;
+
+ return 0;
+}
+
+/*
* Scan the given exclude list in reverse to see whether pathname
* should be ignored. The first match (i.e. the last on the list), if
* any, determines the fate. Returns the exclude_list element which
@@ -901,7 +962,8 @@ static struct exclude *last_exclude_matching_from_list(const char *pathname,
int *dtype,
struct exclude_list *el)
{
- int i;
+ struct exclude *exc = NULL; /* undecided */
+ int i, matched_negative_path = 0;
if (!el->nr)
return NULL; /* undefined */
@@ -922,18 +984,33 @@ static struct exclude *last_exclude_matching_from_list(const char *pathname,
if (match_basename(basename,
pathlen - (basename - pathname),
exclude, prefix, x->patternlen,
- x->flags))
- return x;
+ x->flags)) {
+ exc = x;
+ break;
+ }
continue;
}
assert(x->baselen == 0 || x->base[x->baselen - 1] == '/');
if (match_pathname(pathname, pathlen,
x->base, x->baselen ? x->baselen - 1 : 0,
+ exclude, prefix, x->patternlen, x->flags)) {
+ exc = x;
+ break;
+ }
+
+ if ((x->flags & EXC_FLAG_NEGATIVE) && !matched_negative_path &&
+ match_neg_path(pathname, pathlen, dtype, x->base,
+ x->baselen ? x->baselen - 1 : 0,
exclude, prefix, x->patternlen, x->flags))
- return x;
+ matched_negative_path = 1;
}
- return NULL; /* undecided */
+ if (exc &&
+ !(exc->flags & EXC_FLAG_NEGATIVE) &&
+ !(exc->flags & EXC_FLAG_NODIR) &&
+ matched_negative_path)
+ exc = NULL;
+ return exc;
}
/*
@@ -1519,8 +1596,7 @@ static enum path_treatment treat_path_fast(struct dir_struct *dir,
}
strbuf_addstr(path, cdir->ucd->name);
/* treat_one_path() does this before it calls treat_directory() */
- if (path->buf[path->len - 1] != '/')
- strbuf_addch(path, '/');
+ strbuf_complete(path, '/');
if (cdir->ucd->check_only)
/*
* check_only is set as a result of treat_directory() getting
@@ -2135,8 +2211,7 @@ static int remove_dir_recurse(struct strbuf *path, int flag, int *kept_up)
else
return -1;
}
- if (path->buf[original_len - 1] != '/')
- strbuf_addch(path, '/');
+ strbuf_complete(path, '/');
len = path->len;
while ((e = readdir(dir)) != NULL) {
diff --git a/entry.c b/entry.c
index 1eda8e9471..582c40071a 100644
--- a/entry.c
+++ b/entry.c
@@ -96,8 +96,8 @@ static int open_output_fd(char *path, const struct cache_entry *ce, int to_tempf
{
int symlink = (ce->ce_mode & S_IFMT) != S_IFREG;
if (to_tempfile) {
- strcpy(path, symlink
- ? ".merge_link_XXXXXX" : ".merge_file_XXXXXX");
+ xsnprintf(path, TEMPORARY_FILENAME_LENGTH, "%s",
+ symlink ? ".merge_link_XXXXXX" : ".merge_file_XXXXXX");
return mkstemp(path);
} else {
return create_file(path, !symlink ? ce->ce_mode : 0666);
diff --git a/environment.c b/environment.c
index a533aed630..c5b65f5e23 100644
--- a/environment.c
+++ b/environment.c
@@ -143,11 +143,8 @@ static char *git_path_from_env(const char *envvar, const char *git_dir,
const char *path, int *fromenv)
{
const char *value = getenv(envvar);
- if (!value) {
- char *buf = xmalloc(strlen(git_dir) + strlen(path) + 2);
- sprintf(buf, "%s/%s", git_dir, path);
- return buf;
- }
+ if (!value)
+ return xstrfmt("%s/%s", git_dir, path);
if (fromenv)
*fromenv = 1;
return xstrdup(value);
diff --git a/fast-import.c b/fast-import.c
index 6c7c3c9b66..e3b421d514 100644
--- a/fast-import.c
+++ b/fast-import.c
@@ -424,7 +424,7 @@ static void write_crash_report(const char *err)
fprintf(rpt, "fast-import crash report:\n");
fprintf(rpt, " fast-import process: %"PRIuMAX"\n", (uintmax_t) getpid());
fprintf(rpt, " parent process : %"PRIuMAX"\n", (uintmax_t) getppid());
- fprintf(rpt, " at %s\n", show_date(time(NULL), 0, DATE_MODE(LOCAL)));
+ fprintf(rpt, " at %s\n", show_date(time(NULL), 0, DATE_MODE(ISO8601)));
fputc('\n', rpt);
fputs("fatal: ", rpt);
@@ -644,8 +644,9 @@ static void *pool_calloc(size_t count, size_t size)
static char *pool_strdup(const char *s)
{
- char *r = pool_alloc(strlen(s) + 1);
- strcpy(r, s);
+ size_t len = strlen(s) + 1;
+ char *r = pool_alloc(len);
+ memcpy(r, s, len);
return r;
}
@@ -702,7 +703,7 @@ static struct atom_str *to_atom(const char *s, unsigned short len)
c = pool_alloc(sizeof(struct atom_str) + len + 1);
c->str_len = len;
- strncpy(c->str_dat, s, len);
+ memcpy(c->str_dat, s, len);
c->str_dat[len] = 0;
c->next_atom = atom_table[hc];
atom_table[hc] = c;
@@ -863,13 +864,15 @@ static void start_packfile(void)
{
static char tmp_file[PATH_MAX];
struct packed_git *p;
+ int namelen;
struct pack_header hdr;
int pack_fd;
pack_fd = odb_mkstemp(tmp_file, sizeof(tmp_file),
"pack/tmp_pack_XXXXXX");
- p = xcalloc(1, sizeof(*p) + strlen(tmp_file) + 2);
- strcpy(p->pack_name, tmp_file);
+ namelen = strlen(tmp_file) + 2;
+ p = xcalloc(1, sizeof(*p) + namelen);
+ xsnprintf(p->pack_name, namelen, "%s", tmp_file);
p->pack_fd = pack_fd;
p->do_not_close = 1;
pack_file = sha1fd(pack_fd, p->pack_name);
@@ -1035,8 +1038,8 @@ static int store_object(
git_SHA_CTX c;
git_zstream s;
- hdrlen = sprintf((char *)hdr,"%s %lu", typename(type),
- (unsigned long)dat->len) + 1;
+ hdrlen = xsnprintf((char *)hdr, sizeof(hdr), "%s %lu",
+ typename(type), (unsigned long)dat->len) + 1;
git_SHA1_Init(&c);
git_SHA1_Update(&c, hdr, hdrlen);
git_SHA1_Update(&c, dat->buf, dat->len);
diff --git a/fetch-pack.c b/fetch-pack.c
index 820251a8d8..2dabee97b2 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -681,11 +681,10 @@ static int get_pack(struct fetch_pack_args *args,
int xd[2], char **pack_lockfile)
{
struct async demux;
- const char *argv[22];
- char keep_arg[256];
- char hdr_arg[256];
- const char **av, *cmd_name;
int do_keep = args->keep_pack;
+ const char *cmd_name;
+ struct pack_header header;
+ int pass_header = 0;
struct child_process cmd = CHILD_PROCESS_INIT;
int ret;
@@ -705,17 +704,11 @@ static int get_pack(struct fetch_pack_args *args,
else
demux.out = xd[0];
- cmd.argv = argv;
- av = argv;
- *hdr_arg = 0;
if (!args->keep_pack && unpack_limit) {
- struct pack_header header;
if (read_pack_header(demux.out, &header))
die("protocol error: bad pack header");
- snprintf(hdr_arg, sizeof(hdr_arg),
- "--pack_header=%"PRIu32",%"PRIu32,
- ntohl(header.hdr_version), ntohl(header.hdr_entries));
+ pass_header = 1;
if (ntohl(header.hdr_entries) < unpack_limit)
do_keep = 0;
else
@@ -723,44 +716,49 @@ static int get_pack(struct fetch_pack_args *args,
}
if (alternate_shallow_file) {
- *av++ = "--shallow-file";
- *av++ = alternate_shallow_file;
+ argv_array_push(&cmd.args, "--shallow-file");
+ argv_array_push(&cmd.args, alternate_shallow_file);
}
if (do_keep) {
if (pack_lockfile)
cmd.out = -1;
- *av++ = cmd_name = "index-pack";
- *av++ = "--stdin";
+ cmd_name = "index-pack";
+ argv_array_push(&cmd.args, cmd_name);
+ argv_array_push(&cmd.args, "--stdin");
if (!args->quiet && !args->no_progress)
- *av++ = "-v";
+ argv_array_push(&cmd.args, "-v");
if (args->use_thin_pack)
- *av++ = "--fix-thin";
+ argv_array_push(&cmd.args, "--fix-thin");
if (args->lock_pack || unpack_limit) {
- int s = sprintf(keep_arg,
- "--keep=fetch-pack %"PRIuMAX " on ", (uintmax_t) getpid());
- if (gethostname(keep_arg + s, sizeof(keep_arg) - s))
- strcpy(keep_arg + s, "localhost");
- *av++ = keep_arg;
+ char hostname[256];
+ if (gethostname(hostname, sizeof(hostname)))
+ xsnprintf(hostname, sizeof(hostname), "localhost");
+ argv_array_pushf(&cmd.args,
+ "--keep=fetch-pack %"PRIuMAX " on %s",
+ (uintmax_t)getpid(), hostname);
}
if (args->check_self_contained_and_connected)
- *av++ = "--check-self-contained-and-connected";
+ argv_array_push(&cmd.args, "--check-self-contained-and-connected");
}
else {
- *av++ = cmd_name = "unpack-objects";
+ cmd_name = "unpack-objects";
+ argv_array_push(&cmd.args, cmd_name);
if (args->quiet || args->no_progress)
- *av++ = "-q";
+ argv_array_push(&cmd.args, "-q");
args->check_self_contained_and_connected = 0;
}
- if (*hdr_arg)
- *av++ = hdr_arg;
+
+ if (pass_header)
+ argv_array_pushf(&cmd.args, "--pack_header=%"PRIu32",%"PRIu32,
+ ntohl(header.hdr_version),
+ ntohl(header.hdr_entries));
if (fetch_fsck_objects >= 0
? fetch_fsck_objects
: transfer_fsck_objects >= 0
? transfer_fsck_objects
: 0)
- *av++ = "--strict";
- *av++ = NULL;
+ argv_array_push(&cmd.args, "--strict");
cmd.in = demux.out;
cmd.git_cmd = 1;
diff --git a/git-bisect.sh b/git-bisect.sh
index ea63223ab3..5d1cb00d86 100755
--- a/git-bisect.sh
+++ b/git-bisect.sh
@@ -1,14 +1,19 @@
#!/bin/sh
-USAGE='[help|start|bad|good|skip|next|reset|visualize|replay|log|run]'
+USAGE='[help|start|bad|good|new|old|terms|skip|next|reset|visualize|replay|log|run]'
LONG_USAGE='git bisect help
print this long help message.
-git bisect start [--no-checkout] [<bad> [<good>...]] [--] [<pathspec>...]
+git bisect start [--term-{old,good}=<term> --term-{new,bad}=<term>]
+ [--no-checkout] [<bad> [<good>...]] [--] [<pathspec>...]
reset bisect state and start bisection.
-git bisect bad [<rev>]
- mark <rev> a known-bad revision.
-git bisect good [<rev>...]
- mark <rev>... known-good revisions.
+git bisect (bad|new) [<rev>]
+ mark <rev> a known-bad revision/
+ a revision after change in a given property.
+git bisect (good|old) [<rev>...]
+ mark <rev>... known-good revisions/
+ revisions before change in a given property.
+git bisect terms [--term-good | --term-bad]
+ show the terms used for old and new commits (default: bad, good)
git bisect skip [(<rev>|<range>)...]
mark <rev>... untestable revisions.
git bisect next
@@ -95,6 +100,24 @@ bisect_start() {
--no-checkout)
mode=--no-checkout
shift ;;
+ --term-good|--term-old)
+ shift
+ must_write_terms=1
+ TERM_GOOD=$1
+ shift ;;
+ --term-good=*|--term-old=*)
+ must_write_terms=1
+ TERM_GOOD=${1#*=}
+ shift ;;
+ --term-bad|--term-new)
+ shift
+ must_write_terms=1
+ TERM_BAD=$1
+ shift ;;
+ --term-bad=*|--term-new=*)
+ must_write_terms=1
+ TERM_BAD=${1#*=}
+ shift ;;
--*)
die "$(eval_gettext "unrecognised option: '\$arg'")" ;;
*)
@@ -294,7 +317,7 @@ bisect_next_check() {
false
;;
t,,"$TERM_GOOD")
- # have bad but not good. we could bisect although
+ # have bad (or new) but not good (or old). we could bisect although
# this is less optimum.
eval_gettextln "Warning: bisecting only with a \$TERM_BAD commit." >&2
if test -t 0
@@ -451,6 +474,8 @@ bisect_replay () {
eval "$cmd" ;;
"$TERM_GOOD"|"$TERM_BAD"|skip)
bisect_write "$command" "$rev" ;;
+ terms)
+ bisect_terms $rev ;;
*)
die "$(gettext "?? what are you talking about?")" ;;
esac
@@ -535,9 +560,42 @@ get_terms () {
write_terms () {
TERM_BAD=$1
TERM_GOOD=$2
+ if test "$TERM_BAD" = "$TERM_GOOD"
+ then
+ die "$(gettext "please use two different terms")"
+ fi
+ check_term_format "$TERM_BAD" bad
+ check_term_format "$TERM_GOOD" good
printf '%s\n%s\n' "$TERM_BAD" "$TERM_GOOD" >"$GIT_DIR/BISECT_TERMS"
}
+check_term_format () {
+ term=$1
+ git check-ref-format refs/bisect/"$term" ||
+ die "$(eval_gettext "'\$term' is not a valid term")"
+ case "$term" in
+ help|start|terms|skip|next|reset|visualize|replay|log|run)
+ die "$(eval_gettext "can't use the builtin command '\$term' as a term")"
+ ;;
+ bad|new)
+ if test "$2" != bad
+ then
+ # In theory, nothing prevents swapping
+ # completely good and bad, but this situation
+ # could be confusing and hasn't been tested
+ # enough. Forbid it for now.
+ die "$(eval_gettext "can't change the meaning of term '\$term'")"
+ fi
+ ;;
+ good|old)
+ if test "$2" != good
+ then
+ die "$(eval_gettext "can't change the meaning of term '\$term'")"
+ fi
+ ;;
+ esac
+}
+
check_and_set_terms () {
cmd="$1"
case "$cmd" in
@@ -554,14 +612,51 @@ check_and_set_terms () {
write_terms bad good
fi
;;
+ new|old)
+ if ! test -s "$GIT_DIR/BISECT_TERMS"
+ then
+ write_terms new old
+ fi
+ ;;
esac ;;
esac
}
bisect_voc () {
case "$1" in
- bad) echo "bad" ;;
- good) echo "good" ;;
+ bad) echo "bad|new" ;;
+ good) echo "good|old" ;;
+ esac
+}
+
+bisect_terms () {
+ get_terms
+ if ! test -s "$GIT_DIR/BISECT_TERMS"
+ then
+ die "$(gettext "no terms defined")"
+ fi
+ case "$#" in
+ 0)
+ gettextln "Your current terms are $TERM_GOOD for the old state
+and $TERM_BAD for the new state."
+ ;;
+ 1)
+ arg=$1
+ case "$arg" in
+ --term-good|--term-old)
+ printf '%s\n' "$TERM_GOOD"
+ ;;
+ --term-bad|--term-new)
+ printf '%s\n' "$TERM_BAD"
+ ;;
+ *)
+ die "$(eval_gettext "invalid argument \$arg for 'git bisect terms'.
+Supported options are: --term-good|--term-old and --term-bad|--term-new.")"
+ ;;
+ esac
+ ;;
+ *)
+ usage ;;
esac
}
@@ -577,7 +672,7 @@ case "$#" in
git bisect -h ;;
start)
bisect_start "$@" ;;
- bad|good|"$TERM_BAD"|"$TERM_GOOD")
+ bad|good|new|old|"$TERM_BAD"|"$TERM_GOOD")
bisect_state "$cmd" "$@" ;;
skip)
bisect_skip "$@" ;;
@@ -594,6 +689,8 @@ case "$#" in
bisect_log ;;
run)
bisect_run "$@" ;;
+ terms)
+ bisect_terms "$@" ;;
*)
usage ;;
esac
diff --git a/git-compat-util.h b/git-compat-util.h
index f649e81f11..88964f7886 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -229,7 +229,7 @@ typedef unsigned long uintptr_t;
#else
#define precompose_str(in,i_nfd2nfc)
#define precompose_argv(c,v)
-#define probe_utf8_pathname_composition(a,b)
+#define probe_utf8_pathname_composition()
#endif
#ifdef MKDIR_WO_TRAILING_SLASH
@@ -744,6 +744,9 @@ static inline size_t xsize_t(off_t len)
return (size_t)len;
}
+__attribute__((format (printf, 3, 4)))
+extern int xsnprintf(char *dst, size_t max, const char *fmt, ...);
+
/* in ctype.c, for kwset users */
extern const unsigned char tolower_trans_tbl[256];
@@ -814,6 +817,9 @@ static inline int strtoul_ui(char const *s, int base, unsigned int *result)
char *p;
errno = 0;
+ /* negative values would be accepted by strtoul */
+ if (strchr(s, '-'))
+ return -1;
ul = strtoul(s, &p, base);
if (errno || *p || p == s || (unsigned int) ul != ul)
return -1;
diff --git a/git-filter-branch.sh b/git-filter-branch.sh
index 5b3f63d8bb..27c9c54fbd 100755
--- a/git-filter-branch.sh
+++ b/git-filter-branch.sh
@@ -275,11 +275,41 @@ commits=$(wc -l <../revs | tr -d " ")
test $commits -eq 0 && die "Found nothing to rewrite"
# Rewrite the commits
+report_progress ()
+{
+ if test -n "$progress" &&
+ test $git_filter_branch__commit_count -gt $next_sample_at
+ then
+ count=$git_filter_branch__commit_count
+
+ now=$(date +%s)
+ elapsed=$(($now - $start_timestamp))
+ remaining=$(( ($commits - $count) * $elapsed / $count ))
+ if test $elapsed -gt 0
+ then
+ next_sample_at=$(( ($elapsed + 1) * $count / $elapsed ))
+ else
+ next_sample_at=$(($next_sample_at + 1))
+ fi
+ progress=" ($elapsed seconds passed, remaining $remaining predicted)"
+ fi
+ printf "\rRewrite $commit ($count/$commits)$progress "
+}
git_filter_branch__commit_count=0
+
+progress= start_timestamp=
+if date '+%s' 2>/dev/null | grep -q '^[0-9][0-9]*$'
+then
+ next_sample_at=0
+ progress="dummy to ensure this is not empty"
+ start_timestamp=$(date '+%s')
+fi
+
while read commit parents; do
git_filter_branch__commit_count=$(($git_filter_branch__commit_count+1))
- printf "\rRewrite $commit ($git_filter_branch__commit_count/$commits)"
+
+ report_progress
case "$filter_subdir" in
"")
@@ -347,7 +377,7 @@ while read commit parents; do
fi
{
- while read -r header_line && test -n "$header_line"
+ while IFS='' read -r header_line && test -n "$header_line"
do
# skip header lines...
:;
diff --git a/git-p4.py b/git-p4.py
index 0093fa3d83..daa60c60d6 100755
--- a/git-p4.py
+++ b/git-p4.py
@@ -22,6 +22,8 @@ import platform
import re
import shutil
import stat
+import zipfile
+import zlib
try:
from subprocess import CalledProcessError
@@ -104,6 +106,16 @@ def chdir(path, is_client_path=False):
path = os.getcwd()
os.environ['PWD'] = path
+def calcDiskFree():
+ """Return free space in bytes on the disk of the given dirname."""
+ if platform.system() == 'Windows':
+ free_bytes = ctypes.c_ulonglong(0)
+ ctypes.windll.kernel32.GetDiskFreeSpaceExW(ctypes.c_wchar_p(os.getcwd()), None, None, ctypes.pointer(free_bytes))
+ return free_bytes.value
+ else:
+ st = os.statvfs(os.getcwd())
+ return st.f_bavail * st.f_frsize
+
def die(msg):
if verbose:
raise Exception(msg)
@@ -134,13 +146,11 @@ def read_pipe(c, ignore_error=False):
sys.stderr.write('Reading pipe: %s\n' % str(c))
expand = isinstance(c,basestring)
- p = subprocess.Popen(c, stdout=subprocess.PIPE, shell=expand)
- pipe = p.stdout
- val = pipe.read()
- if p.wait() and not ignore_error:
- die('Command failed: %s' % str(c))
-
- return val
+ p = subprocess.Popen(c, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=expand)
+ (out, err) = p.communicate()
+ if p.returncode != 0 and not ignore_error:
+ die('Command failed: %s\nError: %s' % (str(c), err))
+ return out
def p4_read_pipe(c, ignore_error=False):
real_cmd = p4_build_cmd(c)
@@ -604,9 +614,12 @@ def gitBranchExists(branch):
_gitConfig = {}
-def gitConfig(key):
+def gitConfig(key, typeSpecifier=None):
if not _gitConfig.has_key(key):
- cmd = [ "git", "config", key ]
+ cmd = [ "git", "config" ]
+ if typeSpecifier:
+ cmd += [ typeSpecifier ]
+ cmd += [ key ]
s = read_pipe(cmd, ignore_error=True)
_gitConfig[key] = s.strip()
return _gitConfig[key]
@@ -617,16 +630,26 @@ def gitConfigBool(key):
in the config."""
if not _gitConfig.has_key(key):
- cmd = [ "git", "config", "--bool", key ]
+ _gitConfig[key] = gitConfig(key, '--bool') == "true"
+ return _gitConfig[key]
+
+def gitConfigInt(key):
+ if not _gitConfig.has_key(key):
+ cmd = [ "git", "config", "--int", key ]
s = read_pipe(cmd, ignore_error=True)
v = s.strip()
- _gitConfig[key] = v == "true"
+ try:
+ _gitConfig[key] = int(gitConfig(key, '--int'))
+ except ValueError:
+ _gitConfig[key] = None
return _gitConfig[key]
def gitConfigList(key):
if not _gitConfig.has_key(key):
s = read_pipe(["git", "config", "--get-all", key], ignore_error=True)
_gitConfig[key] = s.strip().split(os.linesep)
+ if _gitConfig[key] == ['']:
+ _gitConfig[key] = []
return _gitConfig[key]
def p4BranchesInGit(branchesAreInRemotes=True):
@@ -909,6 +932,182 @@ def wildcard_present(path):
m = re.search("[*#@%]", path)
return m is not None
+class LargeFileSystem(object):
+ """Base class for large file system support."""
+
+ def __init__(self, writeToGitStream):
+ self.largeFiles = set()
+ self.writeToGitStream = writeToGitStream
+
+ def generatePointer(self, cloneDestination, contentFile):
+ """Return the content of a pointer file that is stored in Git instead of
+ the actual content."""
+ assert False, "Method 'generatePointer' required in " + self.__class__.__name__
+
+ def pushFile(self, localLargeFile):
+ """Push the actual content which is not stored in the Git repository to
+ a server."""
+ assert False, "Method 'pushFile' required in " + self.__class__.__name__
+
+ def hasLargeFileExtension(self, relPath):
+ return reduce(
+ lambda a, b: a or b,
+ [relPath.endswith('.' + e) for e in gitConfigList('git-p4.largeFileExtensions')],
+ False
+ )
+
+ def generateTempFile(self, contents):
+ contentFile = tempfile.NamedTemporaryFile(prefix='git-p4-large-file', delete=False)
+ for d in contents:
+ contentFile.write(d)
+ contentFile.close()
+ return contentFile.name
+
+ def exceedsLargeFileThreshold(self, relPath, contents):
+ if gitConfigInt('git-p4.largeFileThreshold'):
+ contentsSize = sum(len(d) for d in contents)
+ if contentsSize > gitConfigInt('git-p4.largeFileThreshold'):
+ return True
+ if gitConfigInt('git-p4.largeFileCompressedThreshold'):
+ contentsSize = sum(len(d) for d in contents)
+ if contentsSize <= gitConfigInt('git-p4.largeFileCompressedThreshold'):
+ return False
+ contentTempFile = self.generateTempFile(contents)
+ compressedContentFile = tempfile.NamedTemporaryFile(prefix='git-p4-large-file', delete=False)
+ zf = zipfile.ZipFile(compressedContentFile.name, mode='w')
+ zf.write(contentTempFile, compress_type=zipfile.ZIP_DEFLATED)
+ zf.close()
+ compressedContentsSize = zf.infolist()[0].compress_size
+ os.remove(contentTempFile)
+ os.remove(compressedContentFile.name)
+ if compressedContentsSize > gitConfigInt('git-p4.largeFileCompressedThreshold'):
+ return True
+ return False
+
+ def addLargeFile(self, relPath):
+ self.largeFiles.add(relPath)
+
+ def removeLargeFile(self, relPath):
+ self.largeFiles.remove(relPath)
+
+ def isLargeFile(self, relPath):
+ return relPath in self.largeFiles
+
+ def processContent(self, git_mode, relPath, contents):
+ """Processes the content of git fast import. This method decides if a
+ file is stored in the large file system and handles all necessary
+ steps."""
+ if self.exceedsLargeFileThreshold(relPath, contents) or self.hasLargeFileExtension(relPath):
+ contentTempFile = self.generateTempFile(contents)
+ (git_mode, contents, localLargeFile) = self.generatePointer(contentTempFile)
+
+ # Move temp file to final location in large file system
+ largeFileDir = os.path.dirname(localLargeFile)
+ if not os.path.isdir(largeFileDir):
+ os.makedirs(largeFileDir)
+ shutil.move(contentTempFile, localLargeFile)
+ self.addLargeFile(relPath)
+ if gitConfigBool('git-p4.largeFilePush'):
+ self.pushFile(localLargeFile)
+ if verbose:
+ sys.stderr.write("%s moved to large file system (%s)\n" % (relPath, localLargeFile))
+ return (git_mode, contents)
+
+class MockLFS(LargeFileSystem):
+ """Mock large file system for testing."""
+
+ def generatePointer(self, contentFile):
+ """The pointer content is the original content prefixed with "pointer-".
+ The local filename of the large file storage is derived from the file content.
+ """
+ with open(contentFile, 'r') as f:
+ content = next(f)
+ gitMode = '100644'
+ pointerContents = 'pointer-' + content
+ localLargeFile = os.path.join(os.getcwd(), '.git', 'mock-storage', 'local', content[:-1])
+ return (gitMode, pointerContents, localLargeFile)
+
+ def pushFile(self, localLargeFile):
+ """The remote filename of the large file storage is the same as the local
+ one but in a different directory.
+ """
+ remotePath = os.path.join(os.path.dirname(localLargeFile), '..', 'remote')
+ if not os.path.exists(remotePath):
+ os.makedirs(remotePath)
+ shutil.copyfile(localLargeFile, os.path.join(remotePath, os.path.basename(localLargeFile)))
+
+class GitLFS(LargeFileSystem):
+ """Git LFS as backend for the git-p4 large file system.
+ See https://git-lfs.github.com/ for details."""
+
+ def __init__(self, *args):
+ LargeFileSystem.__init__(self, *args)
+ self.baseGitAttributes = []
+
+ def generatePointer(self, contentFile):
+ """Generate a Git LFS pointer for the content. Return LFS Pointer file
+ mode and content which is stored in the Git repository instead of
+ the actual content. Return also the new location of the actual
+ content.
+ """
+ pointerProcess = subprocess.Popen(
+ ['git', 'lfs', 'pointer', '--file=' + contentFile],
+ stdout=subprocess.PIPE
+ )
+ pointerFile = pointerProcess.stdout.read()
+ if pointerProcess.wait():
+ os.remove(contentFile)
+ die('git-lfs pointer command failed. Did you install the extension?')
+ pointerContents = [i+'\n' for i in pointerFile.split('\n')[2:][:-1]]
+ oid = pointerContents[1].split(' ')[1].split(':')[1][:-1]
+ localLargeFile = os.path.join(
+ os.getcwd(),
+ '.git', 'lfs', 'objects', oid[:2], oid[2:4],
+ oid,
+ )
+ # LFS Spec states that pointer files should not have the executable bit set.
+ gitMode = '100644'
+ return (gitMode, pointerContents, localLargeFile)
+
+ def pushFile(self, localLargeFile):
+ uploadProcess = subprocess.Popen(
+ ['git', 'lfs', 'push', '--object-id', 'origin', os.path.basename(localLargeFile)]
+ )
+ if uploadProcess.wait():
+ die('git-lfs push command failed. Did you define a remote?')
+
+ def generateGitAttributes(self):
+ return (
+ self.baseGitAttributes +
+ [
+ '\n',
+ '#\n',
+ '# Git LFS (see https://git-lfs.github.com/)\n',
+ '#\n',
+ ] +
+ ['*.' + f.replace(' ', '[[:space:]]') + ' filter=lfs -text\n'
+ for f in sorted(gitConfigList('git-p4.largeFileExtensions'))
+ ] +
+ ['/' + f.replace(' ', '[[:space:]]') + ' filter=lfs -text\n'
+ for f in sorted(self.largeFiles) if not self.hasLargeFileExtension(f)
+ ]
+ )
+
+ def addLargeFile(self, relPath):
+ LargeFileSystem.addLargeFile(self, relPath)
+ self.writeToGitStream('100644', '.gitattributes', self.generateGitAttributes())
+
+ def removeLargeFile(self, relPath):
+ LargeFileSystem.removeLargeFile(self, relPath)
+ self.writeToGitStream('100644', '.gitattributes', self.generateGitAttributes())
+
+ def processContent(self, git_mode, relPath, contents):
+ if relPath == '.gitattributes':
+ self.baseGitAttributes = contents
+ return (git_mode, self.generateGitAttributes())
+ else:
+ return LargeFileSystem.processContent(self, git_mode, relPath, contents)
+
class Command:
def __init__(self):
self.usage = "usage: %prog [options]"
@@ -1082,6 +1281,9 @@ class P4Submit(Command, P4UserMap):
self.p4HasMoveCommand = p4_has_move_command()
self.branch = None
+ if gitConfig('git-p4.largeFileSystem'):
+ die("Large file system not supported for git-p4 submit command. Please remove it from config.")
+
def check(self):
if len(p4CmdList("opened ...")) > 0:
die("You have files opened with perforce! Close them before starting the sync.")
@@ -2032,6 +2234,13 @@ class P4Sync(Command, P4UserMap):
self.clientSpecDirs = None
self.tempBranches = []
self.tempBranchLocation = "git-p4-tmp"
+ self.largeFileSystem = None
+
+ if gitConfig('git-p4.largeFileSystem'):
+ largeFileSystemConstructor = globals()[gitConfig('git-p4.largeFileSystem')]
+ self.largeFileSystem = largeFileSystemConstructor(
+ lambda git_mode, relPath, contents: self.writeToGitStream(git_mode, relPath, contents)
+ )
if gitConfig("git-p4.syncFromOrigin") == "false":
self.syncWithOrigin = False
@@ -2152,13 +2361,22 @@ class P4Sync(Command, P4UserMap):
return branches
+ def writeToGitStream(self, gitMode, relPath, contents):
+ self.gitStream.write('M %s inline %s\n' % (gitMode, relPath))
+ self.gitStream.write('data %d\n' % sum(len(d) for d in contents))
+ for d in contents:
+ self.gitStream.write(d)
+ self.gitStream.write('\n')
+
# output one file from the P4 stream
# - helper for streamP4Files
def streamOneP4File(self, file, contents):
relPath = self.stripRepoPath(file['depotFile'], self.branchPrefixes)
if verbose:
- sys.stderr.write("%s\n" % relPath)
+ size = int(self.stream_file['fileSize'])
+ sys.stdout.write('\r%s --> %s (%i MB)\n' % (file['depotFile'], relPath, size/1024/1024))
+ sys.stdout.flush()
(type_base, type_mods) = split_p4_type(file["type"])
@@ -2193,10 +2411,17 @@ class P4Sync(Command, P4UserMap):
# them back too. This is not needed to the cygwin windows version,
# just the native "NT" type.
#
- text = p4_read_pipe(['print', '-q', '-o', '-', "%s@%s" % (file['depotFile'], file['change']) ])
- if p4_version_string().find("/NT") >= 0:
- text = text.replace("\r\n", "\n")
- contents = [ text ]
+ try:
+ text = p4_read_pipe(['print', '-q', '-o', '-', '%s@%s' % (file['depotFile'], file['change'])])
+ except Exception as e:
+ if 'Translation of file content failed' in str(e):
+ type_base = 'binary'
+ else:
+ raise e
+ else:
+ if p4_version_string().find('/NT') >= 0:
+ text = text.replace('\r\n', '\n')
+ contents = [ text ]
if type_base == "apple":
# Apple filetype files will be streamed as a concatenation of
@@ -2220,24 +2445,31 @@ class P4Sync(Command, P4UserMap):
text = regexp.sub(r'$\1$', text)
contents = [ text ]
- self.gitStream.write("M %s inline %s\n" % (git_mode, relPath))
+ try:
+ relPath.decode('ascii')
+ except:
+ encoding = 'utf8'
+ if gitConfig('git-p4.pathEncoding'):
+ encoding = gitConfig('git-p4.pathEncoding')
+ relPath = relPath.decode(encoding, 'replace').encode('utf8', 'replace')
+ if self.verbose:
+ print 'Path with non-ASCII characters detected. Used %s to encode: %s ' % (encoding, relPath)
- # total length...
- length = 0
- for d in contents:
- length = length + len(d)
+ if self.largeFileSystem:
+ (git_mode, contents) = self.largeFileSystem.processContent(git_mode, relPath, contents)
- self.gitStream.write("data %d\n" % length)
- for d in contents:
- self.gitStream.write(d)
- self.gitStream.write("\n")
+ self.writeToGitStream(git_mode, relPath, contents)
def streamOneP4Deletion(self, file):
relPath = self.stripRepoPath(file['path'], self.branchPrefixes)
if verbose:
- sys.stderr.write("delete %s\n" % relPath)
+ sys.stdout.write("delete %s\n" % relPath)
+ sys.stdout.flush()
self.gitStream.write("D %s\n" % relPath)
+ if self.largeFileSystem and self.largeFileSystem.isLargeFile(relPath):
+ self.largeFileSystem.removeLargeFile(relPath)
+
# handle another chunk of streaming data
def streamP4FilesCb(self, marshalled):
@@ -2247,6 +2479,14 @@ class P4Sync(Command, P4UserMap):
if marshalled["code"] == "error":
if "data" in marshalled:
err = marshalled["data"].rstrip()
+
+ if not err and 'fileSize' in self.stream_file:
+ required_bytes = int((4 * int(self.stream_file["fileSize"])) - calcDiskFree())
+ if required_bytes > 0:
+ err = 'Not enough space left on %s! Free at least %i MB.' % (
+ os.getcwd(), required_bytes/1024/1024
+ )
+
if err:
f = None
if self.stream_have_file_info:
@@ -2275,10 +2515,23 @@ class P4Sync(Command, P4UserMap):
# 'data' field we need to append to our array
for k in marshalled.keys():
if k == 'data':
+ if 'streamContentSize' not in self.stream_file:
+ self.stream_file['streamContentSize'] = 0
+ self.stream_file['streamContentSize'] += len(marshalled['data'])
self.stream_contents.append(marshalled['data'])
else:
self.stream_file[k] = marshalled[k]
+ if (verbose and
+ 'streamContentSize' in self.stream_file and
+ 'fileSize' in self.stream_file and
+ 'depotFile' in self.stream_file):
+ size = int(self.stream_file["fileSize"])
+ if size > 0:
+ progress = 100*self.stream_file['streamContentSize']/size
+ sys.stdout.write('\r%s %d%% (%i MB)' % (self.stream_file['depotFile'], progress, int(size/1024/1024)))
+ sys.stdout.flush()
+
self.stream_have_file_info = True
# Stream directly from "p4 files" into "git fast-import"
@@ -2329,8 +2582,11 @@ class P4Sync(Command, P4UserMap):
else:
return "%s <a@b>" % userid
- # Stream a p4 tag
def streamTag(self, gitStream, labelName, labelDetails, commit, epoch):
+ """ Stream a p4 tag.
+ commit is either a git commit, or a fast-import mark, ":<p4commit>"
+ """
+
if verbose:
print "writing tag %s for commit %s" % (labelName, commit)
gitStream.write("tag %s\n" % labelName)
@@ -2381,7 +2637,7 @@ class P4Sync(Command, P4UserMap):
self.clientSpecDirs.update_client_spec_path_cache(files)
self.gitStream.write("commit %s\n" % branch)
-# gitStream.write("mark :%s\n" % details["change"])
+ self.gitStream.write("mark :%s\n" % details["change"])
self.committedChanges.add(int(details["change"]))
committer = ""
if author not in self.users:
@@ -2500,13 +2756,19 @@ class P4Sync(Command, P4UserMap):
if change.has_key('change'):
# find the corresponding git commit; take the oldest commit
changelist = int(change['change'])
- gitCommit = read_pipe(["git", "rev-list", "--max-count=1",
- "--reverse", ":/\[git-p4:.*change = %d\]" % changelist])
- if len(gitCommit) == 0:
- print "could not find git commit for changelist %d" % changelist
- else:
- gitCommit = gitCommit.strip()
+ if changelist in self.committedChanges:
+ gitCommit = ":%d" % changelist # use a fast-import mark
commitFound = True
+ else:
+ gitCommit = read_pipe(["git", "rev-list", "--max-count=1",
+ "--reverse", ":/\[git-p4:.*change = %d\]" % changelist], ignore_error=True)
+ if len(gitCommit) == 0:
+ print "importing label %s: could not find git commit for changelist %d" % (name, changelist)
+ else:
+ commitFound = True
+ gitCommit = gitCommit.strip()
+
+ if commitFound:
# Convert from p4 time format
try:
tmwhen = time.strptime(labelDetails['Update'], "%Y/%m/%d %H:%M:%S")
diff --git a/git-quiltimport.sh b/git-quiltimport.sh
index 167d79fea8..6d3a88decd 100755
--- a/git-quiltimport.sh
+++ b/git-quiltimport.sh
@@ -6,7 +6,8 @@ git quiltimport [options]
--
n,dry-run dry run
author= author name and email address for patches without any
-patches= path to the quilt series and patches
+patches= path to the quilt patches
+series= path to the quilt series file
"
SUBDIRECTORY_ON=Yes
. git-sh-setup
@@ -27,6 +28,10 @@ do
shift
QUILT_PATCHES="$1"
;;
+ --series)
+ shift
+ QUILT_SERIES="$1"
+ ;;
--)
shift
break;;
@@ -53,6 +58,13 @@ if ! [ -d "$QUILT_PATCHES" ] ; then
exit 1
fi
+# Quilt series file
+: ${QUILT_SERIES:=$QUILT_PATCHES/series}
+if ! [ -e "$QUILT_SERIES" ] ; then
+ echo "The \"$QUILT_SERIES\" file does not exist."
+ exit 1
+fi
+
# Temporary directories
tmp_dir="$GIT_DIR"/rebase-apply
tmp_msg="$tmp_dir/msg"
@@ -135,5 +147,5 @@ do
commit=$( (echo "$SUBJECT"; echo; cat "$tmp_msg") | git commit-tree $tree -p $commit) &&
git update-ref -m "quiltimport: $patch_name" HEAD $commit || exit 4
fi
-done 3<"$QUILT_PATCHES/series"
+done 3<"$QUILT_SERIES"
rm -rf $tmp_dir || exit 5
diff --git a/git-rebase.sh b/git-rebase.sh
index 1757404bc2..af7ba5fd90 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -14,7 +14,7 @@ git-rebase --continue | --abort | --skip | --edit-todo
Available options are
v,verbose! display a diffstat of what changed upstream
q,quiet! be quiet. implies --no-stat
-autostash! automatically stash/stash pop before and after
+autostash automatically stash/stash pop before and after
fork-point use 'merge-base --fork-point' to refine upstream
onto=! rebase onto given branch instead of upstream
p,preserve-merges! try to recreate merges instead of ignoring them
@@ -292,6 +292,9 @@ do
--autostash)
autostash=true
;;
+ --no-autostash)
+ autostash=false
+ ;;
--verbose)
verbose=t
diffstat=t
diff --git a/git-send-email.perl b/git-send-email.perl
index e3ff44b4d0..e907e0eacf 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -1365,7 +1365,11 @@ Message-Id: $message_id
$smtp->mail( $raw_from ) or die $smtp->message;
$smtp->to( @recipients ) or die $smtp->message;
$smtp->data or die $smtp->message;
- $smtp->datasend("$header\n$message") or die $smtp->message;
+ $smtp->datasend("$header\n") or die $smtp->message;
+ my @lines = split /^/, $message;
+ foreach my $line (@lines) {
+ $smtp->datasend("$line") or die $smtp->message;
+ }
$smtp->dataend() or die $smtp->message;
$smtp->code =~ /250|200/ or die "Failed to send $subject\n".$smtp->message;
}
diff --git a/git-stash.sh b/git-stash.sh
index 1d5ba7a4f9..c7c65e25f5 100755
--- a/git-stash.sh
+++ b/git-stash.sh
@@ -305,7 +305,25 @@ show_stash () {
ALLOW_UNKNOWN_FLAGS=t
assert_stash_like "$@"
- git diff ${FLAGS:---stat} $b_commit $w_commit
+ if test -z "$FLAGS"
+ then
+ if test "$(git config --bool stash.showStat || echo true)" = "true"
+ then
+ FLAGS=--stat
+ fi
+
+ if test "$(git config --bool stash.showPatch || echo false)" = "true"
+ then
+ FLAGS=${FLAGS}${FLAGS:+ }-p
+ fi
+
+ if test -z "$FLAGS"
+ then
+ return 0
+ fi
+ fi
+
+ git diff ${FLAGS} $b_commit $w_commit
}
show_help () {
diff --git a/git-submodule.sh b/git-submodule.sh
index 82e35582cd..9bc5c5f94d 100755
--- a/git-submodule.sh
+++ b/git-submodule.sh
@@ -154,48 +154,6 @@ relative_path ()
echo "$result$target"
}
-#
-# Get submodule info for registered submodules
-# $@ = path to limit submodule list
-#
-module_list()
-{
- eval "set $(git rev-parse --sq --prefix "$wt_prefix" -- "$@")"
- (
- git ls-files -z --error-unmatch --stage -- "$@" ||
- echo "unmatched pathspec exists"
- ) |
- @@PERL@@ -e '
- my %unmerged = ();
- my ($null_sha1) = ("0" x 40);
- my @out = ();
- my $unmatched = 0;
- $/ = "\0";
- while (<STDIN>) {
- if (/^unmatched pathspec/) {
- $unmatched = 1;
- next;
- }
- chomp;
- my ($mode, $sha1, $stage, $path) =
- /^([0-7]+) ([0-9a-f]{40}) ([0-3])\t(.*)$/;
- next unless $mode eq "160000";
- if ($stage ne "0") {
- if (!$unmerged{$path}++) {
- push @out, "$mode $null_sha1 U\t$path\n";
- }
- next;
- }
- push @out, "$_\n";
- }
- if ($unmatched) {
- print "#unmatched\n";
- } else {
- print for (@out);
- }
- '
-}
-
die_if_unmatched ()
{
if test "$1" = "#unmatched"
@@ -229,98 +187,6 @@ get_submodule_config () {
printf '%s' "${value:-$default}"
}
-
-#
-# Map submodule path to submodule name
-#
-# $1 = path
-#
-module_name()
-{
- # Do we have "submodule.<something>.path = $1" defined in .gitmodules file?
- sm_path="$1"
- re=$(printf '%s\n' "$1" | sed -e 's/[].[^$\\*]/\\&/g')
- name=$( git config -f .gitmodules --get-regexp '^submodule\..*\.path$' |
- sed -n -e 's|^submodule\.\(.*\)\.path '"$re"'$|\1|p' )
- test -z "$name" &&
- die "$(eval_gettext "No submodule mapping found in .gitmodules for path '\$sm_path'")"
- printf '%s\n' "$name"
-}
-
-#
-# Clone a submodule
-#
-# $1 = submodule path
-# $2 = submodule name
-# $3 = URL to clone
-# $4 = reference repository to reuse (empty for independent)
-# $5 = depth argument for shallow clones (empty for deep)
-#
-# Prior to calling, cmd_update checks that a possibly existing
-# path is not a git repository.
-# Likewise, cmd_add checks that path does not exist at all,
-# since it is the location of a new submodule.
-#
-module_clone()
-{
- sm_path=$1
- name=$2
- url=$3
- reference="$4"
- depth="$5"
- quiet=
- if test -n "$GIT_QUIET"
- then
- quiet=-q
- fi
-
- gitdir=
- gitdir_base=
- base_name=$(dirname "$name")
-
- gitdir=$(git rev-parse --git-dir)
- gitdir_base="$gitdir/modules/$base_name"
- gitdir="$gitdir/modules/$name"
-
- if test -d "$gitdir"
- then
- mkdir -p "$sm_path"
- rm -f "$gitdir/index"
- else
- mkdir -p "$gitdir_base"
- (
- clear_local_git_env
- git clone $quiet ${depth:+"$depth"} -n ${reference:+"$reference"} \
- --separate-git-dir "$gitdir" "$url" "$sm_path"
- ) ||
- die "$(eval_gettext "Clone of '\$url' into submodule path '\$sm_path' failed")"
- fi
-
- # We already are at the root of the work tree but cd_to_toplevel will
- # resolve any symlinks that might be present in $PWD
- a=$(cd_to_toplevel && cd "$gitdir" && pwd)/
- b=$(cd_to_toplevel && cd "$sm_path" && pwd)/
- # Remove all common leading directories after a sanity check
- if test "${a#$b}" != "$a" || test "${b#$a}" != "$b"; then
- die "$(eval_gettext "Gitdir '\$a' is part of the submodule path '\$b' or vice versa")"
- fi
- while test "${a%%/*}" = "${b%%/*}"
- do
- a=${a#*/}
- b=${b#*/}
- done
- # Now chop off the trailing '/'s that were added in the beginning
- a=${a%/}
- b=${b%/}
-
- # Turn each leading "*/" component into "../"
- rel=$(printf '%s\n' "$b" | sed -e 's|[^/][^/]*|..|g')
- printf '%s\n' "gitdir: $rel/$a" >"$sm_path/.git"
-
- rel=$(printf '%s\n' "$a" | sed -e 's|[^/][^/]*|..|g')
- (clear_local_git_env; cd "$sm_path" && GIT_WORK_TREE=. git config core.worktree "$rel/$b")
-}
-
isnumber()
{
n=$(($1 + 0)) 2>/dev/null && test "$n" = "$1"
@@ -481,7 +347,7 @@ Use -f if you really want to add it." >&2
echo "$(eval_gettext "Reactivating local git directory for submodule '\$sm_name'.")"
fi
fi
- module_clone "$sm_path" "$sm_name" "$realrepo" "$reference" "$depth" || exit
+ git submodule--helper clone ${GIT_QUIET:+--quiet} --prefix "$wt_prefix" --path "$sm_path" --name "$sm_name" --url "$realrepo" "$reference" "$depth" || exit
(
clear_local_git_env
cd "$sm_path" &&
@@ -541,7 +407,7 @@ cmd_foreach()
# command in the subshell (and a recursive call to this function)
exec 3<&0
- module_list |
+ git submodule--helper list --prefix "$wt_prefix"|
while read mode sha1 stage sm_path
do
die_if_unmatched "$mode"
@@ -549,7 +415,7 @@ cmd_foreach()
then
displaypath=$(relative_path "$sm_path")
say "$(eval_gettext "Entering '\$prefix\$displaypath'")"
- name=$(module_name "$sm_path")
+ name=$(git submodule--helper name "$sm_path")
(
prefix="$prefix$sm_path/"
clear_local_git_env
@@ -601,11 +467,11 @@ cmd_init()
shift
done
- module_list "$@" |
+ git submodule--helper list --prefix "$wt_prefix" "$@" |
while read mode sha1 stage sm_path
do
die_if_unmatched "$mode"
- name=$(module_name "$sm_path") || exit
+ name=$(git submodule--helper name "$sm_path") || exit
displaypath=$(relative_path "$sm_path")
@@ -683,11 +549,11 @@ cmd_deinit()
die "$(eval_gettext "Use '.' if you really want to deinitialize all submodules")"
fi
- module_list "$@" |
+ git submodule--helper list --prefix "$wt_prefix" "$@" |
while read mode sha1 stage sm_path
do
die_if_unmatched "$mode"
- name=$(module_name "$sm_path") || exit
+ name=$(git submodule--helper name "$sm_path") || exit
displaypath=$(relative_path "$sm_path")
@@ -799,7 +665,7 @@ cmd_update()
fi
cloned_modules=
- module_list "$@" | {
+ git submodule--helper list --prefix "$wt_prefix" "$@" | {
err=
while read mode sha1 stage sm_path
do
@@ -809,7 +675,7 @@ cmd_update()
echo >&2 "Skipping unmerged submodule $prefix$sm_path"
continue
fi
- name=$(module_name "$sm_path") || exit
+ name=$(git submodule--helper name "$sm_path") || exit
url=$(git config submodule."$name".url)
branch=$(get_submodule_config "$name" branch master)
if ! test -z "$update"
@@ -843,7 +709,7 @@ Maybe you want to use 'update --init'?")"
if ! test -d "$sm_path"/.git && ! test -f "$sm_path"/.git
then
- module_clone "$sm_path" "$name" "$url" "$reference" "$depth" || exit
+ git submodule--helper clone ${GIT_QUIET:+--quiet} --prefix "$prefix" --path "$sm_path" --name "$name" --url "$url" "$reference" "$depth" || exit
cloned_modules="$cloned_modules;$name"
subsha1=
else
@@ -1073,7 +939,7 @@ cmd_summary() {
# Respect the ignore setting for --for-status.
if test -n "$for_status"
then
- name=$(module_name "$sm_path")
+ name=$(git submodule--helper name "$sm_path")
ignore_config=$(get_submodule_config "$name" ignore none)
test $status != A && test $ignore_config = all && continue
fi
@@ -1231,11 +1097,11 @@ cmd_status()
shift
done
- module_list "$@" |
+ git submodule--helper list --prefix "$wt_prefix" "$@" |
while read mode sha1 stage sm_path
do
die_if_unmatched "$mode"
- name=$(module_name "$sm_path") || exit
+ name=$(git submodule--helper name "$sm_path") || exit
url=$(git config submodule."$name".url)
displaypath=$(relative_path "$prefix$sm_path")
if test "$stage" = U
@@ -1308,11 +1174,11 @@ cmd_sync()
esac
done
cd_to_toplevel
- module_list "$@" |
+ git submodule--helper list --prefix "$wt_prefix" "$@" |
while read mode sha1 stage sm_path
do
die_if_unmatched "$mode"
- name=$(module_name "$sm_path")
+ name=$(git submodule--helper name "$sm_path")
url=$(git config -f .gitmodules --get submodule."$name".url)
# Possibly a url relative to parent
diff --git a/git.c b/git.c
index 5feba410ca..6ed824cacf 100644
--- a/git.c
+++ b/git.c
@@ -417,7 +417,7 @@ static struct cmd_struct commands[] = {
{ "index-pack", cmd_index_pack, RUN_SETUP_GENTLY },
{ "init", cmd_init_db, NO_SETUP },
{ "init-db", cmd_init_db, NO_SETUP },
- { "interpret-trailers", cmd_interpret_trailers, RUN_SETUP },
+ { "interpret-trailers", cmd_interpret_trailers, RUN_SETUP_GENTLY },
{ "log", cmd_log, RUN_SETUP },
{ "ls-files", cmd_ls_files, RUN_SETUP },
{ "ls-remote", cmd_ls_remote, RUN_SETUP_GENTLY },
@@ -470,6 +470,7 @@ static struct cmd_struct commands[] = {
{ "stage", cmd_add, RUN_SETUP | NEED_WORK_TREE },
{ "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
{ "stripspace", cmd_stripspace },
+ { "submodule--helper", cmd_submodule__helper, RUN_SETUP },
{ "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
{ "tag", cmd_tag, RUN_SETUP },
{ "unpack-file", cmd_unpack_file, RUN_SETUP },
diff --git a/grep.c b/grep.c
index b58c7c6434..7b2b96a437 100644
--- a/grep.c
+++ b/grep.c
@@ -31,14 +31,14 @@ void init_grep_defaults(void)
opt->max_depth = -1;
opt->pattern_type_option = GREP_PATTERN_TYPE_UNSPECIFIED;
opt->extended_regexp_option = 0;
- strcpy(opt->color_context, "");
- strcpy(opt->color_filename, "");
- strcpy(opt->color_function, "");
- strcpy(opt->color_lineno, "");
- strcpy(opt->color_match_context, GIT_COLOR_BOLD_RED);
- strcpy(opt->color_match_selected, GIT_COLOR_BOLD_RED);
- strcpy(opt->color_selected, "");
- strcpy(opt->color_sep, GIT_COLOR_CYAN);
+ color_set(opt->color_context, "");
+ color_set(opt->color_filename, "");
+ color_set(opt->color_function, "");
+ color_set(opt->color_lineno, "");
+ color_set(opt->color_match_context, GIT_COLOR_BOLD_RED);
+ color_set(opt->color_match_selected, GIT_COLOR_BOLD_RED);
+ color_set(opt->color_selected, "");
+ color_set(opt->color_sep, GIT_COLOR_CYAN);
opt->color = -1;
}
@@ -151,14 +151,14 @@ void grep_init(struct grep_opt *opt, const char *prefix)
opt->regflags = def->regflags;
opt->relative = def->relative;
- strcpy(opt->color_context, def->color_context);
- strcpy(opt->color_filename, def->color_filename);
- strcpy(opt->color_function, def->color_function);
- strcpy(opt->color_lineno, def->color_lineno);
- strcpy(opt->color_match_context, def->color_match_context);
- strcpy(opt->color_match_selected, def->color_match_selected);
- strcpy(opt->color_selected, def->color_selected);
- strcpy(opt->color_sep, def->color_sep);
+ color_set(opt->color_context, def->color_context);
+ color_set(opt->color_filename, def->color_filename);
+ color_set(opt->color_function, def->color_function);
+ color_set(opt->color_lineno, def->color_lineno);
+ color_set(opt->color_match_context, def->color_match_context);
+ color_set(opt->color_match_selected, def->color_match_selected);
+ color_set(opt->color_selected, def->color_selected);
+ color_set(opt->color_sep, def->color_sep);
}
void grep_commit_pattern_type(enum grep_pattern_type pattern_type, struct grep_opt *opt)
@@ -306,9 +306,9 @@ static NORETURN void compile_regexp_failed(const struct grep_pat *p,
char where[1024];
if (p->no)
- sprintf(where, "In '%s' at %d, ", p->origin, p->no);
+ xsnprintf(where, sizeof(where), "In '%s' at %d, ", p->origin, p->no);
else if (p->origin)
- sprintf(where, "%s, ", p->origin);
+ xsnprintf(where, sizeof(where), "%s, ", p->origin);
else
where[0] = 0;
diff --git a/hex.c b/hex.c
index 899b74a08c..0519f853b2 100644
--- a/hex.c
+++ b/hex.c
@@ -61,12 +61,10 @@ int get_oid_hex(const char *hex, struct object_id *oid)
return get_sha1_hex(hex, oid->hash);
}
-char *sha1_to_hex(const unsigned char *sha1)
+char *sha1_to_hex_r(char *buffer, const unsigned char *sha1)
{
- static int bufno;
- static char hexbuffer[4][GIT_SHA1_HEXSZ + 1];
static const char hex[] = "0123456789abcdef";
- char *buffer = hexbuffer[3 & ++bufno], *buf = buffer;
+ char *buf = buffer;
int i;
for (i = 0; i < GIT_SHA1_RAWSZ; i++) {
@@ -79,6 +77,13 @@ char *sha1_to_hex(const unsigned char *sha1)
return buffer;
}
+char *sha1_to_hex(const unsigned char *sha1)
+{
+ static int bufno;
+ static char hexbuffer[4][GIT_SHA1_HEXSZ + 1];
+ return sha1_to_hex_r(hexbuffer[3 & ++bufno], sha1);
+}
+
char *oid_to_hex(const struct object_id *oid)
{
return sha1_to_hex(oid->hash);
diff --git a/http-push.c b/http-push.c
index c98dad23df..48f39b7f71 100644
--- a/http-push.c
+++ b/http-push.c
@@ -10,6 +10,7 @@
#include "remote.h"
#include "list-objects.h"
#include "sigchain.h"
+#include "argv-array.h"
#ifdef EXPAT_NEEDS_XMLPARSE_H
#include <xmlparse.h>
@@ -361,7 +362,7 @@ static void start_put(struct transfer_request *request)
git_zstream stream;
unpacked = read_sha1_file(request->obj->sha1, &type, &len);
- hdrlen = sprintf(hdr, "%s %lu", typename(type), len) + 1;
+ hdrlen = xsnprintf(hdr, sizeof(hdr), "%s %lu", typename(type), len) + 1;
/* Set it up */
git_deflate_init(&stream, zlib_compression_level);
@@ -786,21 +787,21 @@ xml_start_tag(void *userData, const char *name, const char **atts)
{
struct xml_ctx *ctx = (struct xml_ctx *)userData;
const char *c = strchr(name, ':');
- int new_len;
+ int old_namelen, new_len;
if (c == NULL)
c = name;
else
c++;
- new_len = strlen(ctx->name) + strlen(c) + 2;
+ old_namelen = strlen(ctx->name);
+ new_len = old_namelen + strlen(c) + 2;
if (new_len > ctx->len) {
ctx->name = xrealloc(ctx->name, new_len);
ctx->len = new_len;
}
- strcat(ctx->name, ".");
- strcat(ctx->name, c);
+ xsnprintf(ctx->name + old_namelen, ctx->len - old_namelen, ".%s", c);
free(ctx->cdata);
ctx->cdata = NULL;
@@ -881,7 +882,7 @@ static struct remote_lock *lock_remote(const char *path, long timeout)
strbuf_addf(&out_buffer.buf, LOCK_REQUEST, escaped);
free(escaped);
- sprintf(timeout_header, "Timeout: Second-%ld", timeout);
+ xsnprintf(timeout_header, sizeof(timeout_header), "Timeout: Second-%ld", timeout);
dav_headers = curl_slist_append(dav_headers, timeout_header);
dav_headers = curl_slist_append(dav_headers, "Content-Type: text/xml");
@@ -1459,8 +1460,6 @@ static void add_remote_info_ref(struct remote_ls_ctx *ls)
{
struct strbuf *buf = (struct strbuf *)ls->userData;
struct object *o;
- int len;
- char *ref_info;
struct ref *ref;
ref = alloc_ref(ls->dentry_name);
@@ -1484,23 +1483,14 @@ static void add_remote_info_ref(struct remote_ls_ctx *ls)
return;
}
- len = strlen(ls->dentry_name) + 42;
- ref_info = xcalloc(len + 1, 1);
- sprintf(ref_info, "%s %s\n",
- sha1_to_hex(ref->old_sha1), ls->dentry_name);
- fwrite_buffer(ref_info, 1, len, buf);
- free(ref_info);
+ strbuf_addf(buf, "%s\t%s\n",
+ sha1_to_hex(ref->old_sha1), ls->dentry_name);
if (o->type == OBJ_TAG) {
o = deref_tag(o, ls->dentry_name, 0);
- if (o) {
- len = strlen(ls->dentry_name) + 45;
- ref_info = xcalloc(len + 1, 1);
- sprintf(ref_info, "%s %s^{}\n",
- sha1_to_hex(o->sha1), ls->dentry_name);
- fwrite_buffer(ref_info, 1, len, buf);
- free(ref_info);
- }
+ if (o)
+ strbuf_addf(buf, "%s\t%s^{}\n",
+ sha1_to_hex(o->sha1), ls->dentry_name);
}
free(ref);
}
@@ -1866,10 +1856,7 @@ int main(int argc, char **argv)
new_refs = 0;
for (ref = remote_refs; ref; ref = ref->next) {
- char old_hex[60], *new_hex;
- const char *commit_argv[5];
- int commit_argc;
- char *new_sha1_hex, *old_sha1_hex;
+ struct argv_array commit_argv = ARGV_ARRAY_INIT;
if (!ref->peer_ref)
continue;
@@ -1923,13 +1910,12 @@ int main(int argc, char **argv)
}
hashcpy(ref->new_sha1, ref->peer_ref->new_sha1);
new_refs++;
- strcpy(old_hex, sha1_to_hex(ref->old_sha1));
- new_hex = sha1_to_hex(ref->new_sha1);
fprintf(stderr, "updating '%s'", ref->name);
if (strcmp(ref->name, ref->peer_ref->name))
fprintf(stderr, " using '%s'", ref->peer_ref->name);
- fprintf(stderr, "\n from %s\n to %s\n", old_hex, new_hex);
+ fprintf(stderr, "\n from %s\n to %s\n",
+ sha1_to_hex(ref->old_sha1), sha1_to_hex(ref->new_sha1));
if (dry_run) {
if (helper_status)
printf("ok %s\n", ref->name);
@@ -1948,27 +1934,15 @@ int main(int argc, char **argv)
}
/* Set up revision info for this refspec */
- commit_argc = 3;
- new_sha1_hex = xstrdup(sha1_to_hex(ref->new_sha1));
- old_sha1_hex = NULL;
- commit_argv[1] = "--objects";
- commit_argv[2] = new_sha1_hex;
- if (!push_all && !is_null_sha1(ref->old_sha1)) {
- old_sha1_hex = xmalloc(42);
- sprintf(old_sha1_hex, "^%s",
- sha1_to_hex(ref->old_sha1));
- commit_argv[3] = old_sha1_hex;
- commit_argc++;
- }
- commit_argv[commit_argc] = NULL;
+ argv_array_push(&commit_argv, ""); /* ignored */
+ argv_array_push(&commit_argv, "--objects");
+ argv_array_push(&commit_argv, sha1_to_hex(ref->new_sha1));
+ if (!push_all && !is_null_sha1(ref->old_sha1))
+ argv_array_pushf(&commit_argv, "^%s",
+ sha1_to_hex(ref->old_sha1));
init_revisions(&revs, setup_git_directory());
- setup_revisions(commit_argc, commit_argv, &revs, NULL);
+ setup_revisions(commit_argv.argc, commit_argv.argv, &revs, NULL);
revs.edge_hint = 0; /* just in case */
- free(new_sha1_hex);
- if (old_sha1_hex) {
- free(old_sha1_hex);
- commit_argv[1] = NULL;
- }
/* Generate a list of objects that need to be pushed */
pushing = 0;
@@ -1997,6 +1971,7 @@ int main(int argc, char **argv)
printf("%s %s\n", !rc ? "ok" : "error", ref->name);
unlock_remote(ref_lock);
check_locks();
+ argv_array_clear(&commit_argv);
}
/* Update remote server info if appropriate */
diff --git a/http-walker.c b/http-walker.c
index 88da5468e7..2c721f0c30 100644
--- a/http-walker.c
+++ b/http-walker.c
@@ -29,7 +29,7 @@ struct object_request {
struct alternates_request {
struct walker *walker;
const char *base;
- char *url;
+ struct strbuf *url;
struct strbuf *buffer;
struct active_request_slot *slot;
int http_specific;
@@ -195,10 +195,11 @@ static void process_alternates_response(void *callback_data)
/* Try reusing the slot to get non-http alternates */
alt_req->http_specific = 0;
- sprintf(alt_req->url, "%s/objects/info/alternates",
- base);
+ strbuf_reset(alt_req->url);
+ strbuf_addf(alt_req->url, "%s/objects/info/alternates",
+ base);
curl_easy_setopt(slot->curl, CURLOPT_URL,
- alt_req->url);
+ alt_req->url->buf);
active_requests++;
slot->in_use = 1;
if (slot->finished != NULL)
@@ -312,7 +313,7 @@ static void process_alternates_response(void *callback_data)
static void fetch_alternates(struct walker *walker, const char *base)
{
struct strbuf buffer = STRBUF_INIT;
- char *url;
+ struct strbuf url = STRBUF_INIT;
struct active_request_slot *slot;
struct alternates_request alt_req;
struct walker_data *cdata = walker->data;
@@ -338,7 +339,7 @@ static void fetch_alternates(struct walker *walker, const char *base)
if (walker->get_verbosely)
fprintf(stderr, "Getting alternates list for %s\n", base);
- url = xstrfmt("%s/objects/info/http-alternates", base);
+ strbuf_addf(&url, "%s/objects/info/http-alternates", base);
/*
* Use a callback to process the result, since another request
@@ -351,10 +352,10 @@ static void fetch_alternates(struct walker *walker, const char *base)
curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);
curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
- curl_easy_setopt(slot->curl, CURLOPT_URL, url);
+ curl_easy_setopt(slot->curl, CURLOPT_URL, url.buf);
alt_req.base = base;
- alt_req.url = url;
+ alt_req.url = &url;
alt_req.buffer = &buffer;
alt_req.http_specific = 1;
alt_req.slot = slot;
@@ -365,7 +366,7 @@ static void fetch_alternates(struct walker *walker, const char *base)
cdata->got_alternates = -1;
strbuf_release(&buffer);
- free(url);
+ strbuf_release(&url);
}
static int fetch_indices(struct walker *walker, struct alt_base *repo)
diff --git a/http.c b/http.c
index 0f924a8b48..7da76edda1 100644
--- a/http.c
+++ b/http.c
@@ -1122,7 +1122,7 @@ static void write_accept_language(struct strbuf *buf)
decimal_places++, max_q *= 10)
;
- sprintf(q_format, ";q=0.%%0%dd", decimal_places);
+ xsnprintf(q_format, sizeof(q_format), ";q=0.%%0%dd", decimal_places);
strbuf_addstr(buf, "Accept-Language: ");
@@ -1529,6 +1529,7 @@ int finish_http_pack_request(struct http_pack_request *preq)
struct packed_git **lst;
struct packed_git *p = preq->target;
char *tmp_idx;
+ size_t len;
struct child_process ip = CHILD_PROCESS_INIT;
const char *ip_argv[8];
@@ -1542,9 +1543,9 @@ int finish_http_pack_request(struct http_pack_request *preq)
lst = &((*lst)->next);
*lst = (*lst)->next;
- tmp_idx = xstrdup(preq->tmpfile);
- strcpy(tmp_idx + strlen(tmp_idx) - strlen(".pack.temp"),
- ".idx.temp");
+ if (!strip_suffix(preq->tmpfile, ".pack.temp", &len))
+ die("BUG: pack tmpfile does not end in .pack.temp?");
+ tmp_idx = xstrfmt("%.*s.idx.temp", (int)len, preq->tmpfile);
ip_argv[0] = "index-pack";
ip_argv[1] = "-o";
@@ -1619,7 +1620,7 @@ struct http_pack_request *new_http_pack_request(
fprintf(stderr,
"Resuming fetch of pack %s at byte %ld\n",
sha1_to_hex(target->sha1), prev_posn);
- sprintf(range, "Range: bytes=%ld-", prev_posn);
+ xsnprintf(range, sizeof(range), "Range: bytes=%ld-", prev_posn);
preq->range_header = curl_slist_append(NULL, range);
curl_easy_setopt(preq->slot->curl, CURLOPT_HTTPHEADER,
preq->range_header);
@@ -1779,7 +1780,7 @@ struct http_object_request *new_http_object_request(const char *base_url,
fprintf(stderr,
"Resuming fetch of object %s at byte %ld\n",
hex, prev_posn);
- sprintf(range, "Range: bytes=%ld-", prev_posn);
+ xsnprintf(range, sizeof(range), "Range: bytes=%ld-", prev_posn);
range_header = curl_slist_append(range_header, range);
curl_easy_setopt(freq->slot->curl,
CURLOPT_HTTPHEADER, range_header);
diff --git a/imap-send.c b/imap-send.c
index 37ac4aa86a..e9faaeaf2a 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -889,9 +889,8 @@ static char *cram(const char *challenge_64, const char *user, const char *pass)
}
/* response: "<user> <digest in hex>" */
- resp_len = strlen(user) + 1 + strlen(hex) + 1;
- response = xmalloc(resp_len);
- sprintf(response, "%s %s", user, hex);
+ response = xstrfmt("%s %s", user, hex);
+ resp_len = strlen(response) + 1;
response_64 = xmalloc(ENCODED_SIZE(resp_len) + 1);
encoded_len = EVP_EncodeBlock((unsigned char *)response_64,
diff --git a/ll-merge.c b/ll-merge.c
index bf83290793..0338630fc2 100644
--- a/ll-merge.c
+++ b/ll-merge.c
@@ -145,11 +145,11 @@ static struct ll_merge_driver ll_merge_drv[] = {
{ "union", "built-in union merge", ll_union_merge },
};
-static void create_temp(mmfile_t *src, char *path)
+static void create_temp(mmfile_t *src, char *path, size_t len)
{
int fd;
- strcpy(path, ".merge_file_XXXXXX");
+ xsnprintf(path, len, ".merge_file_XXXXXX");
fd = xmkstemp(path);
if (write_in_full(fd, src->ptr, src->size) != src->size)
die_errno("unable to write temp-file");
@@ -190,10 +190,10 @@ static int ll_ext_merge(const struct ll_merge_driver *fn,
result->ptr = NULL;
result->size = 0;
- create_temp(orig, temp[0]);
- create_temp(src1, temp[1]);
- create_temp(src2, temp[2]);
- sprintf(temp[3], "%d", marker_size);
+ create_temp(orig, temp[0], sizeof(temp[0]));
+ create_temp(src1, temp[1], sizeof(temp[1]));
+ create_temp(src2, temp[2], sizeof(temp[2]));
+ xsnprintf(temp[3], sizeof(temp[3]), "%d", marker_size);
strbuf_expand(&cmd, fn->cmdline, strbuf_expand_dict_cb, &dict);
diff --git a/mailmap.c b/mailmap.c
index 9e9589730f..f4a0f1cf27 100644
--- a/mailmap.c
+++ b/mailmap.c
@@ -162,11 +162,10 @@ static void read_mailmap_line(struct string_list *map, char *buffer,
char *cp;
free(*repo_abbrev);
- *repo_abbrev = xmalloc(len);
for (cp = buffer + abblen; isspace(*cp); cp++)
; /* nothing */
- strcpy(*repo_abbrev, cp);
+ *repo_abbrev = xstrdup(cp);
}
return;
}
diff --git a/merge-recursive.c b/merge-recursive.c
index 44d85bea4b..a5e74d85fd 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -630,25 +630,24 @@ static char *unique_path(struct merge_options *o, const char *path, const char *
static int dir_in_way(const char *path, int check_working_copy)
{
- int pos, pathlen = strlen(path);
- char *dirpath = xmalloc(pathlen + 2);
+ int pos;
+ struct strbuf dirpath = STRBUF_INIT;
struct stat st;
- strcpy(dirpath, path);
- dirpath[pathlen] = '/';
- dirpath[pathlen+1] = '\0';
+ strbuf_addstr(&dirpath, path);
+ strbuf_addch(&dirpath, '/');
- pos = cache_name_pos(dirpath, pathlen+1);
+ pos = cache_name_pos(dirpath.buf, dirpath.len);
if (pos < 0)
pos = -1 - pos;
if (pos < active_nr &&
- !strncmp(dirpath, active_cache[pos]->name, pathlen+1)) {
- free(dirpath);
+ !strncmp(dirpath.buf, active_cache[pos]->name, dirpath.len)) {
+ strbuf_release(&dirpath);
return 1;
}
- free(dirpath);
+ strbuf_release(&dirpath);
return check_working_copy && !lstat(path, &st) && S_ISDIR(st.st_mode);
}
diff --git a/notes.c b/notes.c
index eacd2a61da..db77922130 100644
--- a/notes.c
+++ b/notes.c
@@ -539,6 +539,9 @@ static unsigned char determine_fanout(struct int_node *tree, unsigned char n,
return fanout + 1;
}
+/* hex SHA1 + 19 * '/' + NUL */
+#define FANOUT_PATH_MAX 40 + 19 + 1
+
static void construct_path_with_fanout(const unsigned char *sha1,
unsigned char fanout, char *path)
{
@@ -551,7 +554,7 @@ static void construct_path_with_fanout(const unsigned char *sha1,
path[i++] = '/';
fanout--;
}
- strcpy(path + i, hex_sha1 + j);
+ xsnprintf(path + i, FANOUT_PATH_MAX - i, "%s", hex_sha1 + j);
}
static int for_each_note_helper(struct notes_tree *t, struct int_node *tree,
@@ -562,7 +565,7 @@ static int for_each_note_helper(struct notes_tree *t, struct int_node *tree,
void *p;
int ret = 0;
struct leaf_node *l;
- static char path[40 + 19 + 1]; /* hex SHA1 + 19 * '/' + NUL */
+ static char path[FANOUT_PATH_MAX];
fanout = determine_fanout(tree, n, fanout);
for (i = 0; i < 16; i++) {
@@ -595,7 +598,7 @@ redo:
/* invoke callback with subtree */
unsigned int path_len =
l->key_sha1[19] * 2 + fanout;
- assert(path_len < 40 + 19);
+ assert(path_len < FANOUT_PATH_MAX - 1);
construct_path_with_fanout(l->key_sha1, fanout,
path);
/* Create trailing slash, if needed */
diff --git a/pack-bitmap.c b/pack-bitmap.c
index 637770af81..7dfcb341d6 100644
--- a/pack-bitmap.c
+++ b/pack-bitmap.c
@@ -252,16 +252,11 @@ static int load_bitmap_entries_v1(struct bitmap_index *index)
static char *pack_bitmap_filename(struct packed_git *p)
{
- char *idx_name;
- int len;
-
- len = strlen(p->pack_name) - strlen(".pack");
- idx_name = xmalloc(len + strlen(".bitmap") + 1);
-
- memcpy(idx_name, p->pack_name, len);
- memcpy(idx_name + len, ".bitmap", strlen(".bitmap") + 1);
+ size_t len;
- return idx_name;
+ if (!strip_suffix(p->pack_name, ".pack", &len))
+ die("BUG: pack_name does not end in .pack");
+ return xstrfmt("%.*s.bitmap", (int)len, p->pack_name);
}
static int open_pack_bitmap_1(struct packed_git *packfile)
diff --git a/parse-options-cb.c b/parse-options-cb.c
index 5ab6ed6b08..239898d946 100644
--- a/parse-options-cb.c
+++ b/parse-options-cb.c
@@ -5,6 +5,7 @@
#include "color.h"
#include "string-list.h"
#include "argv-array.h"
+#include "sha1-array.h"
/*----- some often used options -----*/
@@ -77,7 +78,7 @@ int parse_opt_verbosity_cb(const struct option *opt, const char *arg,
return 0;
}
-int parse_opt_with_commit(const struct option *opt, const char *arg, int unset)
+int parse_opt_commits(const struct option *opt, const char *arg, int unset)
{
unsigned char sha1[20];
struct commit *commit;
@@ -93,6 +94,22 @@ int parse_opt_with_commit(const struct option *opt, const char *arg, int unset)
return 0;
}
+int parse_opt_object_name(const struct option *opt, const char *arg, int unset)
+{
+ unsigned char sha1[20];
+
+ if (unset) {
+ sha1_array_clear(opt->value);
+ return 0;
+ }
+ if (!arg)
+ return -1;
+ if (get_sha1(arg, sha1))
+ return error(_("malformed object name '%s'"), arg);
+ sha1_array_append(opt->value, sha1);
+ return 0;
+}
+
int parse_opt_tertiary(const struct option *opt, const char *arg, int unset)
{
int *target = opt->value;
diff --git a/parse-options.h b/parse-options.h
index 3f1cc3aee0..e8b55ea87a 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -223,7 +223,8 @@ extern int parse_opt_approxidate_cb(const struct option *, const char *, int);
extern int parse_opt_expiry_date_cb(const struct option *, const char *, int);
extern int parse_opt_color_flag_cb(const struct option *, const char *, int);
extern int parse_opt_verbosity_cb(const struct option *, const char *, int);
-extern int parse_opt_with_commit(const struct option *, const char *, int);
+extern int parse_opt_object_name(const struct option *, const char *, int);
+extern int parse_opt_commits(const struct option *, const char *, int);
extern int parse_opt_tertiary(const struct option *, const char *, int);
extern int parse_opt_string_list(const struct option *, const char *, int);
extern int parse_opt_noop_cb(const struct option *, const char *, int);
@@ -251,5 +252,12 @@ extern int parse_opt_passthru_argv(const struct option *, const char *, int);
{ OPTION_CALLBACK, (s), (l), (v), (a), (h), (f), parse_opt_passthru }
#define OPT_PASSTHRU_ARGV(s, l, v, a, h, f) \
{ OPTION_CALLBACK, (s), (l), (v), (a), (h), (f), parse_opt_passthru_argv }
+#define _OPT_CONTAINS_OR_WITH(name, variable, help, flag) \
+ { OPTION_CALLBACK, 0, name, (variable), N_("commit"), (help), \
+ PARSE_OPT_LASTARG_DEFAULT | flag, \
+ parse_opt_commits, (intptr_t) "HEAD" \
+ }
+#define OPT_CONTAINS(v, h) _OPT_CONTAINS_OR_WITH("contains", v, h, 0)
+#define OPT_WITH(v, h) _OPT_CONTAINS_OR_WITH("with", v, h, PARSE_OPT_HIDDEN)
#endif
diff --git a/path.c b/path.c
index 95acbafa68..c740c4ff94 100644
--- a/path.c
+++ b/path.c
@@ -91,54 +91,274 @@ static void replace_dir(struct strbuf *buf, int len, const char *newdir)
buf->buf[newlen] = '/';
}
-static const char *common_list[] = {
- "/branches", "/hooks", "/info", "!/logs", "/lost-found",
- "/objects", "/refs", "/remotes", "/worktrees", "/rr-cache", "/svn",
- "config", "!gc.pid", "packed-refs", "shallow",
- NULL
+struct common_dir {
+ /* Not considered garbage for report_linked_checkout_garbage */
+ unsigned ignore_garbage:1;
+ unsigned is_dir:1;
+ /* Not common even though its parent is */
+ unsigned exclude:1;
+ const char *dirname;
};
-static void update_common_dir(struct strbuf *buf, int git_dir_len)
+static struct common_dir common_list[] = {
+ { 0, 1, 0, "branches" },
+ { 0, 1, 0, "hooks" },
+ { 0, 1, 0, "info" },
+ { 0, 0, 1, "info/sparse-checkout" },
+ { 1, 1, 0, "logs" },
+ { 1, 1, 1, "logs/HEAD" },
+ { 0, 1, 1, "logs/refs/bisect" },
+ { 0, 1, 0, "lost-found" },
+ { 0, 1, 0, "objects" },
+ { 0, 1, 0, "refs" },
+ { 0, 1, 1, "refs/bisect" },
+ { 0, 1, 0, "remotes" },
+ { 0, 1, 0, "worktrees" },
+ { 0, 1, 0, "rr-cache" },
+ { 0, 1, 0, "svn" },
+ { 0, 0, 0, "config" },
+ { 1, 0, 0, "gc.pid" },
+ { 0, 0, 0, "packed-refs" },
+ { 0, 0, 0, "shallow" },
+ { 0, 0, 0, NULL }
+};
+
+/*
+ * A compressed trie. A trie node consists of zero or more characters that
+ * are common to all elements with this prefix, optionally followed by some
+ * children. If value is not NULL, the trie node is a terminal node.
+ *
+ * For example, consider the following set of strings:
+ * abc
+ * def
+ * definite
+ * definition
+ *
+ * The trie would look look like:
+ * root: len = 0, children a and d non-NULL, value = NULL.
+ * a: len = 2, contents = bc, value = (data for "abc")
+ * d: len = 2, contents = ef, children i non-NULL, value = (data for "def")
+ * i: len = 3, contents = nit, children e and i non-NULL, value = NULL
+ * e: len = 0, children all NULL, value = (data for "definite")
+ * i: len = 2, contents = on, children all NULL,
+ * value = (data for "definition")
+ */
+struct trie {
+ struct trie *children[256];
+ int len;
+ char *contents;
+ void *value;
+};
+
+static struct trie *make_trie_node(const char *key, void *value)
{
- char *base = buf->buf + git_dir_len;
- const char **p;
-
- if (is_dir_file(base, "logs", "HEAD") ||
- is_dir_file(base, "info", "sparse-checkout"))
- return; /* keep this in $GIT_DIR */
- for (p = common_list; *p; p++) {
- const char *path = *p;
- int is_dir = 0;
- if (*path == '!')
- path++;
- if (*path == '/') {
- path++;
- is_dir = 1;
+ struct trie *new_node = xcalloc(1, sizeof(*new_node));
+ new_node->len = strlen(key);
+ if (new_node->len) {
+ new_node->contents = xmalloc(new_node->len);
+ memcpy(new_node->contents, key, new_node->len);
+ }
+ new_node->value = value;
+ return new_node;
+}
+
+/*
+ * Add a key/value pair to a trie. The key is assumed to be \0-terminated.
+ * If there was an existing value for this key, return it.
+ */
+static void *add_to_trie(struct trie *root, const char *key, void *value)
+{
+ struct trie *child;
+ void *old;
+ int i;
+
+ if (!*key) {
+ /* we have reached the end of the key */
+ old = root->value;
+ root->value = value;
+ return old;
+ }
+
+ for (i = 0; i < root->len; i++) {
+ if (root->contents[i] == key[i])
+ continue;
+
+ /*
+ * Split this node: child will contain this node's
+ * existing children.
+ */
+ child = malloc(sizeof(*child));
+ memcpy(child->children, root->children, sizeof(root->children));
+
+ child->len = root->len - i - 1;
+ if (child->len) {
+ child->contents = xstrndup(root->contents + i + 1,
+ child->len);
}
- if (is_dir && dir_prefix(base, path)) {
- replace_dir(buf, git_dir_len, get_git_common_dir());
- return;
+ child->value = root->value;
+ root->value = NULL;
+ root->len = i;
+
+ memset(root->children, 0, sizeof(root->children));
+ root->children[(unsigned char)root->contents[i]] = child;
+
+ /* This is the newly-added child. */
+ root->children[(unsigned char)key[i]] =
+ make_trie_node(key + i + 1, value);
+ return NULL;
+ }
+
+ /* We have matched the entire compressed section */
+ if (key[i]) {
+ child = root->children[(unsigned char)key[root->len]];
+ if (child) {
+ return add_to_trie(child, key + root->len + 1, value);
+ } else {
+ child = make_trie_node(key + root->len + 1, value);
+ root->children[(unsigned char)key[root->len]] = child;
+ return NULL;
}
- if (!is_dir && !strcmp(base, path)) {
- replace_dir(buf, git_dir_len, get_git_common_dir());
- return;
+ }
+
+ old = root->value;
+ root->value = value;
+ return old;
+}
+
+typedef int (*match_fn)(const char *unmatched, void *data, void *baton);
+
+/*
+ * Search a trie for some key. Find the longest /-or-\0-terminated
+ * prefix of the key for which the trie contains a value. Call fn
+ * with the unmatched portion of the key and the found value, and
+ * return its return value. If there is no such prefix, return -1.
+ *
+ * The key is partially normalized: consecutive slashes are skipped.
+ *
+ * For example, consider the trie containing only [refs,
+ * refs/worktree] (both with values).
+ *
+ * | key | unmatched | val from node | return value |
+ * |-----------------|------------|---------------|--------------|
+ * | a | not called | n/a | -1 |
+ * | refs | \0 | refs | as per fn |
+ * | refs/ | / | refs | as per fn |
+ * | refs/w | /w | refs | as per fn |
+ * | refs/worktree | \0 | refs/worktree | as per fn |
+ * | refs/worktree/ | / | refs/worktree | as per fn |
+ * | refs/worktree/a | /a | refs/worktree | as per fn |
+ * |-----------------|------------|---------------|--------------|
+ *
+ */
+static int trie_find(struct trie *root, const char *key, match_fn fn,
+ void *baton)
+{
+ int i;
+ int result;
+ struct trie *child;
+
+ if (!*key) {
+ /* we have reached the end of the key */
+ if (root->value && !root->len)
+ return fn(key, root->value, baton);
+ else
+ return -1;
+ }
+
+ for (i = 0; i < root->len; i++) {
+ /* Partial path normalization: skip consecutive slashes. */
+ if (key[i] == '/' && key[i+1] == '/') {
+ key++;
+ continue;
}
+ if (root->contents[i] != key[i])
+ return -1;
}
+
+ /* Matched the entire compressed section */
+ key += i;
+ if (!*key)
+ /* End of key */
+ return fn(key, root->value, baton);
+
+ /* Partial path normalization: skip consecutive slashes */
+ while (key[0] == '/' && key[1] == '/')
+ key++;
+
+ child = root->children[(unsigned char)*key];
+ if (child)
+ result = trie_find(child, key + 1, fn, baton);
+ else
+ result = -1;
+
+ if (result >= 0 || (*key != '/' && *key != 0))
+ return result;
+ if (root->value)
+ return fn(key, root->value, baton);
+ else
+ return -1;
+}
+
+static struct trie common_trie;
+static int common_trie_done_setup;
+
+static void init_common_trie(void)
+{
+ struct common_dir *p;
+
+ if (common_trie_done_setup)
+ return;
+
+ for (p = common_list; p->dirname; p++)
+ add_to_trie(&common_trie, p->dirname, p);
+
+ common_trie_done_setup = 1;
+}
+
+/*
+ * Helper function for update_common_dir: returns 1 if the dir
+ * prefix is common.
+ */
+static int check_common(const char *unmatched, void *value, void *baton)
+{
+ struct common_dir *dir = value;
+
+ if (!dir)
+ return 0;
+
+ if (dir->is_dir && (unmatched[0] == 0 || unmatched[0] == '/'))
+ return !dir->exclude;
+
+ if (!dir->is_dir && unmatched[0] == 0)
+ return !dir->exclude;
+
+ return 0;
+}
+
+static void update_common_dir(struct strbuf *buf, int git_dir_len,
+ const char *common_dir)
+{
+ char *base = buf->buf + git_dir_len;
+ init_common_trie();
+ if (!common_dir)
+ common_dir = get_git_common_dir();
+ if (trie_find(&common_trie, base, check_common, NULL) > 0)
+ replace_dir(buf, git_dir_len, common_dir);
}
void report_linked_checkout_garbage(void)
{
struct strbuf sb = STRBUF_INIT;
- const char **p;
+ const struct common_dir *p;
int len;
if (!git_common_dir_env)
return;
strbuf_addf(&sb, "%s/", get_git_dir());
len = sb.len;
- for (p = common_list; *p; p++) {
- const char *path = *p;
- if (*path == '!')
+ for (p = common_list; p->dirname; p++) {
+ const char *path = p->dirname;
+ if (p->ignore_garbage)
continue;
strbuf_setlen(&sb, len);
strbuf_addstr(&sb, path);
@@ -160,7 +380,7 @@ static void adjust_git_path(struct strbuf *buf, int git_dir_len)
else if (git_db_env && dir_prefix(base, "objects"))
replace_dir(buf, git_dir_len + 7, get_object_directory());
else if (git_common_dir_env)
- update_common_dir(buf, git_dir_len);
+ update_common_dir(buf, git_dir_len, NULL);
}
static void do_git_path(struct strbuf *buf, const char *fmt, va_list args)
@@ -175,6 +395,16 @@ static void do_git_path(struct strbuf *buf, const char *fmt, va_list args)
strbuf_cleanup_path(buf);
}
+char *git_path_buf(struct strbuf *buf, const char *fmt, ...)
+{
+ va_list args;
+ strbuf_reset(buf);
+ va_start(args, fmt);
+ do_git_path(buf, fmt, args);
+ va_end(args);
+ return buf->buf;
+}
+
void strbuf_git_path(struct strbuf *sb, const char *fmt, ...)
{
va_list args;
@@ -228,10 +458,11 @@ static void do_submodule_path(struct strbuf *buf, const char *path,
const char *fmt, va_list args)
{
const char *git_dir;
+ struct strbuf git_submodule_common_dir = STRBUF_INIT;
+ struct strbuf git_submodule_dir = STRBUF_INIT;
strbuf_addstr(buf, path);
- if (buf->len && buf->buf[buf->len - 1] != '/')
- strbuf_addch(buf, '/');
+ strbuf_complete(buf, '/');
strbuf_addstr(buf, ".git");
git_dir = read_gitfile(buf->buf);
@@ -240,9 +471,17 @@ static void do_submodule_path(struct strbuf *buf, const char *path,
strbuf_addstr(buf, git_dir);
}
strbuf_addch(buf, '/');
+ strbuf_addstr(&git_submodule_dir, buf->buf);
strbuf_vaddf(buf, fmt, args);
+
+ if (get_common_dir_noenv(&git_submodule_common_dir, git_submodule_dir.buf))
+ update_common_dir(buf, git_submodule_dir.len, git_submodule_common_dir.buf);
+
strbuf_cleanup_path(buf);
+
+ strbuf_release(&git_submodule_dir);
+ strbuf_release(&git_submodule_common_dir);
}
char *git_pathdup_submodule(const char *path, const char *fmt, ...)
@@ -381,8 +620,8 @@ return_null:
*/
const char *enter_repo(const char *path, int strict)
{
- static char used_path[PATH_MAX];
- static char validated_path[PATH_MAX];
+ static struct strbuf validated_path = STRBUF_INIT;
+ static struct strbuf used_path = STRBUF_INIT;
if (!path)
return NULL;
@@ -397,52 +636,57 @@ const char *enter_repo(const char *path, int strict)
while ((1 < len) && (path[len-1] == '/'))
len--;
+ /*
+ * We can handle arbitrary-sized buffers, but this remains as a
+ * sanity check on untrusted input.
+ */
if (PATH_MAX <= len)
return NULL;
- strncpy(used_path, path, len); used_path[len] = 0 ;
- strcpy(validated_path, used_path);
- if (used_path[0] == '~') {
- char *newpath = expand_user_path(used_path);
- if (!newpath || (PATH_MAX - 10 < strlen(newpath))) {
- free(newpath);
+ strbuf_reset(&used_path);
+ strbuf_reset(&validated_path);
+ strbuf_add(&used_path, path, len);
+ strbuf_add(&validated_path, path, len);
+
+ if (used_path.buf[0] == '~') {
+ char *newpath = expand_user_path(used_path.buf);
+ if (!newpath)
return NULL;
- }
- /*
- * Copy back into the static buffer. A pity
- * since newpath was not bounded, but other
- * branches of the if are limited by PATH_MAX
- * anyway.
- */
- strcpy(used_path, newpath); free(newpath);
+ strbuf_attach(&used_path, newpath, strlen(newpath),
+ strlen(newpath));
}
- else if (PATH_MAX - 10 < len)
- return NULL;
- len = strlen(used_path);
for (i = 0; suffix[i]; i++) {
struct stat st;
- strcpy(used_path + len, suffix[i]);
- if (!stat(used_path, &st) &&
+ size_t baselen = used_path.len;
+ strbuf_addstr(&used_path, suffix[i]);
+ if (!stat(used_path.buf, &st) &&
(S_ISREG(st.st_mode) ||
- (S_ISDIR(st.st_mode) && is_git_directory(used_path)))) {
- strcat(validated_path, suffix[i]);
+ (S_ISDIR(st.st_mode) && is_git_directory(used_path.buf)))) {
+ strbuf_addstr(&validated_path, suffix[i]);
break;
}
+ strbuf_setlen(&used_path, baselen);
}
if (!suffix[i])
return NULL;
- gitfile = read_gitfile(used_path) ;
+ gitfile = read_gitfile(used_path.buf);
+ if (gitfile) {
+ strbuf_reset(&used_path);
+ strbuf_addstr(&used_path, gitfile);
+ }
+ if (chdir(used_path.buf))
+ return NULL;
+ path = validated_path.buf;
+ }
+ else {
+ const char *gitfile = read_gitfile(path);
if (gitfile)
- strcpy(used_path, gitfile);
- if (chdir(used_path))
+ path = gitfile;
+ if (chdir(path))
return NULL;
- path = validated_path;
}
- else if (chdir(path))
- return NULL;
- if (access("objects", X_OK) == 0 && access("refs", X_OK) == 0 &&
- validate_headref("HEAD") == 0) {
+ if (is_git_directory(".")) {
set_git_dir(".");
check_repository_format();
return path;
@@ -621,7 +865,7 @@ const char *relative_path(const char *in, const char *prefix,
*/
const char *remove_leading_path(const char *in, const char *prefix)
{
- static char buf[PATH_MAX + 1];
+ static struct strbuf buf = STRBUF_INIT;
int i = 0, j = 0;
if (!prefix || !prefix[0])
@@ -650,11 +894,13 @@ const char *remove_leading_path(const char *in, const char *prefix)
return in;
while (is_dir_sep(in[j]))
j++;
+
+ strbuf_reset(&buf);
if (!in[j])
- strcpy(buf, ".");
+ strbuf_addstr(&buf, ".");
else
- strcpy(buf, in + j);
- return buf;
+ strbuf_addstr(&buf, in + j);
+ return buf.buf;
}
/*
@@ -676,6 +922,11 @@ const char *remove_leading_path(const char *in, const char *prefix)
* normalized, any time "../" eats up to the prefix_len part,
* prefix_len is reduced. In the end prefix_len is the remaining
* prefix that has not been overridden by user pathspec.
+ *
+ * NEEDSWORK: This function doesn't perform normalization w.r.t. trailing '/'.
+ * For everything but the root folder itself, the normalized path should not
+ * end with a '/', then the callers need to be fixed up accordingly.
+ *
*/
int normalize_path_copy_len(char *dst, const char *src, int *prefix_len)
{
diff --git a/pkt-line.c b/pkt-line.c
index 08a1427c0d..62fdb37079 100644
--- a/pkt-line.c
+++ b/pkt-line.c
@@ -1,5 +1,6 @@
#include "cache.h"
#include "pkt-line.h"
+#include "run-command.h"
char packet_buffer[LARGE_PACKET_MAX];
static const char *packet_trace_prefix = "git";
@@ -11,6 +12,11 @@ void packet_trace_identity(const char *prog)
packet_trace_prefix = xstrdup(prog);
}
+static const char *get_trace_prefix(void)
+{
+ return in_async() ? "sideband" : packet_trace_prefix;
+}
+
static int packet_trace_pack(const char *buf, unsigned int len, int sideband)
{
if (!sideband) {
@@ -57,7 +63,7 @@ static void packet_trace(const char *buf, unsigned int len, int write)
strbuf_init(&out, len+32);
strbuf_addf(&out, "packet: %12s%c ",
- packet_trace_prefix, write ? '>' : '<');
+ get_trace_prefix(), write ? '>' : '<');
/* XXX we should really handle printable utf8 */
for (i = 0; i < len; i++) {
diff --git a/po/ru.po b/po/ru.po
index 4f66cee346..c9e1fb8e0d 100644
--- a/po/ru.po
+++ b/po/ru.po
@@ -10,10 +10,10 @@ msgid ""
msgstr ""
"Project-Id-Version: Перевод Git на руÑÑкий Ñзык\n"
"Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
-"POT-Creation-Date: 2015-07-14 07:19+0800\n"
-"PO-Revision-Date: 2015-07-14 13:06+0000\n"
+"POT-Creation-Date: 2015-09-15 06:45+0800\n"
+"PO-Revision-Date: 2015-09-30 14:53+0000\n"
"Last-Translator: Dimitriy Ryazantcev <DJm00n@mail.ru>\n"
-"Language-Team: Russian (http://www.transifex.com/p/git-po-ru/language/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"
@@ -31,94 +31,106 @@ msgid ""
"as appropriate to mark resolution and make a commit."
msgstr "ИÑправьте их в рабочем каталоге, затем запуÑтите «git add/rm <файл>»,\nчтобы пометить иÑправление и Ñделайте коммит."
-#: archive.c:11
+#: advice.c:101 builtin/merge.c:1227
+msgid "You have not concluded your merge (MERGE_HEAD exists)."
+msgstr "Ð’Ñ‹ не завершили ÑлиÑние (приÑутÑтвует файл MERGE_HEAD)."
+
+#: advice.c:103
+msgid "Please, commit your changes before you can merge."
+msgstr "Выполните коммит ваших изменений, перед ÑлиÑнием."
+
+#: advice.c:104
+msgid "Exiting because of unfinished merge."
+msgstr "Выход из-за незавершенного ÑлиÑниÑ."
+
+#: archive.c:12
msgid "git archive [<options>] <tree-ish> [<path>...]"
msgstr "git archive [<опции>] <указатель-дерева> [<путь>…]"
-#: archive.c:12
+#: archive.c:13
msgid "git archive --list"
msgstr "git archive --list"
-#: archive.c:13
+#: archive.c:14
msgid ""
"git archive --remote <repo> [--exec <cmd>] [<options>] <tree-ish> "
"[<path>...]"
msgstr "git archive --remote <репозиторий> [--exec <команда>] [<опции>] <указатель-дерева> [<путь>…]"
-#: archive.c:14
+#: archive.c:15
msgid "git archive --remote <repo> [--exec <cmd>] --list"
msgstr "git archive --remote <репозиторий> [--exec <команда>] --list"
-#: archive.c:342 builtin/add.c:137 builtin/add.c:428 builtin/rm.c:327
+#: archive.c:343 builtin/add.c:137 builtin/add.c:426 builtin/rm.c:327
#, c-format
msgid "pathspec '%s' did not match any files"
msgstr "ÑÐ¿ÐµÑ†Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ñ Ð¿ÑƒÑ‚Ð¸ «%s» не ÑоответÑтвует ни одному файлу"
-#: archive.c:427
+#: archive.c:428
msgid "fmt"
msgstr "формат"
-#: archive.c:427
+#: archive.c:428
msgid "archive format"
msgstr "формат архива"
-#: archive.c:428 builtin/log.c:1204
+#: archive.c:429 builtin/log.c:1229
msgid "prefix"
msgstr "префикÑ"
-#: archive.c:429
+#: archive.c:430
msgid "prepend prefix to each pathname in the archive"
msgstr "добавлÑÑ‚ÑŒ Ð¿Ñ€ÐµÑ„Ð¸ÐºÑ Ð¿ÐµÑ€ÐµÐ´ каждым путем файла в архиве"
-#: archive.c:430 builtin/archive.c:88 builtin/blame.c:2516
-#: builtin/blame.c:2517 builtin/config.c:57 builtin/fast-export.c:986
-#: builtin/fast-export.c:988 builtin/grep.c:712 builtin/hash-object.c:99
-#: builtin/ls-files.c:446 builtin/ls-files.c:449 builtin/notes.c:394
-#: builtin/notes.c:557 builtin/read-tree.c:109 parse-options.h:150
+#: archive.c:431 builtin/archive.c:88 builtin/blame.c:2516
+#: builtin/blame.c:2517 builtin/config.c:58 builtin/fast-export.c:987
+#: builtin/fast-export.c:989 builtin/grep.c:712 builtin/hash-object.c:99
+#: builtin/ls-files.c:446 builtin/ls-files.c:449 builtin/notes.c:395
+#: builtin/notes.c:558 builtin/read-tree.c:109 parse-options.h:153
msgid "file"
msgstr "файл"
-#: archive.c:431 builtin/archive.c:89
+#: archive.c:432 builtin/archive.c:89
msgid "write the archive to this file"
msgstr "запиÑÑŒ архива в Ñтот файл"
-#: archive.c:433
+#: archive.c:434
msgid "read .gitattributes in working directory"
msgstr "читать .gitattributes в рабочем каталоге"
-#: archive.c:434
+#: archive.c:435
msgid "report archived files on stderr"
msgstr "отчет об архивированных файлах в stderr"
-#: archive.c:435
+#: archive.c:436
msgid "store only"
msgstr "только хранение"
-#: archive.c:436
+#: archive.c:437
msgid "compress faster"
msgstr "Ñжимать быÑтрее"
-#: archive.c:444
+#: archive.c:445
msgid "compress better"
msgstr "Ñжимать лучше"
-#: archive.c:447
+#: archive.c:448
msgid "list supported archive formats"
msgstr "перечиÑлить поддерживаемые форматы архивов"
-#: archive.c:449 builtin/archive.c:90 builtin/clone.c:77
+#: archive.c:450 builtin/archive.c:90 builtin/clone.c:77
msgid "repo"
msgstr "репозиторий"
-#: archive.c:450 builtin/archive.c:91
+#: archive.c:451 builtin/archive.c:91
msgid "retrieve the archive from remote repository <repo>"
msgstr "получить архив из внешнего <репозиториÑ>"
-#: archive.c:451 builtin/archive.c:92 builtin/notes.c:478
+#: archive.c:452 builtin/archive.c:92 builtin/notes.c:479
msgid "command"
msgstr "комманда"
-#: archive.c:452 builtin/archive.c:93
+#: archive.c:453 builtin/archive.c:93
msgid "path to the remote git-upload-archive command"
msgstr "путь к команде git-upload-archive на машине Ñ Ð²Ð½ÐµÑˆÐ½Ð¸Ð¼ репозиторием"
@@ -230,6 +242,11 @@ msgstr "Ðеоднозначное Ð¸Ð¼Ñ Ð¾Ð±ÑŠÐµÐºÑ‚Ð°: «%s»."
msgid "Not a valid branch point: '%s'."
msgstr "ÐедопуÑÑ‚Ð¸Ð¼Ð°Ñ Ñ‚Ð¾Ñ‡ÐºÐ° ветки: «%s»."
+#: branch.c:399
+#, c-format
+msgid "'%s' is already checked out at '%s'"
+msgstr "«%s» уже находитÑÑ Ð½Ð° «%s»"
+
#: bundle.c:34
#, c-format
msgid "'%s' does not look like a v2 bundle file"
@@ -240,7 +257,7 @@ msgstr "«%s» не похож на файл пакета верÑии 2"
msgid "unrecognized header: %s%s (%d)"
msgstr "неопознанный заголовок: %s%s (%d)"
-#: bundle.c:87 builtin/commit.c:766
+#: bundle.c:87 builtin/commit.c:765
#, c-format
msgid "could not open '%s'"
msgstr "не удалоÑÑŒ открыть «%s»"
@@ -249,9 +266,9 @@ msgstr "не удалоÑÑŒ открыть «%s»"
msgid "Repository lacks these prerequisite commits:"
msgstr "Ð’ репозитории отÑутÑтвуют необходимые коммиты:"
-#: bundle.c:163 sequencer.c:650 sequencer.c:1105 builtin/blame.c:2705
-#: builtin/branch.c:651 builtin/commit.c:1045 builtin/log.c:330
-#: builtin/log.c:825 builtin/log.c:1432 builtin/log.c:1666 builtin/merge.c:358
+#: bundle.c:163 sequencer.c:636 sequencer.c:1083 builtin/blame.c:2708
+#: builtin/branch.c:652 builtin/commit.c:1044 builtin/log.c:334
+#: builtin/log.c:850 builtin/log.c:1457 builtin/log.c:1690 builtin/merge.c:358
#: builtin/shortlog.c:158
msgid "revision walk setup failed"
msgstr "Ñбой инициализации прохода по редакциÑм"
@@ -278,38 +295,38 @@ msgstr[1] "Пакет требует Ñти %d ÑÑылки:"
msgstr[2] "Пакет требует Ñти %d ÑÑылок:"
msgstr[3] "Пакет требует Ñти %d ÑÑылок:"
-#: bundle.c:251
+#: bundle.c:253
msgid "Could not spawn pack-objects"
msgstr "Ðе удалоÑÑŒ Ñоздать объекты пакета"
-#: bundle.c:269
+#: bundle.c:264
msgid "pack-objects died"
msgstr "критичеÑÐºÐ°Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ° pack-objects"
-#: bundle.c:309
+#: bundle.c:304
msgid "rev-list died"
msgstr "критичеÑÐºÐ°Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ° rev-list"
-#: bundle.c:358
+#: bundle.c:353
#, c-format
msgid "ref '%s' is excluded by the rev-list options"
msgstr "ÑÑылка «%s» иÑключена в ÑоответÑтвии Ñ Ð¾Ð¿Ñ†Ð¸Ñми rev-list"
-#: bundle.c:437 builtin/log.c:153 builtin/log.c:1342 builtin/shortlog.c:261
+#: bundle.c:443 builtin/log.c:157 builtin/log.c:1367 builtin/shortlog.c:261
#, c-format
msgid "unrecognized argument: %s"
msgstr "неопознанный аргумент: %s"
-#: bundle.c:443
+#: bundle.c:449
msgid "Refusing to create empty bundle."
msgstr "Отклонение ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ð¿ÑƒÑтого пакета."
-#: bundle.c:453
+#: bundle.c:459
#, c-format
msgid "cannot create '%s'"
msgstr "не удалоÑÑŒ Ñоздать «%s»"
-#: bundle.c:474
+#: bundle.c:480
msgid "index-pack died"
msgstr "критичеÑÐºÐ°Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ° index-pack"
@@ -318,7 +335,8 @@ msgstr "критичеÑÐºÐ°Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ° index-pack"
msgid "invalid color value: %.*s"
msgstr "недопуÑтимое значение цвета: %.*s"
-#: commit.c:40
+#: commit.c:40 builtin/am.c:451 builtin/am.c:487 builtin/am.c:1516
+#: builtin/am.c:2128
#, c-format
msgid "could not parse %s"
msgstr "не удалоÑÑŒ разобрать %s"
@@ -494,75 +512,75 @@ msgstr "Ñбой Ñ‡Ñ‚ÐµÐ½Ð¸Ñ orderfile «%s»"
msgid "Performing inexact rename detection"
msgstr "ВыполнÑетÑÑ Ð½ÐµÑ‚Ð¾Ñ‡Ð½Ð¾Ðµ определение переименованиÑ"
-#: diff.c:114
+#: diff.c:116
#, c-format
msgid " Failed to parse dirstat cut-off percentage '%s'\n"
msgstr " Сбой разбора величины Ñреза (cut-off) у dirstat «%s»\n"
-#: diff.c:119
+#: diff.c:121
#, c-format
msgid " Unknown dirstat parameter '%s'\n"
msgstr "ÐеизвеÑтный параметр dirstat: «%s»\n"
-#: diff.c:214
+#: diff.c:216
#, c-format
msgid "Unknown value for 'diff.submodule' config variable: '%s'"
msgstr "ÐеизвеÑтное Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ¼ÐµÐ½Ð½Ð¾Ð¹ «diff.submodule»: «%s»"
-#: diff.c:266
+#: diff.c:268
#, c-format
msgid ""
"Found errors in 'diff.dirstat' config variable:\n"
"%s"
msgstr "Ðайдены ошибки в переменной «diff.dirstat»:\n%s"
-#: diff.c:2997
+#: diff.c:2998
#, c-format
msgid "external diff died, stopping at %s"
msgstr "критичеÑÐºÐ°Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ° при внешнем Ñравнении, оÑтанов на %s"
-#: diff.c:3393
+#: diff.c:3394
msgid "--follow requires exactly one pathspec"
msgstr "--follow требует ровно одной Ñпецификации пути"
-#: diff.c:3556
+#: diff.c:3557
#, c-format
msgid ""
"Failed to parse --dirstat/-X option parameter:\n"
"%s"
msgstr "Сбой разбора параметра опции --dirstat/-X :\n%s"
-#: diff.c:3570
+#: diff.c:3571
#, c-format
msgid "Failed to parse --submodule option parameter: '%s'"
msgstr "Сбой разбора параметра опции --submodule: «%s»"
-#: dir.c:1852
+#: dir.c:1853
msgid "failed to get kernel name and information"
msgstr "не удалоÑÑŒ получить Ð¸Ð¼Ñ Ñдра и информацию"
-#: dir.c:1945
+#: dir.c:1936
msgid "Untracked cache is disabled on this system."
msgstr "КÑш неотÑлеживаемых файлов отключен на Ñтой ÑиÑтеме."
-#: gpg-interface.c:129 gpg-interface.c:200
+#: gpg-interface.c:166 gpg-interface.c:237
msgid "could not run gpg."
msgstr "не удалоÑÑŒ запуÑтить gpg."
-#: gpg-interface.c:141
+#: gpg-interface.c:178
msgid "gpg did not accept the data"
msgstr "gpg не принÑл данные"
-#: gpg-interface.c:152
+#: gpg-interface.c:189
msgid "gpg failed to sign the data"
msgstr "gpg не удалоÑÑŒ подпиÑать данные"
-#: gpg-interface.c:185
+#: gpg-interface.c:222
#, c-format
msgid "could not create temporary file '%s': %s"
msgstr "не удалоÑÑŒ Ñоздать временный файл «%s»: %s"
-#: gpg-interface.c:188
+#: gpg-interface.c:225
#, c-format
msgid "failed writing detached signature to '%s': %s"
msgstr "Ñбой запиÑи отÑоединенной подпиÑи в «%s»: %s"
@@ -640,20 +658,12 @@ msgstr[3] "\nВозможно, вы имели в виду что-то из ÑÑ‚
msgid "%s: %s - %s"
msgstr "%s: %s — %s"
-#: lockfile.c:345
-msgid "BUG: reopen a lockfile that is still open"
-msgstr "БÐГ: повторное открытие файла блокировки, который уже открыт"
-
-#: lockfile.c:347
-msgid "BUG: reopen a lockfile that has been committed"
-msgstr "БÐГ: повторное открытие файла блокировки, который уже был закоммичен"
-
#: merge.c:41
msgid "failed to read the cache"
msgstr "Ñбой Ñ‡Ñ‚ÐµÐ½Ð¸Ñ ÐºÑша"
-#: merge.c:94 builtin/checkout.c:376 builtin/checkout.c:587
-#: builtin/clone.c:647
+#: merge.c:94 builtin/am.c:2001 builtin/am.c:2036 builtin/checkout.c:375
+#: builtin/checkout.c:586 builtin/clone.c:715
msgid "unable to write new index file"
msgstr "не удалоÑÑŒ запиÑать новый файл индекÑа"
@@ -700,7 +710,7 @@ msgstr "невозможно прочитать объект %s «%s»"
msgid "blob expected for %s '%s'"
msgstr "ожидаетÑÑ Ð´Ð²Ð¾Ð¸Ñ‡Ð½Ñ‹Ð¹ объект Ð´Ð»Ñ %s «%s»"
-#: merge-recursive.c:788 builtin/clone.c:306
+#: merge-recursive.c:788 builtin/clone.c:364
#, c-format
msgid "failed to open '%s'"
msgstr "не удалоÑÑŒ открыть «%s»"
@@ -908,19 +918,19 @@ msgstr "Ðе удаетÑÑ Ð·Ð°Ð¿Ð¸Ñать индекÑ."
msgid "Cannot commit uninitialized/unreferenced notes tree"
msgstr "ÐÐµÐ»ÑŒÐ·Ñ Ð·Ð°ÐºÐ¾Ð¼Ð¼Ð¸Ñ‚Ð¸Ñ‚ÑŒ неинициализированное или не имеющее ÑÑылок дерево заметок"
-#: notes-utils.c:82
+#: notes-utils.c:100
#, c-format
msgid "Bad notes.rewriteMode value: '%s'"
msgstr "Ðеправильное значение notes.rewriteMode: «%s»"
-#: notes-utils.c:92
+#: notes-utils.c:110
#, c-format
msgid "Refusing to rewrite notes in %s (outside of refs/notes/)"
msgstr "Отказ в перезапиÑи заметок в %s (за пределами refs/notes/)"
#. TRANSLATORS: The first %s is the name of the
#. environment variable, the second %s is its value
-#: notes-utils.c:119
+#: notes-utils.c:137
#, c-format
msgid "Bad %s value: '%s'"
msgstr "Ðеправильное значение переменной %s: «%s»"
@@ -930,28 +940,28 @@ msgstr "Ðеправильное значение переменной %s: «%sÂ
msgid "unable to parse object: %s"
msgstr "не удалоÑÑŒ разобрать объект: %s"
-#: parse-options.c:546
+#: parse-options.c:563
msgid "..."
msgstr "…"
-#: parse-options.c:564
+#: parse-options.c:581
#, c-format
msgid "usage: %s"
msgstr "иÑпользование: %s"
#. TRANSLATORS: the colon here should align with the
#. one in "usage: %s" translation
-#: parse-options.c:568
+#: parse-options.c:585
#, c-format
msgid " or: %s"
msgstr " или: %s"
-#: parse-options.c:571
+#: parse-options.c:588
#, c-format
msgid " %s"
msgstr " %s"
-#: parse-options.c:605
+#: parse-options.c:622
msgid "-NUM"
msgstr "-КОЛИЧЕСТВО"
@@ -1015,7 +1025,7 @@ msgid ""
"Perhaps you forgot to add either ':/' or '.' ?"
msgstr "Ðе указан шаблон Ð´Ð»Ñ Ð¸ÑÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ :(exclude).\nВозможно, вы забыли «:/» или «.» ?"
-#: pretty.c:968
+#: pretty.c:969
msgid "unable to parse --pretty format"
msgstr "не удалоÑÑŒ разобрать формат Ð´Ð»Ñ --pretty"
@@ -1023,20 +1033,45 @@ msgstr "не удалоÑÑŒ разобрать формат Ð´Ð»Ñ --pretty"
msgid "done"
msgstr "готово"
-#: read-cache.c:1295
+#: read-cache.c:1296
#, c-format
msgid ""
"index.version set, but the value is invalid.\n"
"Using version %i"
msgstr "index.version указан, но значение недейÑтвительное.\nИÑпользую верÑию %i"
-#: read-cache.c:1305
+#: read-cache.c:1306
#, c-format
msgid ""
"GIT_INDEX_VERSION set, but the value is invalid.\n"
"Using version %i"
msgstr "GIT_INDEX_VERSION указан, но значение недейÑтвительное.\nИÑпользую верÑию %i"
+#: refs.c:2941 builtin/merge.c:760 builtin/merge.c:871 builtin/merge.c:973
+#: builtin/merge.c:983
+#, c-format
+msgid "Could not open '%s' for writing"
+msgstr "Ðе удалоÑÑŒ открыть «%s» Ð´Ð»Ñ Ð·Ð°Ð¿Ð¸Ñи"
+
+#: refs.c:3001
+#, c-format
+msgid "could not delete reference %s: %s"
+msgstr "не удалоÑÑŒ удалить ÑÑылку %s: %s"
+
+#: refs.c:3004
+#, c-format
+msgid "could not delete references: %s"
+msgstr "не удалоÑÑŒ удалить ÑÑылки: %s"
+
+#: refs.c:3013
+#, c-format
+msgid "could not remove reference %s"
+msgstr "не удалоÑÑŒ удалить ÑÑылки %s"
+
+#: ref-filter.c:660
+msgid "unable to parse format"
+msgstr "не удалоÑÑŒ разобрать формат"
+
#: remote.c:792
#, c-format
msgid "Cannot fetch both %s and %s to %s"
@@ -1156,7 +1191,16 @@ msgstr[3] "Ваша ветка и «%s» разошлиÑÑŒ\nи теперь иÐ
msgid " (use \"git pull\" to merge the remote branch into yours)\n"
msgstr " (иÑпользуйте «git pull», чтобы Ñлить внешнюю ветку в вашу)\n"
-#: revision.c:2366
+#: revision.c:2198
+msgid "your current branch appears to be broken"
+msgstr "похоже, ваша Ñ‚ÐµÐºÑƒÑ‰Ð°Ñ Ð²ÐµÑ‚ÐºÐ° повреждена"
+
+#: revision.c:2201
+#, c-format
+msgid "your current branch '%s' does not have any commits yet"
+msgstr "ваша Ñ‚ÐµÐºÑƒÑ‰Ð°Ñ Ð²ÐµÑ‚ÐºÐ° «%s» еще не Ñодержит ни одного коммита"
+
+#: revision.c:2395
msgid "--first-parent is incompatible with --bisect"
msgstr "опцию --first-parent Ð½ÐµÐ»ÑŒÐ·Ñ Ð¸Ñпользовать одновременно Ñ --bisect"
@@ -1169,257 +1213,251 @@ msgstr "Ñбой Ð¾Ñ‚ÐºÑ€Ñ‹Ñ‚Ð¸Ñ /dev/null"
msgid "dup2(%d,%d) failed"
msgstr "dup2(%d,%d) Ñбой"
-#: send-pack.c:272
+#: send-pack.c:295
msgid "failed to sign the push certificate"
msgstr "Ñбой подпиÑÐ°Ð½Ð¸Ñ Ñертификата отправки"
-#: send-pack.c:378
+#: send-pack.c:404
msgid "the receiving end does not support --signed push"
msgstr "Ð¿Ñ€Ð¸Ð½Ð¸Ð¼Ð°ÑŽÑ‰Ð°Ñ Ñторона не поддерживает отправку Ñ Ð¾Ð¿Ñ†Ð¸ÐµÐ¹ --signed"
-#: send-pack.c:389
+#: send-pack.c:406
+msgid ""
+"not sending a push certificate since the receiving end does not support "
+"--signed push"
+msgstr "не отправлÑем Ñертификат Ð´Ð»Ñ Ð¾Ñ‚Ð¿Ñ€Ð°Ð²ÐºÐ¸, так как Ð¿Ñ€Ð¸Ð½Ð¸Ð¼Ð°ÑŽÑ‰Ð°Ñ Ñторона не поддерживает отправку Ñ Ð¾Ð¿Ñ†Ð¸ÐµÐ¹ --signed"
+
+#: send-pack.c:418
msgid "the receiving end does not support --atomic push"
msgstr "Ð¿Ñ€Ð¸Ð½Ð¸Ð¼Ð°ÑŽÑ‰Ð°Ñ Ñторона не поддерживает отправку Ñ Ð¾Ð¿Ñ†Ð¸ÐµÐ¹ --atomic"
-#: sequencer.c:172 builtin/merge.c:760 builtin/merge.c:871 builtin/merge.c:973
-#: builtin/merge.c:983
-#, c-format
-msgid "Could not open '%s' for writing"
-msgstr "Ðе удалоÑÑŒ открыть «%s» Ð´Ð»Ñ Ð·Ð°Ð¿Ð¸Ñи"
-
-#: sequencer.c:174 builtin/merge.c:344 builtin/merge.c:763 builtin/merge.c:975
-#: builtin/merge.c:988
-#, c-format
-msgid "Could not write to '%s'"
-msgstr "Ðе удалоÑÑŒ запиÑать в «%s»"
-
-#: sequencer.c:195
+#: sequencer.c:183
msgid ""
"after resolving the conflicts, mark the corrected paths\n"
"with 'git add <paths>' or 'git rm <paths>'"
msgstr "поÑле Ñ€Ð°Ð·Ñ€ÐµÑˆÐµÐ½Ð¸Ñ ÐºÐ¾Ð½Ñ„Ð»Ð¸ÐºÑ‚Ð¾Ð², пометьте иÑправленные пути\nÑ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ «git add <пути>» или «git rm <пути>»"
-#: sequencer.c:198
+#: sequencer.c:186
msgid ""
"after resolving the conflicts, mark the corrected paths\n"
"with 'git add <paths>' or 'git rm <paths>'\n"
"and commit the result with 'git commit'"
msgstr "поÑле Ñ€Ð°Ð·Ñ€ÐµÑˆÐµÐ½Ð¸Ñ ÐºÐ¾Ð½Ñ„Ð»Ð¸ÐºÑ‚Ð¾Ð², пометьте иÑправленные пути\nÑ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ «git add <пути>» или «git rm <пути>»\nи Ñделайте коммит Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ «git commit»"
-#: sequencer.c:211 sequencer.c:861 sequencer.c:944
+#: sequencer.c:199 sequencer.c:842 sequencer.c:922
#, c-format
msgid "Could not write to %s"
msgstr "Ðе удалоÑÑŒ запиÑать в %s"
-#: sequencer.c:214
+#: sequencer.c:202
#, c-format
msgid "Error wrapping up %s"
msgstr "Ошибка Ð¾Ð±Ð¾Ñ€Ð°Ñ‡Ð¸Ð²Ð°Ð½Ð¸Ñ %s"
-#: sequencer.c:229
+#: sequencer.c:217
msgid "Your local changes would be overwritten by cherry-pick."
msgstr "Ваши локальные изменение будут перезапиÑаны отбором лучшего."
-#: sequencer.c:231
+#: sequencer.c:219
msgid "Your local changes would be overwritten by revert."
msgstr "Ваши локальные изменение будут перезапиÑаны возвратом коммита."
-#: sequencer.c:234
+#: sequencer.c:222
msgid "Commit your changes or stash them to proceed."
msgstr "Сделайте коммит или ÑпрÑчьте ваши Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ð¿Ñ€Ð¾Ð´Ð¾Ð»Ð¶ÐµÐ½Ð¸Ñ."
#. TRANSLATORS: %s will be "revert" or "cherry-pick"
-#: sequencer.c:321
+#: sequencer.c:309
#, c-format
msgid "%s: Unable to write new index file"
msgstr "%s: Ðе удалоÑÑŒ запиÑать файл индекÑа"
-#: sequencer.c:339
+#: sequencer.c:327
msgid "Could not resolve HEAD commit\n"
msgstr "Ðе удалоÑÑŒ определить HEAD коммит\n"
-#: sequencer.c:359
+#: sequencer.c:347
msgid "Unable to update cache tree\n"
msgstr "Ðе удалоÑÑŒ обновить дерево кÑша\n"
-#: sequencer.c:411
+#: sequencer.c:399
#, c-format
msgid "Could not parse commit %s\n"
msgstr "Ðе удалоÑÑŒ разобрать коммит %s\n"
-#: sequencer.c:416
+#: sequencer.c:404
#, c-format
msgid "Could not parse parent commit %s\n"
msgstr "Ðе удалоÑÑŒ разобрать родительÑкую коммит %s\n"
-#: sequencer.c:482
+#: sequencer.c:469
msgid "Your index file is unmerged."
msgstr "Ваш файл индекÑа не Ñлит."
-#: sequencer.c:501
+#: sequencer.c:488
#, c-format
msgid "Commit %s is a merge but no -m option was given."
msgstr "Коммит %s — Ñто коммит-ÑлиÑние, но Ð¾Ð¿Ñ†Ð¸Ñ -m не указана."
-#: sequencer.c:509
+#: sequencer.c:496
#, c-format
msgid "Commit %s does not have parent %d"
msgstr "У коммита %s нет предка %d"
-#: sequencer.c:513
+#: sequencer.c:500
#, c-format
msgid "Mainline was specified but commit %s is not a merge."
msgstr "ОÑÐ½Ð¾Ð²Ð½Ð°Ñ Ð²ÐµÑ‚ÐºÐ° указана, но коммит %s не ÑвлÑетÑÑ ÑлиÑнием."
#. TRANSLATORS: The first %s will be "revert" or
#. "cherry-pick", the second %s a SHA1
-#: sequencer.c:526
+#: sequencer.c:513
#, c-format
msgid "%s: cannot parse parent commit %s"
msgstr "%s: не удалоÑÑŒ разобрать родительÑкий коммит Ð´Ð»Ñ %s"
-#: sequencer.c:530
+#: sequencer.c:517
#, c-format
msgid "Cannot get commit message for %s"
msgstr "Ðе удалоÑÑŒ получить Ñообщение коммита Ð´Ð»Ñ %s"
-#: sequencer.c:616
+#: sequencer.c:603
#, c-format
msgid "could not revert %s... %s"
msgstr "не удалоÑÑŒ возвратить коммит %s… %s"
-#: sequencer.c:617
+#: sequencer.c:604
#, c-format
msgid "could not apply %s... %s"
msgstr "не удалоÑÑŒ применить коммит %s… %s"
-#: sequencer.c:653
+#: sequencer.c:639
msgid "empty commit set passed"
msgstr "передан пуÑтой набор коммитов"
-#: sequencer.c:661
+#: sequencer.c:647
#, c-format
msgid "git %s: failed to read the index"
msgstr "git %s: Ñбой Ñ‡Ñ‚ÐµÐ½Ð¸Ñ Ð¸Ð½Ð´ÐµÐºÑа"
-#: sequencer.c:665
+#: sequencer.c:651
#, c-format
msgid "git %s: failed to refresh the index"
msgstr "git %s: Ñбой Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð¸Ð½Ð´ÐµÐºÑа"
-#: sequencer.c:725
+#: sequencer.c:711
#, c-format
msgid "Cannot %s during a %s"
msgstr "Ðе удалоÑÑŒ %s во Ð²Ñ€ÐµÐ¼Ñ %s"
-#: sequencer.c:747
+#: sequencer.c:733
#, c-format
msgid "Could not parse line %d."
msgstr "Ðе удалоÑÑŒ разобрать Ñтроку %d."
-#: sequencer.c:752
+#: sequencer.c:738
msgid "No commits parsed."
msgstr "Коммиты не разобраны."
-#: sequencer.c:765
+#: sequencer.c:750
#, c-format
msgid "Could not open %s"
msgstr "Ðе удалоÑÑŒ открыть %s"
-#: sequencer.c:769
+#: sequencer.c:754
#, c-format
msgid "Could not read %s."
msgstr "Ðе удалоÑÑŒ прочитать %s."
-#: sequencer.c:776
+#: sequencer.c:761
#, c-format
msgid "Unusable instruction sheet: %s"
msgstr "ÐÐµÐ¿Ñ€Ð¸Ð³Ð¾Ð´Ð½Ð°Ñ Ð´Ð»Ñ Ð¸ÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ ÐºÐ°Ñ€Ñ‚Ð° Ñ Ð¸Ð½ÑтрукциÑми: %s"
-#: sequencer.c:806
+#: sequencer.c:791
#, c-format
msgid "Invalid key: %s"
msgstr "ÐедейÑтвительный ключ: %s"
-#: sequencer.c:809
+#: sequencer.c:794 builtin/pull.c:47 builtin/pull.c:49
#, c-format
msgid "Invalid value for %s: %s"
msgstr "Ðеправильное значение %s: %s"
-#: sequencer.c:821
+#: sequencer.c:804
#, c-format
msgid "Malformed options sheet: %s"
msgstr "ИÑÐ¿Ð¾Ñ€Ñ‡ÐµÐ½Ð½Ð°Ñ ÐºÐ°Ñ€Ñ‚Ð° Ñ Ð¾Ð¿Ñ†Ð¸Ñми: %s"
-#: sequencer.c:842
+#: sequencer.c:823
msgid "a cherry-pick or revert is already in progress"
msgstr "отбор лучшего или возврат коммита уже выполнÑетÑÑ"
-#: sequencer.c:843
+#: sequencer.c:824
msgid "try \"git cherry-pick (--continue | --quit | --abort)\""
msgstr "попробуйте «git cherry-pick (--continue | --quit | --abort)»"
-#: sequencer.c:847
+#: sequencer.c:828
#, c-format
msgid "Could not create sequencer directory %s"
msgstr "Ðе удалоÑÑŒ Ñоздать каталог Ð´Ð»Ñ ÑƒÐºÐ°Ð·Ð°Ñ‚ÐµÐ»Ñ ÑÐ»ÐµÐ´Ð¾Ð²Ð°Ð½Ð¸Ñ ÐºÐ¾Ð¼Ð¼Ð¸Ñ‚Ð¾Ð² %s"
-#: sequencer.c:863 sequencer.c:948
+#: sequencer.c:844 sequencer.c:926
#, c-format
msgid "Error wrapping up %s."
msgstr "Ошибка Ð¾Ð±Ð¾Ñ€Ð°Ñ‡Ð¸Ð²Ð°Ð½Ð¸Ñ %s."
-#: sequencer.c:882 sequencer.c:1018
+#: sequencer.c:863 sequencer.c:996
msgid "no cherry-pick or revert in progress"
msgstr "отбор лучшего или возврат коммита не выполнÑетÑÑ"
-#: sequencer.c:884
+#: sequencer.c:865
msgid "cannot resolve HEAD"
msgstr "не удалоÑÑŒ определить HEAD"
-#: sequencer.c:886
+#: sequencer.c:867
msgid "cannot abort from a branch yet to be born"
msgstr "Ð½ÐµÐ»ÑŒÐ·Ñ Ð¾Ñ‚Ð¼ÐµÐ½Ð¸Ñ‚ÑŒ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ñ Ð²ÐµÑ‚ÐºÐ¸, ÐºÐ¾Ñ‚Ð¾Ñ€Ð°Ñ ÐµÑ‰Ðµ не Ñоздана"
-#: sequencer.c:908 builtin/apply.c:4291
+#: sequencer.c:887 builtin/apply.c:4291
#, c-format
msgid "cannot open %s: %s"
msgstr "не удалоÑÑŒ открыть %s: %s"
-#: sequencer.c:911
+#: sequencer.c:890
#, c-format
msgid "cannot read %s: %s"
msgstr "не удалоÑÑŒ прочитать %s: %s"
-#: sequencer.c:912
+#: sequencer.c:891
msgid "unexpected end of file"
msgstr "неожиданный конец файла"
-#: sequencer.c:918
+#: sequencer.c:897
#, c-format
msgid "stored pre-cherry-pick HEAD file '%s' is corrupt"
msgstr "Ñохраненный файл Ñ HEAD перед отбором лучшего «%s» поврежден"
-#: sequencer.c:941
+#: sequencer.c:919
#, c-format
msgid "Could not format %s."
msgstr "Ðе удалоÑÑŒ отформатировать %s."
-#: sequencer.c:1086
+#: sequencer.c:1064
#, c-format
msgid "%s: can't cherry-pick a %s"
msgstr "%s: не удалоÑÑŒ отобрать %s"
-#: sequencer.c:1089
+#: sequencer.c:1067
#, c-format
msgid "%s: bad revision"
msgstr "%s: Ð¿Ð»Ð¾Ñ…Ð°Ñ Ñ€ÐµÐ´Ð°ÐºÑ†Ð¸Ñ"
-#: sequencer.c:1123
+#: sequencer.c:1101
msgid "Can't revert as initial commit"
msgstr "ÐÐµÐ»ÑŒÐ·Ñ Ð²Ð¾Ð·Ð²Ñ€Ð°Ñ‚Ð¸Ñ‚ÑŒ изначальный коммит"
-#: sequencer.c:1124
+#: sequencer.c:1102
msgid "Can't cherry-pick into empty head"
msgstr "ÐÐµÐ»ÑŒÐ·Ñ Ð¾Ñ‚Ð¾Ð±Ñ€Ð°Ñ‚ÑŒ лучшее в пуÑтой HEAD"
@@ -1441,30 +1479,30 @@ msgid ""
"running \"git config advice.objectNameWarning false\""
msgstr "Обычно Git не Ñоздает ÑÑылки, оканчивающиеÑÑ Ð½Ð° 40 шеÑтнадцатеричных\nÑимволов, потому, что они будут игнорироватьÑÑ, когда вы проÑто\nукажете Ñто 40-Ñимвольное шеÑтнадцатеричное чиÑло. Такие ÑÑылки\nмогли быть Ñозданы по ошибке. Ðапример, Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ:\n\n git checkout -b $br $(git rev-parse …)\n\n, еÑли «$br» оказалÑÑ Ð¿ÑƒÑтым, то ÑÑылка Ñ 40-Ñимвольным\nшеÑтнадцатеричным чиÑлом будет Ñоздана. ПожалуйÑта, проÑмотрите Ñти\nÑÑылки и, возможно, удалите их. Ð’Ñ‹ можете отключить Ñто Ñообщение\nзапуÑтив «git config advice.objectNameWarning false»"
-#: submodule.c:64 submodule.c:98
+#: submodule.c:61 submodule.c:95
msgid "Cannot change unmerged .gitmodules, resolve merge conflicts first"
msgstr "Ðе удалоÑÑŒ изменить не Ñлитый .gitmodules, Ñначала разрешите конфликты"
-#: submodule.c:68 submodule.c:102
+#: submodule.c:65 submodule.c:99
#, c-format
msgid "Could not find section in .gitmodules where path=%s"
msgstr "Ðе удалоÑÑŒ найти раздел в .gitmodules, где путь равен %s"
-#: submodule.c:76
+#: submodule.c:73
#, c-format
msgid "Could not update .gitmodules entry %s"
msgstr " Ðе удалоÑÑŒ обновить .gitmodules запиÑÑŒ %s"
-#: submodule.c:109
+#: submodule.c:106
#, c-format
msgid "Could not remove .gitmodules entry for %s"
msgstr "Ðе удалоÑÑŒ удалить запиÑÑŒ в .gitmodules Ð´Ð»Ñ %s"
-#: submodule.c:120
+#: submodule.c:117
msgid "staging updated .gitmodules failed"
msgstr "Ñбой индекÑÐ¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ð¾Ð³Ð¾ .gitmodules"
-#: submodule.c:1115
+#: submodule.c:1045
#, c-format
msgid "Could not set core.worktree in %s"
msgstr "Ðе удалоÑÑŒ уÑтановить core.worktree в %s"
@@ -1494,6 +1532,11 @@ msgstr "не удалоÑÑŒ прочитать входной файл «%s»"
msgid "could not read from stdin"
msgstr "не удалоÑÑŒ прочитать из Ñтандартного ввода"
+#: transport-helper.c:1025
+#, c-format
+msgid "Could not read ref %s"
+msgstr "Ðе удалоÑÑŒ прочитать ÑÑылку %s"
+
#: unpack-trees.c:203
msgid "Checking out files"
msgstr "РаÑпаковка файлов"
@@ -1527,368 +1570,423 @@ msgstr "неправильный номер порта"
msgid "invalid '..' path segment"
msgstr "Ð½ÐµÐ¿Ñ€Ð°Ð²Ð¸Ð»ÑŒÐ½Ð°Ñ Ñ‡Ð°ÑÑ‚ÑŒ пути «..»"
-#: wrapper.c:523
+#: wrapper.c:219 wrapper.c:362
+#, c-format
+msgid "could not open '%s' for reading and writing"
+msgstr "не удалоÑÑŒ открыть «%s» Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ Ð¸ запиÑи"
+
+#: wrapper.c:221 wrapper.c:364
+#, c-format
+msgid "could not open '%s' for writing"
+msgstr "не удалоÑÑŒ открыть «%s» Ð´Ð»Ñ Ð·Ð°Ð¿Ð¸Ñи"
+
+#: wrapper.c:223 wrapper.c:366 builtin/am.c:337 builtin/commit.c:1688
+#: builtin/merge.c:1076 builtin/pull.c:380
+#, c-format
+msgid "could not open '%s' for reading"
+msgstr "не удалоÑÑŒ открыть «%s» Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ"
+
+#: wrapper.c:579
#, c-format
msgid "unable to access '%s': %s"
msgstr "«%s» недоÑтупно: %s"
-#: wrapper.c:544
+#: wrapper.c:600
#, c-format
msgid "unable to access '%s'"
msgstr "«%s» недоÑтупно"
-#: wrapper.c:555
+#: wrapper.c:611
#, c-format
msgid "unable to look up current user in the passwd file: %s"
msgstr "не удалоÑÑŒ запроÑить текущего Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ð² файле passwd: %s"
-#: wrapper.c:556
+#: wrapper.c:612
msgid "no such user"
msgstr "нет такого пользователÑ"
-#: wrapper.c:564
+#: wrapper.c:620
msgid "unable to get current working directory"
msgstr "не удалоÑÑŒ получить текущий рабочий каталог"
-#: wrapper.c:575
+#: wrapper.c:631
#, c-format
msgid "could not open %s for writing"
msgstr "не удалоÑÑŒ открыть «%s» Ð´Ð»Ñ Ð·Ð°Ð¿Ð¸Ñи"
-#: wrapper.c:587
+#: wrapper.c:642 builtin/am.c:424
#, c-format
msgid "could not write to %s"
msgstr "не удалоÑÑŒ запиÑать в %s"
-#: wrapper.c:593
+#: wrapper.c:648
#, c-format
msgid "could not close %s"
msgstr "не удалоÑÑŒ закрыть %s"
-#: wt-status.c:150
+#: wt-status.c:149
msgid "Unmerged paths:"
msgstr "Ðе Ñлитые пути:"
-#: wt-status.c:177 wt-status.c:204
+#: wt-status.c:176 wt-status.c:203
#, c-format
msgid " (use \"git reset %s <file>...\" to unstage)"
msgstr " (иÑпользуйте «git reset %s <файл>…», чтобы убрать из индекÑа)"
-#: wt-status.c:179 wt-status.c:206
+#: wt-status.c:178 wt-status.c:205
msgid " (use \"git rm --cached <file>...\" to unstage)"
msgstr " (иÑпользуйте «git rm --cached <файл>…», чтобы убрать из индекÑа)"
-#: wt-status.c:183
+#: wt-status.c:182
msgid " (use \"git add <file>...\" to mark resolution)"
msgstr " (иÑпользуйте «git add <файл>…», чтобы пометить разрешение конфликта)"
-#: wt-status.c:185 wt-status.c:189
+#: wt-status.c:184 wt-status.c:188
msgid " (use \"git add/rm <file>...\" as appropriate to mark resolution)"
msgstr " (иÑпользуйте «git add/rm <файл>…», чтобы пометить выбранное разрешение конфликта)"
-#: wt-status.c:187
+#: wt-status.c:186
msgid " (use \"git rm <file>...\" to mark resolution)"
msgstr " (иÑпользуйте «git rm <файл>…», чтобы пометить разрешение конфликта)"
-#: wt-status.c:198 wt-status.c:881
+#: wt-status.c:197 wt-status.c:880
msgid "Changes to be committed:"
msgstr "ИзменениÑ, которые будут включены в коммит:"
-#: wt-status.c:216 wt-status.c:890
+#: wt-status.c:215 wt-status.c:889
msgid "Changes not staged for commit:"
msgstr "ИзменениÑ, которые не в индекÑе Ð´Ð»Ñ ÐºÐ¾Ð¼Ð¼Ð¸Ñ‚Ð°:"
-#: wt-status.c:220
+#: wt-status.c:219
msgid " (use \"git add <file>...\" to update what will be committed)"
msgstr " (иÑпользуйте «git add <файл>…», чтобы добавить файл в индекÑ)"
-#: wt-status.c:222
+#: wt-status.c:221
msgid " (use \"git add/rm <file>...\" to update what will be committed)"
msgstr " (иÑпользуйте «git add/rm <файл>…», чтобы добавить или удалить файл из индекÑа)"
-#: wt-status.c:223
+#: wt-status.c:222
msgid ""
" (use \"git checkout -- <file>...\" to discard changes in working "
"directory)"
msgstr " (иÑпользуйте «git checkout -- <файл>…», чтобы отменить изменениÑ\n в рабочем каталоге)"
-#: wt-status.c:225
+#: wt-status.c:224
msgid " (commit or discard the untracked or modified content in submodules)"
msgstr " (Ñделайте коммит или отмените Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð² неотÑлеживаемом или измененном Ñодержимом в подмодулÑÑ…)"
-#: wt-status.c:237
+#: wt-status.c:236
#, c-format
msgid " (use \"git %s <file>...\" to include in what will be committed)"
msgstr " (иÑпользуйте «git %s <файл>…», чтобы добавить в то, что будет включено в коммит)"
-#: wt-status.c:252
+#: wt-status.c:251
msgid "both deleted:"
msgstr "оба удалены:"
-#: wt-status.c:254
+#: wt-status.c:253
msgid "added by us:"
msgstr "добавлено нами:"
-#: wt-status.c:256
+#: wt-status.c:255
msgid "deleted by them:"
msgstr "удалено ими:"
-#: wt-status.c:258
+#: wt-status.c:257
msgid "added by them:"
msgstr "добавлено ими:"
-#: wt-status.c:260
+#: wt-status.c:259
msgid "deleted by us:"
msgstr "удалено нами:"
-#: wt-status.c:262
+#: wt-status.c:261
msgid "both added:"
msgstr "оба добавлены:"
-#: wt-status.c:264
+#: wt-status.c:263
msgid "both modified:"
msgstr "оба измены:"
-#: wt-status.c:266
+#: wt-status.c:265
#, c-format
msgid "bug: unhandled unmerged status %x"
msgstr "ошибка: необработанный ÑÑ‚Ð°Ñ‚ÑƒÑ Ð½Ðµ Ñлитых изменений %x"
-#: wt-status.c:274
+#: wt-status.c:273
msgid "new file:"
msgstr "новый файл:"
-#: wt-status.c:276
+#: wt-status.c:275
msgid "copied:"
msgstr "Ñкопировано:"
-#: wt-status.c:278
+#: wt-status.c:277
msgid "deleted:"
msgstr "удалено:"
-#: wt-status.c:280
+#: wt-status.c:279
msgid "modified:"
msgstr "изменено:"
-#: wt-status.c:282
+#: wt-status.c:281
msgid "renamed:"
msgstr "переименовано:"
-#: wt-status.c:284
+#: wt-status.c:283
msgid "typechange:"
msgstr "изменен тип:"
-#: wt-status.c:286
+#: wt-status.c:285
msgid "unknown:"
msgstr "неизвеÑтно:"
-#: wt-status.c:288
+#: wt-status.c:287
msgid "unmerged:"
msgstr "не Ñлитые:"
-#: wt-status.c:370
+#: wt-status.c:369
msgid "new commits, "
msgstr "новые коммиты, "
-#: wt-status.c:372
+#: wt-status.c:371
msgid "modified content, "
msgstr "изменено Ñодержимое, "
-#: wt-status.c:374
+#: wt-status.c:373
msgid "untracked content, "
msgstr "неотÑлеживаемое Ñодержимое, "
-#: wt-status.c:391
+#: wt-status.c:390
#, c-format
msgid "bug: unhandled diff status %c"
msgstr "ошибка: необработанный ÑÑ‚Ð°Ñ‚ÑƒÑ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ð¹ %c"
-#: wt-status.c:755
+#: wt-status.c:754
msgid "Submodules changed but not updated:"
msgstr "Измененные, но не обновленные подмодули:"
-#: wt-status.c:757
+#: wt-status.c:756
msgid "Submodule changes to be committed:"
msgstr "Ð˜Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð² подмодулÑÑ…, которые будут закоммичены:"
-#: wt-status.c:838
+#: wt-status.c:837
msgid ""
"Do not touch the line above.\n"
"Everything below will be removed."
msgstr "Ðе трогайте Ñтроку выше Ñтой.\nÐ’ÑÑ‘, что ниже — будет удалено."
-#: wt-status.c:949
+#: wt-status.c:948
msgid "You have unmerged paths."
msgstr "У Ð²Ð°Ñ ÐµÑÑ‚ÑŒ не Ñлитые пути."
-#: wt-status.c:952
+#: wt-status.c:951
msgid " (fix conflicts and run \"git commit\")"
msgstr " (разрешите конфликты, затем запуÑтите «git commit»)"
-#: wt-status.c:955
+#: wt-status.c:954
msgid "All conflicts fixed but you are still merging."
msgstr "Ð’Ñе конфликты иÑправлены, но вы вÑе еще в процеÑÑе ÑлиÑниÑ."
-#: wt-status.c:958
+#: wt-status.c:957
msgid " (use \"git commit\" to conclude merge)"
msgstr " (иÑпользуйте «git commit», чтобы завершить ÑлиÑние)"
-#: wt-status.c:968
+#: wt-status.c:967
msgid "You are in the middle of an am session."
msgstr "Ð’Ñ‹ в процеÑÑе ÑеÑÑии am."
-#: wt-status.c:971
+#: wt-status.c:970
msgid "The current patch is empty."
msgstr "Текущий патч пуÑтой."
-#: wt-status.c:975
+#: wt-status.c:974
msgid " (fix conflicts and then run \"git am --continue\")"
msgstr " (разрешите конфликты, затем запуÑтите «git am --continue»)"
-#: wt-status.c:977
+#: wt-status.c:976
msgid " (use \"git am --skip\" to skip this patch)"
msgstr " (иÑпользуйте «git am --skip», чтобы пропуÑтить Ñтот патч)"
-#: wt-status.c:979
+#: wt-status.c:978
msgid " (use \"git am --abort\" to restore the original branch)"
msgstr " (иÑпользуйте «git am --abort», чтобы воÑÑтановить оригинальную ветку)"
-#: wt-status.c:1039 wt-status.c:1056
+#: wt-status.c:1105
+msgid "No commands done."
+msgstr "Команды не выполнены."
+
+#: wt-status.c:1108
+#, c-format
+msgid "Last command done (%d command done):"
+msgid_plural "Last commands done (%d commands done):"
+msgstr[0] "ПоÑледнÑÑ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð° выполнена (%d команд выполнено):"
+msgstr[1] "ПоÑледнÑÑ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð° выполнена (%d команд выполнено):"
+msgstr[2] "ПоÑледнÑÑ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð° выполнена (%d команд выполнено):"
+msgstr[3] "ПоÑледнÑÑ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð° выполнена (%d команд выполнено):"
+
+#: wt-status.c:1119
+#, c-format
+msgid " (see more in file %s)"
+msgstr " (Ñмотрите дополнительно в файле %s)"
+
+#: wt-status.c:1124
+msgid "No commands remaining."
+msgstr "Команд больше не оÑталоÑÑŒ."
+
+#: wt-status.c:1127
+#, c-format
+msgid "Next command to do (%d remaining command):"
+msgid_plural "Next commands to do (%d remaining commands):"
+msgstr[0] "Ð¡Ð»ÐµÐ´ÑƒÑŽÑ‰Ð°Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð° Ð´Ð»Ñ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ (%d команда оÑталаÑÑŒ):"
+msgstr[1] "Ð¡Ð»ÐµÐ´ÑƒÑŽÑ‰Ð°Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð° Ð´Ð»Ñ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ (%d команды оÑталоÑÑŒ):"
+msgstr[2] "Ð¡Ð»ÐµÐ´ÑƒÑŽÑ‰Ð°Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð° Ð´Ð»Ñ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ (%d команд оÑталоÑÑŒ):"
+msgstr[3] "Ð¡Ð»ÐµÐ´ÑƒÑŽÑ‰Ð°Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð° Ð´Ð»Ñ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ (%d команд оÑталоÑÑŒ):"
+
+#: wt-status.c:1135
+msgid " (use \"git rebase --edit-todo\" to view and edit)"
+msgstr " (иÑпользуйте «git rebase --edit-todo», чтобы проÑмотреть и изменить)"
+
+#: wt-status.c:1148
#, c-format
msgid "You are currently rebasing branch '%s' on '%s'."
msgstr "Ð’Ñ‹ ÑÐµÐ¹Ñ‡Ð°Ñ Ð¿ÐµÑ€ÐµÐ¼ÐµÑ‰Ð°ÐµÑ‚Ðµ ветку «%s» над «%s»."
-#: wt-status.c:1044 wt-status.c:1061
+#: wt-status.c:1153
msgid "You are currently rebasing."
msgstr "Ð’Ñ‹ ÑÐµÐ¹Ñ‡Ð°Ñ Ð¿ÐµÑ€ÐµÐ¼ÐµÑ‰Ð°ÐµÑ‚Ðµ ветку."
-#: wt-status.c:1047
+#: wt-status.c:1167
msgid " (fix conflicts and then run \"git rebase --continue\")"
msgstr " (разрешите конфликты, затем запуÑтите «git rebase --continue»)"
-#: wt-status.c:1049
+#: wt-status.c:1169
msgid " (use \"git rebase --skip\" to skip this patch)"
msgstr " (иÑпользуйте «git rebase --skip», чтобы пропуÑтить Ñтот патч)"
-#: wt-status.c:1051
+#: wt-status.c:1171
msgid " (use \"git rebase --abort\" to check out the original branch)"
msgstr " (иÑпользуйте «git rebase --abort», чтобы перейти на оригинальную ветку)"
-#: wt-status.c:1064
+#: wt-status.c:1177
msgid " (all conflicts fixed: run \"git rebase --continue\")"
msgstr " (вÑе конфликты разрешены: запуÑтите «git rebase --continue»)"
-#: wt-status.c:1068
+#: wt-status.c:1181
#, c-format
msgid ""
"You are currently splitting a commit while rebasing branch '%s' on '%s'."
msgstr "Ð’Ñ‹ ÑÐµÐ¹Ñ‡Ð°Ñ Ñ€Ð°Ð·Ð´ÐµÐ»Ñете коммит при перемещении ветки «%s» над «%s»."
-#: wt-status.c:1073
+#: wt-status.c:1186
msgid "You are currently splitting a commit during a rebase."
msgstr "Ð’Ñ‹ ÑÐµÐ¹Ñ‡Ð°Ñ Ñ€Ð°Ð·Ð´ÐµÐ»Ñете коммит при перемещении ветки."
-#: wt-status.c:1076
+#: wt-status.c:1189
msgid " (Once your working directory is clean, run \"git rebase --continue\")"
msgstr "(Как только ваш рабочий каталог будет чиÑтый, запуÑтите «git rebase --continue»)"
-#: wt-status.c:1080
+#: wt-status.c:1193
#, c-format
msgid "You are currently editing a commit while rebasing branch '%s' on '%s'."
msgstr "Ð’Ñ‹ ÑÐµÐ¹Ñ‡Ð°Ñ Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¸Ñ€ÑƒÐµÑ‚Ðµ коммит при перемещении ветки «%s» над «%s»."
-#: wt-status.c:1085
+#: wt-status.c:1198
msgid "You are currently editing a commit during a rebase."
msgstr "Ð’Ñ‹ ÑÐµÐ¹Ñ‡Ð°Ñ Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¸Ñ€ÑƒÐµÑ‚Ðµ коммит при перемещении ветки."
-#: wt-status.c:1088
+#: wt-status.c:1201
msgid " (use \"git commit --amend\" to amend the current commit)"
msgstr " (иÑпользуйте «git commit --amend», чтобы иÑправить текущий коммит)"
-#: wt-status.c:1090
+#: wt-status.c:1203
msgid " (use \"git rebase --continue\" once you are satisfied with your changes)"
msgstr " (иÑпользуйте «git rebase --continue», когда будете довольны изменениÑми)"
-#: wt-status.c:1100
+#: wt-status.c:1213
#, c-format
msgid "You are currently cherry-picking commit %s."
msgstr "Ð’Ñ‹ ÑÐµÐ¹Ñ‡Ð°Ñ Ð¾Ñ‚Ð±Ð¸Ñ€Ð°ÐµÑ‚Ðµ лучший коммит %s."
-#: wt-status.c:1105
+#: wt-status.c:1218
msgid " (fix conflicts and run \"git cherry-pick --continue\")"
msgstr " (разрешите конфликты, затем запуÑтите «git cherry-pick --continue»)"
-#: wt-status.c:1108
+#: wt-status.c:1221
msgid " (all conflicts fixed: run \"git cherry-pick --continue\")"
msgstr " (вÑе конфликты разрешены: запуÑтите «git cherry-pick --continue»)"
-#: wt-status.c:1110
+#: wt-status.c:1223
msgid " (use \"git cherry-pick --abort\" to cancel the cherry-pick operation)"
msgstr " (иÑпользуйте «git cherry-pick --abort», чтобы отменить операцию отбора лучшего)"
-#: wt-status.c:1119
+#: wt-status.c:1232
#, c-format
msgid "You are currently reverting commit %s."
msgstr "Ð’Ñ‹ ÑÐµÐ¹Ñ‡Ð°Ñ Ð²Ð¾Ð·Ð²Ñ€Ð°Ñ‰Ð°ÐµÑ‚Ðµ коммит %s."
-#: wt-status.c:1124
+#: wt-status.c:1237
msgid " (fix conflicts and run \"git revert --continue\")"
msgstr " (разрешите конфликты, затем запуÑтите «git revert --continue»)"
-#: wt-status.c:1127
+#: wt-status.c:1240
msgid " (all conflicts fixed: run \"git revert --continue\")"
msgstr " (вÑе конфликты разрешены: запуÑтите «git revert --continue»)"
-#: wt-status.c:1129
+#: wt-status.c:1242
msgid " (use \"git revert --abort\" to cancel the revert operation)"
msgstr " (иÑпользуйте «git revert --abort», чтобы отменить операцию возврата)"
-#: wt-status.c:1140
+#: wt-status.c:1253
#, c-format
msgid "You are currently bisecting, started from branch '%s'."
msgstr "Ð’Ñ‹ ÑÐµÐ¹Ñ‡Ð°Ñ Ð² процеÑÑе двоичного поиÑка, начатого Ñ Ð²ÐµÑ‚ÐºÐ¸ «%s»."
-#: wt-status.c:1144
+#: wt-status.c:1257
msgid "You are currently bisecting."
msgstr "Ð’Ñ‹ ÑÐµÐ¹Ñ‡Ð°Ñ Ð² процеÑÑе двоичного поиÑка."
-#: wt-status.c:1147
+#: wt-status.c:1260
msgid " (use \"git bisect reset\" to get back to the original branch)"
msgstr " (иÑпользуйте «git bisect reset», чтобы вернутьÑÑ Ð½Ð° иÑходную ветку)"
-#: wt-status.c:1324
+#: wt-status.c:1437
msgid "On branch "
msgstr "Ðа ветке "
-#: wt-status.c:1331
+#: wt-status.c:1445
+msgid "interactive rebase in progress; onto "
+msgstr "интерактивное перемещение в процеÑÑе; над "
+
+#: wt-status.c:1447
msgid "rebase in progress; onto "
msgstr "перемещение в процеÑÑе; над "
-#: wt-status.c:1336
+#: wt-status.c:1452
msgid "HEAD detached at "
msgstr "HEAD отделен на "
-#: wt-status.c:1338
+#: wt-status.c:1454
msgid "HEAD detached from "
msgstr "HEAD отделен Ð½Ð°Ñ‡Ð¸Ð½Ð°Ñ Ñ "
-#: wt-status.c:1341
+#: wt-status.c:1457
msgid "Not currently on any branch."
msgstr "Ð¡ÐµÐ¹Ñ‡Ð°Ñ Ð½Ð¸ на одной из веток"
-#: wt-status.c:1358
+#: wt-status.c:1474
msgid "Initial commit"
msgstr "Ðачальный коммит"
-#: wt-status.c:1372
+#: wt-status.c:1488
msgid "Untracked files"
msgstr "ÐеотÑлеживаемые файлы"
-#: wt-status.c:1374
+#: wt-status.c:1490
msgid "Ignored files"
msgstr "Игнорируемые файлы"
-#: wt-status.c:1378
+#: wt-status.c:1494
#, c-format
msgid ""
"It took %.2f seconds to enumerate untracked files. 'status -uno'\n"
@@ -1896,78 +1994,78 @@ msgid ""
"new files yourself (see 'git help status')."
msgstr "%.2f Ñекунды занÑл вывод ÑпиÑка неотÑлеживаемых файлов. «status -uno» возможно может уÑкорить Ñто, но будьте внимательны, и не забудьте добавить новые файлы вручную (Ñмотрите «git help status» Ð´Ð»Ñ Ð¿Ð¾Ð´Ñ€Ð¾Ð±Ð½Ð¾Ñтей)."
-#: wt-status.c:1384
+#: wt-status.c:1500
#, c-format
msgid "Untracked files not listed%s"
msgstr "ÐеотÑлеживаемые файлы не показаны%s"
-#: wt-status.c:1386
+#: wt-status.c:1502
msgid " (use -u option to show untracked files)"
msgstr "(иÑпользуйте опцию «-u», чтобы показать неотÑлеживаемые файлы)"
-#: wt-status.c:1392
+#: wt-status.c:1508
msgid "No changes"
msgstr "Ðет изменений"
-#: wt-status.c:1397
+#: wt-status.c:1513
#, c-format
msgid "no changes added to commit (use \"git add\" and/or \"git commit -a\")\n"
msgstr "нет изменений добавленных Ð´Ð»Ñ ÐºÐ¾Ð¼Ð¼Ð¸Ñ‚Ð°\n(иÑпользуйте «git add» и/или «git commit -a»)\n"
-#: wt-status.c:1400
+#: wt-status.c:1516
#, c-format
msgid "no changes added to commit\n"
msgstr "нет изменений добавленных Ð´Ð»Ñ ÐºÐ¾Ð¼Ð¼Ð¸Ñ‚Ð°\n"
-#: wt-status.c:1403
+#: wt-status.c:1519
#, c-format
msgid ""
"nothing added to commit but untracked files present (use \"git add\" to "
"track)\n"
msgstr "ничего не добавлено в коммит, но еÑÑ‚ÑŒ неотÑлеживаемые файлы (иÑпользуйте \"git add\", чтобы отÑлеживать их)\n"
-#: wt-status.c:1406
+#: wt-status.c:1522
#, c-format
msgid "nothing added to commit but untracked files present\n"
msgstr "ничего не добавлено в коммит, но еÑÑ‚ÑŒ неотÑлеживаемые файлы\n"
-#: wt-status.c:1409
+#: wt-status.c:1525
#, c-format
msgid "nothing to commit (create/copy files and use \"git add\" to track)\n"
msgstr "нечего коммитить (Ñоздайте/Ñкопируйте файлы, затем запуÑтите «git add», чтобы отÑлеживать их)\n"
-#: wt-status.c:1412 wt-status.c:1417
+#: wt-status.c:1528 wt-status.c:1533
#, c-format
msgid "nothing to commit\n"
msgstr "нечего коммитить\n"
-#: wt-status.c:1415
+#: wt-status.c:1531
#, c-format
msgid "nothing to commit (use -u to show untracked files)\n"
msgstr "нечего коммитить (иÑпользуйте опцию «-u», чтобы показать неотÑлеживаемые файлы)\n"
-#: wt-status.c:1419
+#: wt-status.c:1535
#, c-format
msgid "nothing to commit, working directory clean\n"
msgstr "нечего коммитить, нет изменений в рабочем каталоге\n"
-#: wt-status.c:1528
+#: wt-status.c:1644
msgid "HEAD (no branch)"
msgstr "HEAD (нет ветки)"
-#: wt-status.c:1534
+#: wt-status.c:1650
msgid "Initial commit on "
msgstr "Ðачальный коммит на "
-#: wt-status.c:1561
+#: wt-status.c:1677
msgid "gone"
msgstr "иÑчез"
-#: wt-status.c:1563 wt-status.c:1571
+#: wt-status.c:1679 wt-status.c:1687
msgid "behind "
msgstr "позади"
-#: compat/precompose_utf8.c:55 builtin/clone.c:345
+#: compat/precompose_utf8.c:55 builtin/clone.c:403
#, c-format
msgid "failed to unlink '%s'"
msgstr "Ñбой отÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ Â«%s»"
@@ -1994,7 +2092,7 @@ msgstr "удалить «%s»\n"
msgid "Unstaged changes after refreshing the index:"
msgstr "ÐепроиндекÑированные Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð¿Ð¾Ñле Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð¸Ð½Ð´ÐµÐºÑа:"
-#: builtin/add.c:194 builtin/rev-parse.c:796
+#: builtin/add.c:194 builtin/rev-parse.c:799
msgid "Could not read the index"
msgstr "Ðе удалоÑÑŒ прочитать индекÑ"
@@ -2029,15 +2127,15 @@ msgstr "Ðе удалоÑÑŒ применить «%s»"
msgid "The following paths are ignored by one of your .gitignore files:\n"
msgstr "Следующие пути игнорируютÑÑ Ð¾Ð´Ð½Ð¸Ð¼ из ваших файлов .gitignore:\n"
-#: builtin/add.c:249 builtin/clean.c:874 builtin/fetch.c:107 builtin/mv.c:110
-#: builtin/prune-packed.c:55 builtin/push.c:508 builtin/remote.c:1369
-#: builtin/rm.c:268
+#: builtin/add.c:249 builtin/clean.c:896 builtin/fetch.c:108 builtin/mv.c:110
+#: builtin/prune-packed.c:55 builtin/pull.c:182 builtin/push.c:545
+#: builtin/remote.c:1339 builtin/rm.c:268 builtin/send-pack.c:162
msgid "dry run"
msgstr "пробный запуÑк"
#: builtin/add.c:250 builtin/apply.c:4580 builtin/check-ignore.c:19
-#: builtin/commit.c:1322 builtin/count-objects.c:63 builtin/fsck.c:616
-#: builtin/log.c:1617 builtin/mv.c:109 builtin/read-tree.c:114
+#: builtin/commit.c:1321 builtin/count-objects.c:63 builtin/fsck.c:636
+#: builtin/log.c:1641 builtin/mv.c:109 builtin/read-tree.c:114
msgid "be verbose"
msgstr "быть многоÑловнее"
@@ -2045,7 +2143,7 @@ msgstr "быть многоÑловнее"
msgid "interactive picking"
msgstr "интерактивный выбор"
-#: builtin/add.c:253 builtin/checkout.c:1221 builtin/reset.c:286
+#: builtin/add.c:253 builtin/checkout.c:1152 builtin/reset.c:286
msgid "select hunks interactively"
msgstr "интерактивный выбор блоков"
@@ -2112,15 +2210,403 @@ msgstr "Ðичего не указано, ничего не добавлено.\
msgid "Maybe you wanted to say 'git add .'?\n"
msgstr "Возможно, вы имели в виду «git add .»?\n"
-#: builtin/add.c:364 builtin/check-ignore.c:172 builtin/clean.c:918
-#: builtin/commit.c:335 builtin/mv.c:130 builtin/reset.c:235 builtin/rm.c:298
+#: builtin/add.c:364 builtin/check-ignore.c:172 builtin/clean.c:940
+#: builtin/commit.c:336 builtin/mv.c:130 builtin/reset.c:235 builtin/rm.c:298
msgid "index file corrupt"
msgstr "файл индекÑа поврежден"
-#: builtin/add.c:447 builtin/apply.c:4678 builtin/mv.c:279 builtin/rm.c:430
+#: builtin/add.c:445 builtin/apply.c:4678 builtin/mv.c:279 builtin/rm.c:430
msgid "Unable to write new index file"
msgstr "Ðе удалоÑÑŒ запиÑать новый файл индекÑа"
+#: builtin/am.c:41
+#, c-format
+msgid "could not stat %s"
+msgstr "не удалоÑÑŒ выполнить stat Ð´Ð»Ñ %s"
+
+#: builtin/am.c:270 builtin/am.c:1345 builtin/commit.c:737
+#: builtin/merge.c:1079
+#, c-format
+msgid "could not read '%s'"
+msgstr "не удалоÑÑŒ прочитать «%s»"
+
+#: builtin/am.c:444
+msgid "could not parse author script"
+msgstr "не удалоÑÑŒ разобрать Ñценарий авторÑтва"
+
+#: builtin/am.c:521
+#, c-format
+msgid "'%s' was deleted by the applypatch-msg hook"
+msgstr "«%s» был удален перехватчиком applypatch-msg"
+
+#: builtin/am.c:562 builtin/notes.c:300
+#, c-format
+msgid "Malformed input line: '%s'."
+msgstr "ÐŸÐ»Ð¾Ñ…Ð°Ñ Ñтрока ввода: «%s»."
+
+#: builtin/am.c:599 builtin/notes.c:315
+#, c-format
+msgid "Failed to copy notes from '%s' to '%s'"
+msgstr "Ðе удалоÑÑŒ Ñкопировать заметку из «%s» в «%s»"
+
+#: builtin/am.c:625
+msgid "fseek failed"
+msgstr "Ñбой при выполнении fseek"
+
+#: builtin/am.c:786 builtin/am.c:874
+#, c-format
+msgid "could not open '%s' for reading: %s"
+msgstr "не удалоÑÑŒ открыть «%s» Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ: %s"
+
+#: builtin/am.c:793
+#, c-format
+msgid "could not open '%s' for writing: %s"
+msgstr "не удалоÑÑŒ открыть «%s» Ð´Ð»Ñ Ð·Ð°Ð¿Ð¸Ñи: %s"
+
+#: builtin/am.c:802
+#, c-format
+msgid "could not parse patch '%s'"
+msgstr "не удалоÑÑŒ разобрать патч «%s»"
+
+#: builtin/am.c:867
+msgid "Only one StGIT patch series can be applied at once"
+msgstr "Только ÑÐµÑ€Ð¸Ñ Ð¿Ð°Ñ‚Ñ‡ÐµÐ¹ StGIT может быть применена за раз"
+
+#: builtin/am.c:915
+msgid "invalid timestamp"
+msgstr "недопуÑÑ‚Ð¸Ð¼Ð°Ñ Ð¼ÐµÑ‚ÐºÐ° даты/времени"
+
+#: builtin/am.c:918 builtin/am.c:926
+msgid "invalid Date line"
+msgstr "недопуÑÑ‚Ð¸Ð¼Ð°Ñ Ñтрока даты"
+
+#: builtin/am.c:923
+msgid "invalid timezone offset"
+msgstr "недопуÑтимое Ñмещение чаÑового поÑÑа"
+
+#: builtin/am.c:1010
+msgid "Patch format detection failed."
+msgstr "Сбой Ð¾Ð¿Ñ€ÐµÐ´ÐµÐ»ÐµÐ½Ð¸Ñ Ñ„Ð¾Ñ€Ð¼Ð°Ñ‚Ð° патча."
+
+#: builtin/am.c:1015 builtin/clone.c:368
+#, c-format
+msgid "failed to create directory '%s'"
+msgstr "не удалоÑÑŒ Ñоздать каталог «%s»"
+
+#: builtin/am.c:1019
+msgid "Failed to split patches."
+msgstr "Ðе удалоÑÑŒ разделить патчи на чаÑти."
+
+#: builtin/am.c:1151 builtin/commit.c:362
+msgid "unable to write index file"
+msgstr "не удалоÑÑŒ запиÑать индекÑ"
+
+#: builtin/am.c:1202
+#, c-format
+msgid "When you have resolved this problem, run \"%s --continue\"."
+msgstr "Когда вы уÑтраните Ñту проблему, запуÑтите «%s --continue»."
+
+#: builtin/am.c:1203
+#, c-format
+msgid "If you prefer to skip this patch, run \"%s --skip\" instead."
+msgstr "ЕÑли вы хотите пропуÑтить Ñтот патч, то запуÑтите «%s --skip»."
+
+#: builtin/am.c:1204
+#, c-format
+msgid "To restore the original branch and stop patching, run \"%s --abort\"."
+msgstr "Чтобы вернутьÑÑ Ð½Ð° предыдущую ветку и оÑтановить применение изменений, запуÑтите «%s --abort»."
+
+#: builtin/am.c:1339
+msgid "Patch is empty. Was it split wrong?"
+msgstr "Патч пуÑÑ‚. Возможно, он был неправильно разделён?"
+
+#: builtin/am.c:1413 builtin/log.c:1345
+#, c-format
+msgid "invalid ident line: %s"
+msgstr "Ð½ÐµÐ¿Ñ€Ð°Ð²Ð¸Ð»ÑŒÐ½Ð°Ñ Ñтрока идентификации: %s"
+
+#: builtin/am.c:1440
+#, c-format
+msgid "unable to parse commit %s"
+msgstr "не удалоÑÑŒ разобрать коммит %s"
+
+#: builtin/am.c:1614
+msgid "Repository lacks necessary blobs to fall back on 3-way merge."
+msgstr "Ð’ репозитории отÑутÑтвуют двоичные объекты, необходимые Ð´Ð»Ñ Ð¾Ñ‚ÐºÐ°Ñ‚Ð° к трехходовому ÑлиÑнию."
+
+#: builtin/am.c:1616
+msgid "Using index info to reconstruct a base tree..."
+msgstr "ИÑпользую Ð¸Ð½Ð´ÐµÐºÑ Ð´Ð»Ñ Ñ€ÐµÐºÐ¾Ð½Ñтрукции базового дерева…"
+
+#: builtin/am.c:1635
+msgid ""
+"Did you hand edit your patch?\n"
+"It does not apply to blobs recorded in its index."
+msgstr "Ð’Ñ‹ вручную изменÑли патч?\nОн не накладываетÑÑ Ð±ÐµÐ· ошибок на двоичные объекты, запиÑанные в его заголовке."
+
+#: builtin/am.c:1641
+msgid "Falling back to patching base and 3-way merge..."
+msgstr "Откат к применению изменений к базовому коммиту Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ трехходового ÑлиÑниÑ…"
+
+#: builtin/am.c:1666
+msgid "Failed to merge in the changes."
+msgstr "Ðе удалоÑÑŒ Ñлить изменениÑ."
+
+#: builtin/am.c:1691 builtin/merge.c:632
+msgid "git write-tree failed to write a tree"
+msgstr "git write-tree не удалоÑÑŒ запиÑать дерево"
+
+#: builtin/am.c:1698
+msgid "applying to an empty history"
+msgstr "применение к пуÑтой иÑтории"
+
+#: builtin/am.c:1711 builtin/commit.c:1752 builtin/merge.c:829
+#: builtin/merge.c:854
+msgid "failed to write commit object"
+msgstr "Ñбой запиÑи объекта коммита"
+
+#: builtin/am.c:1743 builtin/am.c:1747
+#, c-format
+msgid "cannot resume: %s does not exist."
+msgstr "Ð½ÐµÐ»ÑŒÐ·Ñ Ð¿Ñ€Ð¾Ð´Ð¾Ð»Ð¶Ð½Ð¸Ñ‚ÑŒ: %s не ÑущеÑтвует "
+
+#: builtin/am.c:1763
+msgid "cannot be interactive without stdin connected to a terminal."
+msgstr "не удалоÑÑŒ иÑпользовать интерактивное поведение, без stdin подключенного к терминалу."
+
+#: builtin/am.c:1768
+msgid "Commit Body is:"
+msgstr "Тело коммита:"
+
+#. TRANSLATORS: Make sure to include [y], [n], [e], [v] and [a]
+#. in your translation. The program will only accept English
+#. input at this point.
+#: builtin/am.c:1778
+msgid "Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all: "
+msgstr "Применить? [y] - да/[n] - нет/[e] - редактировать/[v] - проÑмотреть патч/[a] - применить вÑÑ‘: "
+
+#: builtin/am.c:1828
+#, c-format
+msgid "Dirty index: cannot apply patches (dirty: %s)"
+msgstr "Ð˜Ð½Ð´ÐµÐºÑ Ð½Ðµ пуÑтой: Ð½ÐµÐ»ÑŒÐ·Ñ Ð¿Ñ€Ð¸Ð¼ÐµÐ½ÑÑ‚ÑŒ патчи (в индекÑе: %s)"
+
+#: builtin/am.c:1863 builtin/am.c:1934
+#, c-format
+msgid "Applying: %.*s"
+msgstr "Применение: %.*s"
+
+#: builtin/am.c:1879
+msgid "No changes -- Patch already applied."
+msgstr "Ðет изменений — Патч уже применен."
+
+#: builtin/am.c:1887
+#, c-format
+msgid "Patch failed at %s %.*s"
+msgstr "Ошибка Ð¿Ñ€Ð¸Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ð¹ на %s %.*s"
+
+#: builtin/am.c:1893
+#, c-format
+msgid "The copy of the patch that failed is found in: %s"
+msgstr "Копию изменений, которые не удалоÑÑŒ применить, вы можете найти в: %s"
+
+#: builtin/am.c:1937
+msgid ""
+"No changes - did you forget to use 'git add'?\n"
+"If there is nothing left to stage, chances are that something else\n"
+"already introduced the same changes; you might want to skip this patch."
+msgstr "Ðет изменений — возможно, вы забыли вызвать «git add»?\nЕÑли ничего не оÑталоÑÑŒ Ð´Ð»Ñ Ð¸Ð½Ð´ÐµÐºÑации, то, Ñкорее вÑего, что-то другое уже Ñделало те же изменениÑ; возможно, вам Ñледует пропуÑтить Ñтот патч."
+
+#: builtin/am.c:1944
+msgid ""
+"You still have unmerged paths in your index.\n"
+"Did you forget to use 'git add'?"
+msgstr "У Ð²Ð°Ñ Ð²Ñе еще имеютÑÑ Ð½Ðµ Ñлитые пути в индекÑе.\nВозможно, вы забыли вызвать «git add»?"
+
+#: builtin/am.c:2052 builtin/am.c:2056 builtin/am.c:2068 builtin/reset.c:308
+#: builtin/reset.c:316
+#, c-format
+msgid "Could not parse object '%s'."
+msgstr "Ðе удалоÑÑŒ разобрать объект «%s»."
+
+#: builtin/am.c:2104
+msgid "failed to clean index"
+msgstr "не удалоÑÑŒ очиÑтить индекÑ"
+
+#: builtin/am.c:2138
+msgid ""
+"You seem to have moved HEAD since the last 'am' failure.\n"
+"Not rewinding to ORIG_HEAD"
+msgstr "Похоже, что вы перемеÑтили HEAD Ñ Ð¼Ð¾Ð¼ÐµÐ½Ñ‚Ð° поÑледней ошибки Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Â«am».\nПеремотка на ORIG_HEAD не выполнÑетÑÑ"
+
+#: builtin/am.c:2199
+#, c-format
+msgid "Invalid value for --patch-format: %s"
+msgstr "Ðеправильное значение Ð´Ð»Ñ --patch-format: %s"
+
+#: builtin/am.c:2221
+msgid "git am [options] [(<mbox>|<Maildir>)...]"
+msgstr "git am [опции] [(<mbox>|<Maildir>)…]"
+
+#: builtin/am.c:2222
+msgid "git am [options] (--continue | --skip | --abort)"
+msgstr "git am [опции] (--continue | --skip | --abort)"
+
+#: builtin/am.c:2228
+msgid "run interactively"
+msgstr "запуÑтить в интерактивном режиме"
+
+#: builtin/am.c:2230
+msgid "historical option -- no-op"
+msgstr "иÑторичеÑÐºÐ°Ñ Ð¾Ð¿Ñ†Ð¸Ñ â€” ничего не делает"
+
+#: builtin/am.c:2232
+msgid "allow fall back on 3way merging if needed"
+msgstr "разрешить откатитьÑÑ Ðº трехходовому ÑлиÑнию, еÑли нужно"
+
+#: builtin/am.c:2233 builtin/init-db.c:509 builtin/prune-packed.c:57
+#: builtin/repack.c:171
+msgid "be quiet"
+msgstr "тихий режим"
+
+#: builtin/am.c:2235
+msgid "add a Signed-off-by line to the commit message"
+msgstr "добавить Ñтроку Signed-off-by к Ñообщению коммита"
+
+#: builtin/am.c:2238
+msgid "recode into utf8 (default)"
+msgstr "перекодировать в utf8 (по умолчанию)"
+
+#: builtin/am.c:2240
+msgid "pass -k flag to git-mailinfo"
+msgstr "передать флаг -k в git-mailinfo"
+
+#: builtin/am.c:2242
+msgid "pass -b flag to git-mailinfo"
+msgstr "передать флаг -b в git-mailinfo"
+
+#: builtin/am.c:2244
+msgid "pass -m flag to git-mailinfo"
+msgstr "передать флаг -m в git-mailinfo"
+
+#: builtin/am.c:2246
+msgid "pass --keep-cr flag to git-mailsplit for mbox format"
+msgstr "передать флаг --keep-cr в git-mailsplit Ð´Ð»Ñ Ñ„Ð¾Ñ€Ð¼Ð°Ñ‚Ð° mbox"
+
+#: builtin/am.c:2249
+msgid "do not pass --keep-cr flag to git-mailsplit independent of am.keepcr"
+msgstr "не передавать --keep-cr флаг в git-mailsplit вне завиÑимоÑти от am.keepcr"
+
+#: builtin/am.c:2252
+msgid "strip everything before a scissors line"
+msgstr "обрезать вÑе до Ñтроки обрезки"
+
+#: builtin/am.c:2253 builtin/apply.c:4563
+msgid "action"
+msgstr "дейÑтвие"
+
+#: builtin/am.c:2254 builtin/am.c:2257 builtin/am.c:2260 builtin/am.c:2263
+#: builtin/am.c:2266 builtin/am.c:2269 builtin/am.c:2272 builtin/am.c:2275
+#: builtin/am.c:2281
+msgid "pass it through git-apply"
+msgstr "передать его в git-apply"
+
+#: builtin/am.c:2262 builtin/apply.c:4587
+msgid "root"
+msgstr "корень"
+
+#: builtin/am.c:2265 builtin/am.c:2268 builtin/apply.c:4525
+#: builtin/apply.c:4528 builtin/clone.c:85 builtin/fetch.c:93
+#: builtin/pull.c:167
+msgid "path"
+msgstr "путь"
+
+#: builtin/am.c:2271 builtin/fmt-merge-msg.c:669 builtin/fmt-merge-msg.c:672
+#: builtin/grep.c:698 builtin/merge.c:198 builtin/pull.c:127
+#: builtin/repack.c:178 builtin/repack.c:182 builtin/show-branch.c:664
+#: builtin/show-ref.c:180 builtin/tag.c:591 parse-options.h:132
+#: parse-options.h:134 parse-options.h:243
+msgid "n"
+msgstr "n"
+
+#: builtin/am.c:2274 builtin/apply.c:4531
+msgid "num"
+msgstr "количеÑтво"
+
+#: builtin/am.c:2277 builtin/for-each-ref.c:34 builtin/replace.c:438
+msgid "format"
+msgstr "формат"
+
+#: builtin/am.c:2278
+msgid "format the patch(es) are in"
+msgstr "формат, в котором находÑÑ‚ÑÑ Ð¿Ð°Ñ‚Ñ‡Ð¸"
+
+#: builtin/am.c:2284
+msgid "override error message when patch failure occurs"
+msgstr "переопределить Ñообщение об ошибке, еÑли не удалоÑÑŒ наложить изменениÑ"
+
+#: builtin/am.c:2286
+msgid "continue applying patches after resolving a conflict"
+msgstr "продолжить применение изменений поÑле Ñ€Ð°Ð·Ñ€ÐµÑˆÐµÐ½Ð¸Ñ ÐºÐ¾Ð½Ñ„Ð¸Ð»Ð¸ÐºÑ‚Ð°"
+
+#: builtin/am.c:2289
+msgid "synonyms for --continue"
+msgstr "Ñинонимы Ð´Ð»Ñ --continue"
+
+#: builtin/am.c:2292
+msgid "skip the current patch"
+msgstr "пропуÑтить текущий патч"
+
+#: builtin/am.c:2295
+msgid "restore the original branch and abort the patching operation."
+msgstr "воÑÑтановить оригинальную ветку и отменить операцию Ð¿Ñ€Ð¸Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ð¹."
+
+#: builtin/am.c:2299
+msgid "lie about committer date"
+msgstr "Ñоврать о дате коммитера"
+
+#: builtin/am.c:2301
+msgid "use current timestamp for author date"
+msgstr "иÑпользовать текущее Ð²Ñ€ÐµÐ¼Ñ ÐºÐ°Ðº Ð²Ñ€ÐµÐ¼Ñ Ð°Ð²Ñ‚Ð¾Ñ€Ñтва"
+
+#: builtin/am.c:2303 builtin/commit.c:1590 builtin/merge.c:225
+#: builtin/pull.c:155 builtin/revert.c:92 builtin/tag.c:606
+msgid "key-id"
+msgstr "key-id"
+
+#: builtin/am.c:2304
+msgid "GPG-sign commits"
+msgstr "подпиÑать коммиты Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ GPG"
+
+#: builtin/am.c:2307
+msgid "(internal use for git-rebase)"
+msgstr "(внутреннее иÑпользование Ð´Ð»Ñ git-rebase)"
+
+#: builtin/am.c:2322
+msgid ""
+"The -b/--binary option has been a no-op for long time, and\n"
+"it will be removed. Please do not use it anymore."
+msgstr "ÐžÐ¿Ñ†Ð¸Ñ -b/--binary уже долгое Ð²Ñ€ÐµÐ¼Ñ Ð½Ð¸Ñ‡ÐµÐ³Ð¾ не делает и будет удалена Ñ Ñледующих верÑиÑÑ… Git. ПожалуйÑта, не иÑпользуйте ее."
+
+#: builtin/am.c:2329
+msgid "failed to read the index"
+msgstr "Ñбой Ñ‡Ñ‚ÐµÐ½Ð¸Ñ Ð¸Ð½Ð´ÐµÐºÑа"
+
+#: builtin/am.c:2344
+#, c-format
+msgid "previous rebase directory %s still exists but mbox given."
+msgstr "предыдущий каталог Ð¿ÐµÑ€ÐµÐ¼ÐµÑ‰ÐµÐ½Ð¸Ñ %s еще ÑущеÑтвует, но передан mbox."
+
+#: builtin/am.c:2368
+#, c-format
+msgid ""
+"Stray %s directory found.\n"
+"Use \"git am --abort\" to remove it."
+msgstr "Ðайден забытый каталог %s.\nИÑпользуйте «git am --abort», чтобы удалить его."
+
+#: builtin/am.c:2374
+msgid "Resolve operation not in progress, we are not resuming."
+msgstr "ÐžÐ¿ÐµÑ€Ð°Ñ†Ð¸Ñ Ñ€Ð°Ð·Ñ€ÐµÑˆÐµÐ½Ð¸Ñ ÐºÐ¾Ð½Ñ„Ð»Ð¸ÐºÑ‚Ð¾Ð² не в процеÑÑе выполнениÑ, не продолжаем."
+
#: builtin/apply.c:59
msgid "git apply [<options>] [<patch>...]"
msgstr "git apply [<опции>] [<патч>…]"
@@ -2381,7 +2867,7 @@ msgstr "%s: не удалоÑÑŒ применить патч"
msgid "Checking patch %s..."
msgstr "Проверка патча %s…"
-#: builtin/apply.c:3909 builtin/checkout.c:233 builtin/reset.c:135
+#: builtin/apply.c:3909 builtin/checkout.c:232 builtin/reset.c:135
#, c-format
msgid "make_cache_entry failed for path '%s'"
msgstr "Ñбой make_cache_entry Ð´Ð»Ñ Ð¿ÑƒÑ‚Ð¸ «%s»"
@@ -2462,11 +2948,6 @@ msgstr "не раÑпознанный ввод"
msgid "unable to read index file"
msgstr "не удалоÑÑŒ прочитать файл индекÑа"
-#: builtin/apply.c:4525 builtin/apply.c:4528 builtin/clone.c:85
-#: builtin/fetch.c:92
-msgid "path"
-msgstr "путь"
-
#: builtin/apply.c:4526
msgid "don't apply changes matching the given path"
msgstr "не применÑÑ‚ÑŒ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð¿Ð¾ указанному пути"
@@ -2475,10 +2956,6 @@ msgstr "не применÑÑ‚ÑŒ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð¿Ð¾ указанному пÑ
msgid "apply changes matching the given path"
msgstr "применÑÑ‚ÑŒ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð¿Ð¾ указанному пути"
-#: builtin/apply.c:4531
-msgid "num"
-msgstr "количеÑтво"
-
#: builtin/apply.c:4532
msgid "remove <num> leading slashes from traditional diff paths"
msgstr "удалить <количеÑтво> ведущих коÑÑ‹Ñ… черт из традиционных путей ÑпиÑка изменений"
@@ -2535,10 +3012,6 @@ msgstr "пути, отделенные ÐУЛЕВЫМ Ñимволом"
msgid "ensure at least <n> lines of context match"
msgstr "удоÑтоверитьÑÑ, что по крайней мере <n> Ñтрок контекÑта Ñовпадают"
-#: builtin/apply.c:4563
-msgid "action"
-msgstr "дейÑтвие"
-
#: builtin/apply.c:4564
msgid "detect new or modified lines that have whitespace errors"
msgstr "определÑÑ‚ÑŒ новые или модифицированные Ñтроки, у которых еÑÑ‚ÑŒ ошибки в пробельных Ñимволах"
@@ -2571,10 +3044,6 @@ msgstr "разрешить некорректно определенные прÐ
msgid "do not trust the line counts in the hunk headers"
msgstr "не доверÑÑ‚ÑŒ количеÑтву Ñтрок из заголовка блока изменений"
-#: builtin/apply.c:4587
-msgid "root"
-msgstr "корень"
-
#: builtin/apply.c:4588
msgid "prepend <root> to all filenames"
msgstr "добавить <корень> Ñпереди ко вÑем именам файлов"
@@ -2661,11 +3130,11 @@ msgstr "выполнить «git bisect next»"
msgid "update BISECT_HEAD instead of checking out the current commit"
msgstr "обновить BISECT_HEAD вмеÑто перехода на текущий коммит"
-#: builtin/blame.c:31
+#: builtin/blame.c:32
msgid "git blame [<options>] [<rev-opts>] [<rev>] [--] <file>"
msgstr "git blame [<опции>] [<опции-редакции>] [<редакциÑ>] [--] <файл>"
-#: builtin/blame.c:36
+#: builtin/blame.c:37
msgid "<rev-opts> are documented in git-rev-list(1)"
msgstr "<опции-rev-list> документированы в git-rev-list(1)"
@@ -2840,323 +3309,323 @@ msgstr "внешнÑÑ Ð¾Ñ‚ÑÐ»ÐµÐ¶Ð¸Ð²Ð°ÐµÐ¼Ð°Ñ Ð²ÐµÑ‚ÐºÐ° «%s» не найÐ
msgid "branch '%s' not found."
msgstr "ветка «%s» не найдена."
-#: builtin/branch.c:258
+#: builtin/branch.c:259
#, c-format
msgid "Error deleting remote-tracking branch '%s'"
msgstr "Ошибка ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ Ð²Ð½ÐµÑˆÐ½ÐµÐ¹ отÑлеживаемой ветки «%s»"
-#: builtin/branch.c:259
+#: builtin/branch.c:260
#, c-format
msgid "Error deleting branch '%s'"
msgstr "Ошибка ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ Ð²ÐµÑ‚ÐºÐ¸ «%s»"
-#: builtin/branch.c:266
+#: builtin/branch.c:267
#, c-format
msgid "Deleted remote-tracking branch %s (was %s).\n"
msgstr "ВнешнÑÑ Ð¾Ñ‚ÑÐ»ÐµÐ¶Ð¸Ð²Ð°ÐµÐ¼Ð°Ñ Ð²ÐµÑ‚ÐºÐ° %s удалена (была %s).\n"
-#: builtin/branch.c:267
+#: builtin/branch.c:268
#, c-format
msgid "Deleted branch %s (was %s).\n"
msgstr "Ветка %s удалена (была %s).\n"
-#: builtin/branch.c:368
+#: builtin/branch.c:369
#, c-format
msgid "branch '%s' does not point at a commit"
msgstr "ветка «%s» не указывает на коммит"
-#: builtin/branch.c:451
+#: builtin/branch.c:452
#, c-format
msgid "[%s: gone]"
msgstr "[%s: пропал]"
-#: builtin/branch.c:456
+#: builtin/branch.c:457
#, c-format
msgid "[%s]"
msgstr "[%s]"
-#: builtin/branch.c:461
+#: builtin/branch.c:462
#, c-format
msgid "[%s: behind %d]"
msgstr "[%s: позади %d]"
-#: builtin/branch.c:463
+#: builtin/branch.c:464
#, c-format
msgid "[behind %d]"
msgstr "[позади %d]"
-#: builtin/branch.c:467
+#: builtin/branch.c:468
#, c-format
msgid "[%s: ahead %d]"
msgstr "[%s: впереди %d]"
-#: builtin/branch.c:469
+#: builtin/branch.c:470
#, c-format
msgid "[ahead %d]"
msgstr "[впереди %d]"
-#: builtin/branch.c:472
+#: builtin/branch.c:473
#, c-format
msgid "[%s: ahead %d, behind %d]"
msgstr "[%s: впереди %d, позади %d]"
-#: builtin/branch.c:475
+#: builtin/branch.c:476
#, c-format
msgid "[ahead %d, behind %d]"
msgstr "[впереди %d, позади %d]"
-#: builtin/branch.c:488
+#: builtin/branch.c:489
msgid " **** invalid ref ****"
msgstr " **** недейÑÑ‚Ð²Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð°Ñ ÑÑылка ****"
-#: builtin/branch.c:579
+#: builtin/branch.c:580
#, c-format
msgid "(no branch, rebasing %s)"
msgstr "(нет ветки, перемещение %s)"
-#: builtin/branch.c:582
+#: builtin/branch.c:583
#, c-format
msgid "(no branch, bisect started on %s)"
msgstr "(нет ветки, двоичный поиÑк начат на %s)"
-#: builtin/branch.c:588
+#: builtin/branch.c:589
#, c-format
msgid "(HEAD detached at %s)"
msgstr "(HEAD отделён на %s)"
-#: builtin/branch.c:591
+#: builtin/branch.c:592
#, c-format
msgid "(HEAD detached from %s)"
msgstr "(HEAD отделён Ð½Ð°Ñ‡Ð¸Ð½Ð°Ñ Ñ %s)"
-#: builtin/branch.c:595
+#: builtin/branch.c:596
msgid "(no branch)"
msgstr "(нет ветки)"
-#: builtin/branch.c:642
+#: builtin/branch.c:643
#, c-format
msgid "object '%s' does not point to a commit"
msgstr "объект «%s» не указывает на коммит"
-#: builtin/branch.c:690
+#: builtin/branch.c:691
msgid "some refs could not be read"
msgstr "не удаетÑÑ Ð¿Ñ€Ð¾Ñ‡Ð¸Ñ‚Ð°Ñ‚ÑŒ некоторые ÑÑылки"
-#: builtin/branch.c:703
+#: builtin/branch.c:704
msgid "cannot rename the current branch while not on any."
msgstr "невозможно переименовать текущую ветку, еÑли вы не находитеÑÑŒ ни на одной из них."
-#: builtin/branch.c:713
+#: builtin/branch.c:714
#, c-format
msgid "Invalid branch name: '%s'"
msgstr "ÐедейÑтвительное Ð¸Ð¼Ñ Ð²ÐµÑ‚ÐºÐ¸: «%s»"
-#: builtin/branch.c:728
+#: builtin/branch.c:729
msgid "Branch rename failed"
msgstr "Сбой Ð¿ÐµÑ€ÐµÐ¸Ð¼ÐµÐ½Ð¾Ð²Ð°Ð½Ð¸Ñ Ð²ÐµÑ‚ÐºÐ¸"
-#: builtin/branch.c:732
+#: builtin/branch.c:733
#, c-format
msgid "Renamed a misnamed branch '%s' away"
msgstr "Переименована неправильно Ð½Ð°Ð·Ð²Ð°Ð½Ð½Ð°Ñ Ð²ÐµÑ‚ÐºÐ° «%s»"
-#: builtin/branch.c:736
+#: builtin/branch.c:737
#, c-format
msgid "Branch renamed to %s, but HEAD is not updated!"
msgstr "Ветка переименована в %s, но HEAD не обновлен!"
-#: builtin/branch.c:743
+#: builtin/branch.c:744
msgid "Branch is renamed, but update of config-file failed"
msgstr "Ветка переименована, но произошел Ñбой Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ñ„Ð°Ð¹Ð»Ð° конфигурации"
-#: builtin/branch.c:758
+#: builtin/branch.c:759
#, c-format
msgid "malformed object name %s"
msgstr "плохое Ð¸Ð¼Ñ Ð¾Ð±ÑŠÐµÐºÑ‚Ð° %s"
-#: builtin/branch.c:780
+#: builtin/branch.c:781
#, c-format
msgid "could not write branch description template: %s"
msgstr "не удалоÑÑŒ запиÑать шаблон опиÑÐ°Ð½Ð¸Ñ Ð²ÐµÑ‚ÐºÐ¸: %s"
-#: builtin/branch.c:810
+#: builtin/branch.c:811
msgid "Generic options"
msgstr "Общие параметры"
-#: builtin/branch.c:812
+#: builtin/branch.c:813
msgid "show hash and subject, give twice for upstream branch"
msgstr "показывать хеш-Ñумму и тему, укажите дважды Ð´Ð»Ñ Ð²Ñ‹ÑˆÐµÑтоÑщей ветки"
-#: builtin/branch.c:813
+#: builtin/branch.c:814
msgid "suppress informational messages"
msgstr "не выводить информационные ÑообщениÑ"
-#: builtin/branch.c:814
+#: builtin/branch.c:815
msgid "set up tracking mode (see git-pull(1))"
msgstr "уÑтановить режим отÑÐ»ÐµÐ¶Ð¸Ð²Ð°Ð½Ð¸Ñ Ð²Ñ‹ÑˆÐµÑтоÑщей ветки (Ñм. git-pull(1))"
-#: builtin/branch.c:816
+#: builtin/branch.c:817
msgid "change upstream info"
msgstr "изменить информацию о вышеÑтоÑщей ветке"
-#: builtin/branch.c:820
+#: builtin/branch.c:821
msgid "use colored output"
msgstr "иÑпользовать цветной вывод"
-#: builtin/branch.c:821
+#: builtin/branch.c:822
msgid "act on remote-tracking branches"
msgstr "выполнить дейÑÑ‚Ð²Ð¸Ñ Ð½Ð° отÑлеживаемых внешних ветках"
-#: builtin/branch.c:824 builtin/branch.c:830 builtin/branch.c:851
-#: builtin/branch.c:857 builtin/commit.c:1581 builtin/commit.c:1582
-#: builtin/commit.c:1583 builtin/commit.c:1584 builtin/tag.c:616
-#: builtin/tag.c:622
+#: builtin/branch.c:825 builtin/branch.c:831 builtin/branch.c:852
+#: builtin/branch.c:858 builtin/commit.c:1580 builtin/commit.c:1581
+#: builtin/commit.c:1582 builtin/commit.c:1583 builtin/tag.c:618
+#: builtin/tag.c:624
msgid "commit"
msgstr "коммит"
-#: builtin/branch.c:825 builtin/branch.c:831
+#: builtin/branch.c:826 builtin/branch.c:832
msgid "print only branches that contain the commit"
msgstr "вывод только веток, которые Ñодержат коммит"
-#: builtin/branch.c:837
+#: builtin/branch.c:838
msgid "Specific git-branch actions:"
msgstr "Специфичные Ð´Ð»Ñ git-branch дейÑтвиÑ:"
-#: builtin/branch.c:838
+#: builtin/branch.c:839
msgid "list both remote-tracking and local branches"
msgstr "показать ÑпиÑок и отÑлеживаемых и локальных веток"
-#: builtin/branch.c:840
+#: builtin/branch.c:841
msgid "delete fully merged branch"
msgstr "удалить полноÑтью Ñлитую ветку"
-#: builtin/branch.c:841
+#: builtin/branch.c:842
msgid "delete branch (even if not merged)"
msgstr "удалить ветку (даже никуда не Ñлитую)"
-#: builtin/branch.c:842
+#: builtin/branch.c:843
msgid "move/rename a branch and its reflog"
msgstr "перемеÑтить/переименовать ветки и ее журнал ÑÑылок"
-#: builtin/branch.c:843
+#: builtin/branch.c:844
msgid "move/rename a branch, even if target exists"
msgstr "перемеÑтить/переименовать ветку, даже еÑли целевое Ð¸Ð¼Ñ ÑƒÐ¶Ðµ ÑущеÑтвует"
-#: builtin/branch.c:844
+#: builtin/branch.c:845
msgid "list branch names"
msgstr "показать ÑпиÑок имен веток"
-#: builtin/branch.c:845
+#: builtin/branch.c:846
msgid "create the branch's reflog"
msgstr "Ñоздать журнал ÑÑылок ветки"
-#: builtin/branch.c:847
+#: builtin/branch.c:848
msgid "edit the description for the branch"
msgstr "изменить опиÑание ветки"
-#: builtin/branch.c:848
+#: builtin/branch.c:849
msgid "force creation, move/rename, deletion"
msgstr "принудительное Ñоздание, перемещение или удаление ветки"
-#: builtin/branch.c:851
+#: builtin/branch.c:852
msgid "print only not merged branches"
msgstr "вывод только не Ñлитых веток"
-#: builtin/branch.c:857
+#: builtin/branch.c:858
msgid "print only merged branches"
msgstr "вывод только Ñлитых веток"
-#: builtin/branch.c:861
+#: builtin/branch.c:862
msgid "list branches in columns"
msgstr "показать ÑпиÑок веток по Ñтолбцам"
-#: builtin/branch.c:874
+#: builtin/branch.c:875
msgid "Failed to resolve HEAD as a valid ref."
msgstr "Ðе удалоÑÑŒ определить HEAD как дейÑтвительную ÑÑылку."
-#: builtin/branch.c:878 builtin/clone.c:622
+#: builtin/branch.c:879 builtin/clone.c:690
msgid "HEAD not found below refs/heads!"
msgstr "HEAD не найден в refs/heads!"
-#: builtin/branch.c:900
+#: builtin/branch.c:901
msgid "--column and --verbose are incompatible"
msgstr "--column и --verbose Ð½ÐµÐ»ÑŒÐ·Ñ Ð¸Ñпользовать одновременно"
-#: builtin/branch.c:911 builtin/branch.c:950
+#: builtin/branch.c:912 builtin/branch.c:951
msgid "branch name required"
msgstr "требуетÑÑ Ð¸Ð¼Ñ Ð²ÐµÑ‚ÐºÐ¸"
-#: builtin/branch.c:926
+#: builtin/branch.c:927
msgid "Cannot give description to detached HEAD"
msgstr "ÐÐµÐ»ÑŒÐ·Ñ Ð´Ð°Ñ‚ÑŒ опиÑание отделенному HEAD"
-#: builtin/branch.c:931
+#: builtin/branch.c:932
msgid "cannot edit description of more than one branch"
msgstr "Ð½ÐµÐ»ÑŒÐ·Ñ Ð¸Ð·Ð¼ÐµÐ½Ð¸Ñ‚ÑŒ опиÑание более одной ветки за раз"
-#: builtin/branch.c:938
+#: builtin/branch.c:939
#, c-format
msgid "No commit on branch '%s' yet."
msgstr "Еще нет коммита на ветке «%s»."
-#: builtin/branch.c:941
+#: builtin/branch.c:942
#, c-format
msgid "No branch named '%s'."
msgstr "Ðет ветки Ñ Ð¸Ð¼ÐµÐ½ÐµÐ¼ «%s»."
-#: builtin/branch.c:956
+#: builtin/branch.c:957
msgid "too many branches for a rename operation"
msgstr "Ñлишком много веток Ð´Ð»Ñ Ð¾Ð¿ÐµÑ€Ð°Ñ†Ð¸Ð¸ переименованиÑ"
-#: builtin/branch.c:961
+#: builtin/branch.c:962
msgid "too many branches to set new upstream"
msgstr "Ñлишком много веток Ð´Ð»Ñ ÑƒÐºÐ°Ð·Ð°Ð½Ð¸Ñ Ð½Ð¾Ð²Ñ‹Ñ… вышеÑтоÑщих"
-#: builtin/branch.c:965
+#: builtin/branch.c:966
#, c-format
msgid ""
"could not set upstream of HEAD to %s when it does not point to any branch."
msgstr "невозможно уÑтановить вышеÑтоÑщий репозиторий Ð´Ð»Ñ HEAD на %s, когда он не указывает ни на одну ветку."
-#: builtin/branch.c:968 builtin/branch.c:990 builtin/branch.c:1011
+#: builtin/branch.c:969 builtin/branch.c:991 builtin/branch.c:1012
#, c-format
msgid "no such branch '%s'"
msgstr "нет такой ветки «%s»"
-#: builtin/branch.c:972
+#: builtin/branch.c:973
#, c-format
msgid "branch '%s' does not exist"
msgstr "ветка «%s» не ÑущеÑтвует"
-#: builtin/branch.c:984
+#: builtin/branch.c:985
msgid "too many branches to unset upstream"
msgstr "Ñлишком много веток Ð´Ð»Ñ ÑƒÐ±Ð¸Ñ€Ð°Ð½Ð¸Ñ Ð²Ñ‹ÑˆÐµÑтоÑщих"
-#: builtin/branch.c:988
+#: builtin/branch.c:989
msgid "could not unset upstream of HEAD when it does not point to any branch."
msgstr "невозможно убрать вышеÑтоÑщий репозиторий Ð´Ð»Ñ HEAD, когда он не указывает ни на одну ветку."
-#: builtin/branch.c:994
+#: builtin/branch.c:995
#, c-format
msgid "Branch '%s' has no upstream information"
msgstr "Ветка «%s» не имеет информации о вышеÑтоÑщей ветке"
-#: builtin/branch.c:1008
+#: builtin/branch.c:1009
msgid "it does not make sense to create 'HEAD' manually"
msgstr "не имеет ÑмыÑла Ñоздавать «HEAD» вручную"
-#: builtin/branch.c:1014
+#: builtin/branch.c:1015
msgid "-a and -r options to 'git branch' do not make sense with a branch name"
msgstr "параметры -a и -r Ð´Ð»Ñ Â«git branch» не имеют ÑмыÑла Ñ ÑƒÐºÐ°Ð·Ð°Ð½Ð¸ÐµÐ¼ имени ветки"
-#: builtin/branch.c:1017
+#: builtin/branch.c:1018
#, c-format
msgid ""
"The --set-upstream flag is deprecated and will be removed. Consider using "
"--track or --set-upstream-to\n"
msgstr "Флаг --set-upstream уÑтарел и будет удален в будущем. ВмеÑто него иÑпользуйте --track или --set-upstream-to\n"
-#: builtin/branch.c:1034
+#: builtin/branch.c:1035
#, c-format
msgid ""
"\n"
@@ -3164,12 +3633,12 @@ msgid ""
"\n"
msgstr "\nЕÑли вы хотите, чтобы «%s» отÑлеживала «%s», Ñделайте Ñледующее:\n\n"
-#: builtin/branch.c:1035
+#: builtin/branch.c:1036
#, c-format
msgid " git branch -d %s\n"
msgstr "git branch -d %s\n"
-#: builtin/branch.c:1036
+#: builtin/branch.c:1037
#, c-format
msgid " git branch --set-upstream-to %s\n"
msgstr " git branch --set-upstream-to %s\n"
@@ -3187,58 +3656,66 @@ msgstr "ТребуетÑÑ Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ð¸Ð¹ Ð´Ð»Ñ ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ð¿Ð°Ðº
msgid "Need a repository to unbundle."
msgstr "ТребуетÑÑ Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ð¸Ð¹ Ð´Ð»Ñ Ñ€Ð°Ñпаковки."
-#: builtin/cat-file.c:369
+#: builtin/cat-file.c:428
msgid ""
"git cat-file (-t [--allow-unknown-type]|-s [--allow-unknown-"
"type]|-e|-p|<type>|--textconv) <object>"
msgstr "git cat-file (-t [--allow-unknown-type]|-s [--allow-unknown-type]|-e|-p|<тип>|--textconv) <объект>"
-#: builtin/cat-file.c:370
+#: builtin/cat-file.c:429
msgid ""
"git cat-file (--batch | --batch-check) [--follow-symlinks] < <list-of-"
"objects>"
msgstr "git cat-file (--batch | --batch-check) [--follow-symlinks] < <ÑпиÑок-объектов>"
-#: builtin/cat-file.c:407
+#: builtin/cat-file.c:466
msgid "<type> can be one of: blob, tree, commit, tag"
msgstr "<тип> может быть одним из: blob, tree, commit, tag"
-#: builtin/cat-file.c:408
+#: builtin/cat-file.c:467
msgid "show object type"
msgstr "показать тип объекта"
-#: builtin/cat-file.c:409
+#: builtin/cat-file.c:468
msgid "show object size"
msgstr "показать размер объекта"
-#: builtin/cat-file.c:411
+#: builtin/cat-file.c:470
msgid "exit with zero when there's no error"
msgstr "выйти Ñ Ð½ÑƒÐ»ÐµÐ²Ñ‹Ð¼ кодом возврата, еÑли нет ошибки"
-#: builtin/cat-file.c:412
+#: builtin/cat-file.c:471
msgid "pretty-print object's content"
msgstr "Ñтруктурированный вывод Ñодержимого объекта"
-#: builtin/cat-file.c:414
+#: builtin/cat-file.c:473
msgid "for blob objects, run textconv on object's content"
msgstr "запуÑтить texconv на Ñодержимом двоичных объектов "
-#: builtin/cat-file.c:416
+#: builtin/cat-file.c:475
msgid "allow -s and -t to work with broken/corrupt objects"
msgstr "разрешить -s и -t работать Ñ Ð¿Ð¾Ð²Ñ€ÐµÐ¶Ð´Ñ‘Ð½Ð½Ñ‹Ð¼Ð¸ объектами"
-#: builtin/cat-file.c:418
+#: builtin/cat-file.c:476
+msgid "buffer --batch output"
+msgstr "буфферировать вывод --batch"
+
+#: builtin/cat-file.c:478
msgid "show info and content of objects fed from the standard input"
msgstr "показать информацию и Ñодержимое объектов, переданных из Ñтандартного ввода"
-#: builtin/cat-file.c:421
+#: builtin/cat-file.c:481
msgid "show info about objects fed from the standard input"
msgstr "показать информацию об объектах, переданных из Ñтандартного ввода"
-#: builtin/cat-file.c:424
+#: builtin/cat-file.c:484
msgid "follow in-tree symlinks (used with --batch or --batch-check)"
msgstr "переходить по Ñимвольным ÑÑылкам внутри дерева (иÑпользуетÑÑ Ñ Ð¾Ð¿Ñ†Ð¸Ñми --batch и --batch-check)"
+#: builtin/cat-file.c:486
+msgid "show all objects with --batch or --batch-check"
+msgstr "показать вÑе объекты Ñ Ð¾Ð¿Ñ†Ð¸Ñми --batch или --batch-check"
+
#: builtin/check-attr.c:11
msgid "git check-attr [-a | --all | <attr>...] [--] <pathname>..."
msgstr "git check-attr [-a | --all | <атрибут>…] [--] <путь>…"
@@ -3263,7 +3740,7 @@ msgstr "прочитать имена файлов из Ñтандартного
msgid "terminate input and output records by a NUL character"
msgstr "окончание ввода и вывода запиÑей по ÐУЛЕВОМУ Ñимволу"
-#: builtin/check-ignore.c:18 builtin/checkout.c:1202 builtin/gc.c:279
+#: builtin/check-ignore.c:18 builtin/checkout.c:1133 builtin/gc.c:267
msgid "suppress progress reporting"
msgstr "не выводить прогреÑÑ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ"
@@ -3360,113 +3837,113 @@ msgstr "добавить Ñпереди <Ñтроку> при Ñоздании Ñ
msgid "copy out the files from named stage"
msgstr "копировать файлы из указанного индекÑа"
-#: builtin/checkout.c:24
+#: builtin/checkout.c:25
msgid "git checkout [<options>] <branch>"
msgstr "git checkout [<опции>] <ветка>"
-#: builtin/checkout.c:25
+#: builtin/checkout.c:26
msgid "git checkout [<options>] [<branch>] -- <file>..."
msgstr "git checkout [<опции>] [<ветка>] -- <файл>…"
-#: builtin/checkout.c:134 builtin/checkout.c:167
+#: builtin/checkout.c:133 builtin/checkout.c:166
#, c-format
msgid "path '%s' does not have our version"
msgstr "путь «%s» не имеет нашей верÑии"
-#: builtin/checkout.c:136 builtin/checkout.c:169
+#: builtin/checkout.c:135 builtin/checkout.c:168
#, c-format
msgid "path '%s' does not have their version"
msgstr "путь «%s» не имеет их верÑии"
-#: builtin/checkout.c:152
+#: builtin/checkout.c:151
#, c-format
msgid "path '%s' does not have all necessary versions"
msgstr "путь «%s» не имеет вÑех необходимых верÑий"
-#: builtin/checkout.c:196
+#: builtin/checkout.c:195
#, c-format
msgid "path '%s' does not have necessary versions"
msgstr "путь «%s» не имеет необходимых верÑий"
-#: builtin/checkout.c:213
+#: builtin/checkout.c:212
#, c-format
msgid "path '%s': cannot merge"
msgstr "путь «%s»: не удалоÑÑŒ Ñлить"
-#: builtin/checkout.c:230
+#: builtin/checkout.c:229
#, c-format
msgid "Unable to add merge result for '%s'"
msgstr "Ðе удалоÑÑŒ добавить результат ÑлиÑÐ½Ð¸Ñ Â«%s»"
-#: builtin/checkout.c:251 builtin/checkout.c:254 builtin/checkout.c:257
-#: builtin/checkout.c:260
+#: builtin/checkout.c:250 builtin/checkout.c:253 builtin/checkout.c:256
+#: builtin/checkout.c:259
#, c-format
msgid "'%s' cannot be used with updating paths"
msgstr "«%s» Ð½ÐµÐ»ÑŒÐ·Ñ Ð¸Ñпользовать при обновлении путей"
-#: builtin/checkout.c:263 builtin/checkout.c:266
+#: builtin/checkout.c:262 builtin/checkout.c:265
#, c-format
msgid "'%s' cannot be used with %s"
msgstr "«%s» Ð½ÐµÐ»ÑŒÐ·Ñ Ð¸Ñпользовать одновременно Ñ %s"
-#: builtin/checkout.c:269
+#: builtin/checkout.c:268
#, c-format
msgid "Cannot update paths and switch to branch '%s' at the same time."
msgstr "ÐÐµÐ»ÑŒÐ·Ñ Ð¾Ð±Ð½Ð¾Ð²Ð»ÑÑ‚ÑŒ пути и переключатьÑÑ Ð½Ð° ветку «%s» одновременно."
-#: builtin/checkout.c:280 builtin/checkout.c:474
+#: builtin/checkout.c:279 builtin/checkout.c:473
msgid "corrupt index file"
msgstr "файл индекÑа поврежден"
-#: builtin/checkout.c:340 builtin/checkout.c:347
+#: builtin/checkout.c:339 builtin/checkout.c:346
#, c-format
msgid "path '%s' is unmerged"
msgstr "путь «%s» не Ñлит"
-#: builtin/checkout.c:496
+#: builtin/checkout.c:495
msgid "you need to resolve your current index first"
msgstr "Ñначала нужно разрешить конфликты в вашем текущем индекÑе"
-#: builtin/checkout.c:627
+#: builtin/checkout.c:622
#, c-format
-msgid "Can not do reflog for '%s'\n"
-msgstr "Ðе удалоÑÑŒ Ñоздать журнал ÑÑылок Ð´Ð»Ñ Â«%s»\n"
+msgid "Can not do reflog for '%s': %s\n"
+msgstr "Ðе удалоÑÑŒ Ñоздать журнал ÑÑылок Ð´Ð»Ñ Â«%s»': %s\n"
-#: builtin/checkout.c:663
+#: builtin/checkout.c:660
msgid "HEAD is now at"
msgstr "HEAD ÑÐµÐ¹Ñ‡Ð°Ñ Ð½Ð°"
-#: builtin/checkout.c:670
+#: builtin/checkout.c:667
#, c-format
msgid "Reset branch '%s'\n"
msgstr "Ð¡Ð±Ñ€Ð¾Ñ Ð²ÐµÑ‚ÐºÐ¸ «%s»\n"
-#: builtin/checkout.c:673
+#: builtin/checkout.c:670
#, c-format
msgid "Already on '%s'\n"
msgstr "Уже на «%s»\n"
-#: builtin/checkout.c:677
+#: builtin/checkout.c:674
#, c-format
msgid "Switched to and reset branch '%s'\n"
msgstr "Переключение и ÑÐ±Ñ€Ð¾Ñ Ð²ÐµÑ‚ÐºÐ¸ «%s»\n"
-#: builtin/checkout.c:679 builtin/checkout.c:1134
+#: builtin/checkout.c:676 builtin/checkout.c:1065
#, c-format
msgid "Switched to a new branch '%s'\n"
msgstr "Переключено на новую ветку «%s»\n"
-#: builtin/checkout.c:681
+#: builtin/checkout.c:678
#, c-format
msgid "Switched to branch '%s'\n"
msgstr "Переключено на ветку «%s»\n"
-#: builtin/checkout.c:733
+#: builtin/checkout.c:730
#, c-format
msgid " ... and %d more.\n"
msgstr " … и еще %d.\n"
-#: builtin/checkout.c:739
+#: builtin/checkout.c:736
#, c-format
msgid ""
"Warning: you are leaving %d commit behind, not connected to\n"
@@ -3483,7 +3960,7 @@ msgstr[1] "Предупреждение: вы оÑтавлÑете позади
msgstr[2] "Предупреждение: вы оÑтавлÑете позади %d коммитов не Ñоединенные ни Ñ Ð¾Ð´Ð½Ð¾Ð¹ из ваших веток:\n\n%s\n"
msgstr[3] "Предупреждение: вы оÑтавлÑете позади %d коммитов не Ñоединенные ни Ñ Ð¾Ð´Ð½Ð¾Ð¹ из ваших веток:\n\n%s\n"
-#: builtin/checkout.c:758
+#: builtin/checkout.c:755
#, c-format
msgid ""
"If you want to keep it by creating a new branch, this may be a good time\n"
@@ -3502,197 +3979,192 @@ msgstr[1] "ЕÑли вы хотите Ñохранить их Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ
msgstr[2] "ЕÑли вы хотите Ñохранить их Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ð½Ð¾Ð²Ð¾Ð¹ ветки, то ÑÐµÐ¹Ñ‡Ð°Ñ Ñамое времÑ\nÑделать Ñто Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ:\n\n git branch <имÑ-новой-ветки> %s\n\n"
msgstr[3] "ЕÑли вы хотите Ñохранить их Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ð½Ð¾Ð²Ð¾Ð¹ ветки, то ÑÐµÐ¹Ñ‡Ð°Ñ Ñамое времÑ\nÑделать Ñто Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ:\n\n git branch <имÑ-новой-ветки> %s\n\n"
-#: builtin/checkout.c:794
+#: builtin/checkout.c:791
msgid "internal error in revision walk"
msgstr "внутреннÑÑ Ð¾ÑˆÐ¸Ð±ÐºÐ° при хождении по редакциÑм"
-#: builtin/checkout.c:798
+#: builtin/checkout.c:795
msgid "Previous HEAD position was"
msgstr "ÐŸÑ€ÐµÐ´Ñ‹Ð´ÑƒÑ‰Ð°Ñ Ð¿Ð¾Ð·Ð¸Ñ†Ð¸Ñ HEAD была"
-#: builtin/checkout.c:825 builtin/checkout.c:1129
+#: builtin/checkout.c:822 builtin/checkout.c:1060
msgid "You are on a branch yet to be born"
msgstr "Ð’Ñ‹ находитеÑÑŒ на еще не Ñозданной ветке"
-#: builtin/checkout.c:931
-#, c-format
-msgid "'%s' is already checked out at '%s'"
-msgstr "«%s» уже находитÑÑ Ð½Ð° «%s»"
-
-#: builtin/checkout.c:1036
+#: builtin/checkout.c:967
#, c-format
msgid "only one reference expected, %d given."
msgstr "ожидаетÑÑ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ одна ÑÑылка, а передано %d."
-#: builtin/checkout.c:1075
+#: builtin/checkout.c:1006 builtin/worktree.c:210
#, c-format
msgid "invalid reference: %s"
msgstr "Ð½ÐµÐ¿Ñ€Ð°Ð²Ð¸Ð»ÑŒÐ½Ð°Ñ ÑÑылка: %s"
-#: builtin/checkout.c:1104
+#: builtin/checkout.c:1035
#, c-format
msgid "reference is not a tree: %s"
msgstr "в дереве нет такой ÑÑылки: %s"
-#: builtin/checkout.c:1143
+#: builtin/checkout.c:1074
msgid "paths cannot be used with switching branches"
msgstr "Ð½ÐµÐ»ÑŒÐ·Ñ Ð¸Ñпользовать пути при переключении веток"
-#: builtin/checkout.c:1146 builtin/checkout.c:1150
+#: builtin/checkout.c:1077 builtin/checkout.c:1081
#, c-format
msgid "'%s' cannot be used with switching branches"
msgstr "Ð½ÐµÐ»ÑŒÐ·Ñ Ð¸Ñпользовать «%s» при переключении веток"
-#: builtin/checkout.c:1154 builtin/checkout.c:1157 builtin/checkout.c:1162
-#: builtin/checkout.c:1165
+#: builtin/checkout.c:1085 builtin/checkout.c:1088 builtin/checkout.c:1093
+#: builtin/checkout.c:1096
#, c-format
msgid "'%s' cannot be used with '%s'"
msgstr "«%s» Ð½ÐµÐ»ÑŒÐ·Ñ Ð¸Ñпользовать одновременно Ñ Â«%s»"
-#: builtin/checkout.c:1170
+#: builtin/checkout.c:1101
#, c-format
msgid "Cannot switch branch to a non-commit '%s'"
msgstr "ÐÐµÐ»ÑŒÐ·Ñ Ð¿ÐµÑ€ÐµÐºÐ»ÑŽÑ‡Ð¸Ñ‚ÑŒ ветку на не коммит «%s»"
-#: builtin/checkout.c:1203 builtin/checkout.c:1205 builtin/clone.c:83
-#: builtin/remote.c:159 builtin/remote.c:161 builtin/worktree.c:282
-#: builtin/worktree.c:284
+#: builtin/checkout.c:1134 builtin/checkout.c:1136 builtin/clone.c:83
+#: builtin/remote.c:159 builtin/remote.c:161 builtin/worktree.c:317
+#: builtin/worktree.c:319
msgid "branch"
msgstr "ветка"
-#: builtin/checkout.c:1204
+#: builtin/checkout.c:1135
msgid "create and checkout a new branch"
msgstr "Ñоздать и перейти на новую ветку"
-#: builtin/checkout.c:1206
+#: builtin/checkout.c:1137
msgid "create/reset and checkout a branch"
msgstr "Ñоздать/ÑброÑить и перейти на новую ветку"
-#: builtin/checkout.c:1207
+#: builtin/checkout.c:1138
msgid "create reflog for new branch"
msgstr "Ñоздать журнал ÑÑылок Ð´Ð»Ñ Ð½Ð¾Ð²Ð¾Ð¹ ветки"
-#: builtin/checkout.c:1208
+#: builtin/checkout.c:1139
msgid "detach the HEAD at named commit"
msgstr "отÑоединить HEAD на указанном коммите"
-#: builtin/checkout.c:1209
+#: builtin/checkout.c:1140
msgid "set upstream info for new branch"
msgstr "уÑтановить информацию о вышеÑтоÑщей ветке Ð´Ð»Ñ Ð½Ð¾Ð²Ð¾Ð¹ ветки"
-#: builtin/checkout.c:1211
+#: builtin/checkout.c:1142
msgid "new-branch"
msgstr "новаÑ-ветка"
-#: builtin/checkout.c:1211
+#: builtin/checkout.c:1142
msgid "new unparented branch"
msgstr "Ð½Ð¾Ð²Ð°Ñ Ð²ÐµÑ‚ÐºÐ° без родителей"
-#: builtin/checkout.c:1212
+#: builtin/checkout.c:1143
msgid "checkout our version for unmerged files"
msgstr "перейти на нашу верÑию Ð´Ð»Ñ Ð½Ðµ Ñлитых файлов"
-#: builtin/checkout.c:1214
+#: builtin/checkout.c:1145
msgid "checkout their version for unmerged files"
msgstr "перейти на их верÑию Ð´Ð»Ñ Ð½Ðµ Ñлитых файлов"
-#: builtin/checkout.c:1216
+#: builtin/checkout.c:1147
msgid "force checkout (throw away local modifications)"
msgstr "принудительный переход (отбраÑывает вÑе локальные изменениÑ)"
-#: builtin/checkout.c:1217
+#: builtin/checkout.c:1148
msgid "perform a 3-way merge with the new branch"
msgstr "выполнить трехходовое ÑлиÑние Ñ Ð½Ð¾Ð²Ð¾Ð¹ веткой"
-#: builtin/checkout.c:1218 builtin/merge.c:227
+#: builtin/checkout.c:1149 builtin/merge.c:227
msgid "update ignored files (default)"
msgstr "обновить игнорируемые файлы (по умолчанию)"
-#: builtin/checkout.c:1219 builtin/log.c:1239 parse-options.h:244
+#: builtin/checkout.c:1150 builtin/log.c:1264 parse-options.h:249
msgid "style"
msgstr "Ñтиль"
-#: builtin/checkout.c:1220
+#: builtin/checkout.c:1151
msgid "conflict style (merge or diff3)"
msgstr "Ñтиль конфликтов ÑлиÑÐ½Ð¸Ñ (merge или diff3)"
-#: builtin/checkout.c:1223
+#: builtin/checkout.c:1154
msgid "do not limit pathspecs to sparse entries only"
msgstr "не ограничивать Ñпецификаторы пути только чаÑтичными запиÑÑми"
-#: builtin/checkout.c:1225
+#: builtin/checkout.c:1156
msgid "second guess 'git checkout <no-such-branch>'"
msgstr "переÑмотр «git checkout <no-such-branch>»"
-#: builtin/checkout.c:1227
+#: builtin/checkout.c:1158
msgid "do not check if another worktree is holding the given ref"
msgstr "не проверÑÑ‚ÑŒ, что другое дерево уже Ñодержит указанную ÑÑылку"
-#: builtin/checkout.c:1252
+#: builtin/checkout.c:1181
msgid "-b, -B and --orphan are mutually exclusive"
msgstr "-b, -B и --orphan Ð½ÐµÐ»ÑŒÐ·Ñ Ð¸Ñпользовать одновременно"
-#: builtin/checkout.c:1269
+#: builtin/checkout.c:1198
msgid "--track needs a branch name"
msgstr "--track требует Ð¸Ð¼Ñ Ð²ÐµÑ‚ÐºÐ¸"
-#: builtin/checkout.c:1274
+#: builtin/checkout.c:1203
msgid "Missing branch name; try -b"
msgstr "Пропущено Ð¸Ð¼Ñ Ð²ÐµÑ‚ÐºÐ¸; попробуйте -b"
-#: builtin/checkout.c:1310
+#: builtin/checkout.c:1239
msgid "invalid path specification"
msgstr "Ð½ÐµÐ¿Ñ€Ð°Ð²Ð¸Ð»ÑŒÐ½Ð°Ñ ÑÐ¿ÐµÑ†Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ñ Ð¿ÑƒÑ‚Ð¸"
-#: builtin/checkout.c:1317
+#: builtin/checkout.c:1246
#, c-format
msgid ""
"Cannot update paths and switch to branch '%s' at the same time.\n"
"Did you intend to checkout '%s' which can not be resolved as commit?"
msgstr "ÐÐµÐ»ÑŒÐ·Ñ Ð¾Ð±Ð½Ð¾Ð²Ð¸Ñ‚ÑŒ пути и одновременно переключить на ветку «%s».\nÐ’Ñ‹ хотели переключитьÑÑ Ð½Ð° «%s», что не может быть определено как коммит?"
-#: builtin/checkout.c:1322
+#: builtin/checkout.c:1251
#, c-format
msgid "git checkout: --detach does not take a path argument '%s'"
msgstr "git checkout: --detach не принимает путь «%s» как аргумент"
-#: builtin/checkout.c:1326
+#: builtin/checkout.c:1255
msgid ""
"git checkout: --ours/--theirs, --force and --merge are incompatible when\n"
"checking out of the index."
msgstr "git checkout: --ours/--theirs, --force and --merge Ð½ÐµÐ»ÑŒÐ·Ñ Ð¸Ñпользовать одновременно при применении ÑоÑтоÑÐ½Ð¸Ñ Ð¸Ð½Ð´ÐµÐºÑа."
-#: builtin/clean.c:26
+#: builtin/clean.c:25
msgid ""
"git clean [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>..."
msgstr "git clean [-d] [-f] [-i] [-n] [-q] [-e <шаблон>] [-x | -X] [--] <пути>…"
-#: builtin/clean.c:30
+#: builtin/clean.c:29
#, c-format
msgid "Removing %s\n"
msgstr "Удаление %s\n"
-#: builtin/clean.c:31
+#: builtin/clean.c:30
#, c-format
msgid "Would remove %s\n"
msgstr "Будет удалено %s\n"
-#: builtin/clean.c:32
+#: builtin/clean.c:31
#, c-format
msgid "Skipping repository %s\n"
msgstr "ПропуÑк Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ð¸Ñ %s\n"
-#: builtin/clean.c:33
+#: builtin/clean.c:32
#, c-format
msgid "Would skip repository %s\n"
msgstr "Будет пропущен репозиторий %s\n"
-#: builtin/clean.c:34
+#: builtin/clean.c:33
#, c-format
msgid "failed to remove %s"
msgstr "Ñбой ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ %s"
-#: builtin/clean.c:295
+#: builtin/clean.c:317
msgid ""
"Prompt help:\n"
"1 - select a numbered item\n"
@@ -3700,7 +4172,7 @@ msgid ""
" - (empty) select nothing"
msgstr "Справка по выделению:\n1 - выбрать указанный Ñлемент\nfoo - выбрать Ñлемент Ñ ÑƒÐºÐ°Ð·Ð°Ð½Ð½Ñ‹Ð¼ префикÑом\n - (пуÑто) не выбирать ничего"
-#: builtin/clean.c:299
+#: builtin/clean.c:321
msgid ""
"Prompt help:\n"
"1 - select a single item\n"
@@ -3712,36 +4184,36 @@ msgid ""
" - (empty) finish selecting"
msgstr "Справка по выделению:\n1 - выбрать один Ñлемент\n3-5 - выбрать диапазон Ñлементов\n2-3,6-9 - выбрать неÑколько диапазонов\nfoo - выбрать Ñлемент Ñ ÑƒÐºÐ°Ð·Ð°Ð½Ð½Ñ‹Ð¼ префикÑом\n-… - убрать выделение Ñ ÑƒÐºÐ°Ð·Ð°Ð½Ð½Ñ‹Ñ… Ñлементов\n* - выбрать вÑе Ñлементы\n - (пуÑто) завершить выделение"
-#: builtin/clean.c:515
+#: builtin/clean.c:537
#, c-format
msgid "Huh (%s)?"
msgstr "Хм (%s)?"
-#: builtin/clean.c:657
+#: builtin/clean.c:679
#, c-format
msgid "Input ignore patterns>> "
msgstr "Шаблоны Ð¸Ð³Ð½Ð¾Ñ€Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð²Ð²Ð¾Ð´Ð°>> "
-#: builtin/clean.c:694
+#: builtin/clean.c:716
#, c-format
msgid "WARNING: Cannot find items matched by: %s"
msgstr "ПРЕДУПРЕЖДЕÐИЕ: Ðе удалоÑÑŒ найти Ñлементы ÑоответÑтвующие: %s"
-#: builtin/clean.c:715
+#: builtin/clean.c:737
msgid "Select items to delete"
msgstr "Укажите Ñлементы Ð´Ð»Ñ ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ"
#. TRANSLATORS: Make sure to keep [y/N] as is
-#: builtin/clean.c:756
+#: builtin/clean.c:778
#, c-format
msgid "Remove %s [y/N]? "
msgstr "Удалить %s [y - да/N - нет]? "
-#: builtin/clean.c:781
+#: builtin/clean.c:803
msgid "Bye."
msgstr "До ÑвиданиÑ."
-#: builtin/clean.c:789
+#: builtin/clean.c:811
msgid ""
"clean - start cleaning\n"
"filter by pattern - exclude items from deletion\n"
@@ -3752,15 +4224,15 @@ msgid ""
"? - help for prompt selection"
msgstr "clean - начать очиÑтку\nfilter by pattern - иÑключить удаление Ñлементов\nselect by numbers - иÑключить удаление Ñлементов по номерам\nask each - запрашивать подтверждение на удаление каждого Ñлемента (как «rm -i»)\nquit - прекратить очиÑтку\nhelp - Ñтот Ñкран\n? - Ñправка по выделению"
-#: builtin/clean.c:816
+#: builtin/clean.c:838
msgid "*** Commands ***"
msgstr "*** Команды ***"
-#: builtin/clean.c:817
+#: builtin/clean.c:839
msgid "What now"
msgstr "Что теперь"
-#: builtin/clean.c:825
+#: builtin/clean.c:847
msgid "Would remove the following item:"
msgid_plural "Would remove the following items:"
msgstr[0] "Удалить Ñледующие Ñлементы:"
@@ -3768,54 +4240,54 @@ msgstr[1] "Удалить Ñледующие Ñлементы:"
msgstr[2] "Удалить Ñледующие Ñлементы:"
msgstr[3] "Удалить Ñледующие Ñлементы:"
-#: builtin/clean.c:842
+#: builtin/clean.c:864
msgid "No more files to clean, exiting."
msgstr "Больше нет файлов Ð´Ð»Ñ Ð¾Ñ‡Ð¸Ñтки, выходим."
-#: builtin/clean.c:873
+#: builtin/clean.c:895
msgid "do not print names of files removed"
msgstr "не выводить имена удалÑемых файлов"
-#: builtin/clean.c:875
+#: builtin/clean.c:897
msgid "force"
msgstr "принудительно"
-#: builtin/clean.c:876
+#: builtin/clean.c:898
msgid "interactive cleaning"
msgstr "Ð¸Ð½Ñ‚ÐµÑ€Ð°ÐºÑ‚Ð¸Ð²Ð½Ð°Ñ Ð¾Ñ‡Ð¸Ñтка"
-#: builtin/clean.c:878
+#: builtin/clean.c:900
msgid "remove whole directories"
msgstr "удалить каталоги полноÑтью"
-#: builtin/clean.c:879 builtin/describe.c:407 builtin/grep.c:714
+#: builtin/clean.c:901 builtin/describe.c:407 builtin/grep.c:714
#: builtin/ls-files.c:443 builtin/name-rev.c:311 builtin/show-ref.c:187
msgid "pattern"
msgstr "шаблон"
-#: builtin/clean.c:880
+#: builtin/clean.c:902
msgid "add <pattern> to ignore rules"
msgstr "добавить <шаблон> в правила игнорированиÑ"
-#: builtin/clean.c:881
+#: builtin/clean.c:903
msgid "remove ignored files, too"
msgstr "также удалить игнорируемые файлы"
-#: builtin/clean.c:883
+#: builtin/clean.c:905
msgid "remove only ignored files"
msgstr "удалить только игнорируемые файлы"
-#: builtin/clean.c:901
+#: builtin/clean.c:923
msgid "-x and -X cannot be used together"
msgstr "Ð½ÐµÐ»ÑŒÐ·Ñ Ð¸Ñпользовать одновременно -x и -X"
-#: builtin/clean.c:905
+#: builtin/clean.c:927
msgid ""
"clean.requireForce set to true and neither -i, -n, nor -f given; refusing to"
" clean"
msgstr "clean.requireForce уÑтановлен как true и ни одна из опций -i, -n или -f не указана; отказ очиÑтки"
-#: builtin/clean.c:908
+#: builtin/clean.c:930
msgid ""
"clean.requireForce defaults to true and neither -i, -n, nor -f given; "
"refusing to clean"
@@ -3825,8 +4297,8 @@ msgstr "clean.requireForce уÑтановлен по умолчанию как t
msgid "git clone [<options>] [--] <repo> [<dir>]"
msgstr "git clone [<опции>] [--] <репозиторий> [<каталог>]"
-#: builtin/clone.c:57 builtin/fetch.c:111 builtin/merge.c:224
-#: builtin/push.c:523
+#: builtin/clone.c:57 builtin/fetch.c:112 builtin/merge.c:224
+#: builtin/pull.c:109 builtin/push.c:560 builtin/send-pack.c:168
msgid "force progress reporting"
msgstr "принудительно выводить прогреÑÑ"
@@ -3834,7 +4306,7 @@ msgstr "принудительно выводить прогреÑÑ"
msgid "don't create a checkout"
msgstr "не переключать рабочую копию на HEAD"
-#: builtin/clone.c:60 builtin/clone.c:62 builtin/init-db.c:503
+#: builtin/clone.c:60 builtin/clone.c:62 builtin/init-db.c:504
msgid "create a bare repository"
msgstr "Ñоздать голый репозиторий"
@@ -3858,11 +4330,11 @@ msgstr "наÑтроить как общедоÑтупный репозиторÐ
msgid "initialize submodules in the clone"
msgstr "инициализировать подмодули в клоне"
-#: builtin/clone.c:75 builtin/init-db.c:500
+#: builtin/clone.c:75 builtin/init-db.c:501
msgid "template-directory"
msgstr "каталог-шаблонов"
-#: builtin/clone.c:76 builtin/init-db.c:501
+#: builtin/clone.c:76 builtin/init-db.c:502
msgid "directory from which templates will be used"
msgstr "каталог, шаблоны из которого будут иÑпользованы"
@@ -3890,7 +4362,8 @@ msgstr "перейти на <ветку>, вмеÑто HEAD внешнего Ñ€Ð
msgid "path to git-upload-pack on the remote"
msgstr "путь к git-upload-pack на внешнем репозитории"
-#: builtin/clone.c:87 builtin/fetch.c:112 builtin/grep.c:659
+#: builtin/clone.c:87 builtin/fetch.c:113 builtin/grep.c:659
+#: builtin/pull.c:186
msgid "depth"
msgstr "глубина"
@@ -3902,11 +4375,11 @@ msgstr "Ñделать чаÑтичный клон указанной глуби
msgid "clone only one branch, HEAD or --branch"
msgstr "клонировать только одну ветку, HEAD или --branch"
-#: builtin/clone.c:91 builtin/init-db.c:509
+#: builtin/clone.c:91 builtin/init-db.c:510
msgid "gitdir"
msgstr "каталог-git"
-#: builtin/clone.c:92 builtin/init-db.c:510
+#: builtin/clone.c:92 builtin/init-db.c:511
msgid "separate git dir from working tree"
msgstr "размеÑтить каталог git отдельно от рабочей копии"
@@ -3918,178 +4391,173 @@ msgstr "ключ=значение"
msgid "set config inside the new repository"
msgstr "уÑтановить параметры внутри нового репозиториÑ"
-#: builtin/clone.c:240
+#: builtin/clone.c:298
#, c-format
msgid "reference repository '%s' is not a local repository."
msgstr "ÑÑылаемый репозиторий «%s» не ÑвлÑетÑÑ Ð»Ð¾ÐºÐ°Ð»ÑŒÐ½Ñ‹Ð¼."
-#: builtin/clone.c:244
+#: builtin/clone.c:302
#, c-format
msgid "reference repository '%s' is shallow"
msgstr "ÑÑылаемый репозиторий «%s» ÑвлÑетÑÑ Ñ‡Ð°Ñтичным"
-#: builtin/clone.c:247
+#: builtin/clone.c:305
#, c-format
msgid "reference repository '%s' is grafted"
msgstr "ÑÑылаемый репозиторий «%s» ÑвлÑетÑÑ Ñращенным"
-#: builtin/clone.c:310
-#, c-format
-msgid "failed to create directory '%s'"
-msgstr "не удалоÑÑŒ Ñоздать каталог «%s»"
-
-#: builtin/clone.c:312 builtin/diff.c:84
+#: builtin/clone.c:370 builtin/diff.c:84
#, c-format
msgid "failed to stat '%s'"
msgstr "не удалоÑÑŒ выполнить stat «%s»"
-#: builtin/clone.c:314
+#: builtin/clone.c:372
#, c-format
msgid "%s exists and is not a directory"
msgstr "%s уже ÑущеÑтвует и не ÑвлÑетÑÑ ÐºÐ°Ñ‚Ð°Ð»Ð¾Ð³Ð¾Ð¼"
-#: builtin/clone.c:328
+#: builtin/clone.c:386
#, c-format
msgid "failed to stat %s\n"
msgstr "не удалоÑÑŒ выполнить stat %s\n"
-#: builtin/clone.c:350
+#: builtin/clone.c:408
#, c-format
msgid "failed to create link '%s'"
msgstr "не удалоÑÑŒ Ñоздать ÑÑылку «%s»"
-#: builtin/clone.c:354
+#: builtin/clone.c:412
#, c-format
msgid "failed to copy file to '%s'"
msgstr "не удалоÑÑŒ копировать файл в «%s»"
-#: builtin/clone.c:377 builtin/clone.c:551
+#: builtin/clone.c:435 builtin/clone.c:619
#, c-format
msgid "done.\n"
msgstr "готово.\n"
-#: builtin/clone.c:389
+#: builtin/clone.c:447
msgid ""
"Clone succeeded, but checkout failed.\n"
"You can inspect what was checked out with 'git status'\n"
"and retry the checkout with 'git checkout -f HEAD'\n"
msgstr "Клонирование прошло уÑпешно, но во Ð²Ñ€ÐµÐ¼Ñ Ð¿ÐµÑ€ÐµÑ…Ð¾Ð´Ð° на верÑию произошла ошибка.\nС помощь команды «git status» вы можете проÑмотреть, какие файлы были обновлены, а повторить попытку перехода на верÑию Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ «git checkout -f HEAD»\n"
-#: builtin/clone.c:466
+#: builtin/clone.c:524
#, c-format
msgid "Could not find remote branch %s to clone."
msgstr "Ðе удалоÑÑŒ найти внешнюю ветку %s Ð´Ð»Ñ ÐºÐ»Ð¾Ð½Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ."
-#: builtin/clone.c:546
+#: builtin/clone.c:614
#, c-format
msgid "Checking connectivity... "
msgstr "Проверка ÑоединениÑ… "
-#: builtin/clone.c:549
+#: builtin/clone.c:617
msgid "remote did not send all necessary objects"
msgstr "внешний репозиторий приÑлал не вÑе необходимые объекты"
-#: builtin/clone.c:613
+#: builtin/clone.c:681
msgid "remote HEAD refers to nonexistent ref, unable to checkout.\n"
msgstr "внешний HEAD ÑÑылаетÑÑ Ð½Ð° неÑущеÑтвующую ÑÑылку, Ð½ÐµÐ»ÑŒÐ·Ñ Ð¿ÐµÑ€ÐµÐ¹Ñ‚Ð¸ на такую верÑию.\n"
-#: builtin/clone.c:644
+#: builtin/clone.c:712
msgid "unable to checkout working tree"
msgstr "не удалоÑÑŒ перейти на верÑию в рабочем каталоге"
-#: builtin/clone.c:731
+#: builtin/clone.c:799
msgid "cannot repack to clean up"
msgstr "не удалоÑÑŒ выполнить перепаковку Ð´Ð»Ñ Ð¾Ñ‡Ð¸Ñтки"
-#: builtin/clone.c:733
+#: builtin/clone.c:801
msgid "cannot unlink temporary alternates file"
msgstr "не удалоÑÑŒ отÑоединить временные альтернативные файлы"
-#: builtin/clone.c:763
+#: builtin/clone.c:831
msgid "Too many arguments."
msgstr "Слишком много аргументов."
-#: builtin/clone.c:767
+#: builtin/clone.c:835
msgid "You must specify a repository to clone."
msgstr "Ð’Ñ‹ должны указать репозиторий Ð´Ð»Ñ ÐºÐ»Ð¾Ð½Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ."
-#: builtin/clone.c:778
+#: builtin/clone.c:846
#, c-format
msgid "--bare and --origin %s options are incompatible."
msgstr "--bare и --origin %s Ð½ÐµÐ»ÑŒÐ·Ñ Ð¸Ñпользовать одновременно."
-#: builtin/clone.c:781
+#: builtin/clone.c:849
msgid "--bare and --separate-git-dir are incompatible."
msgstr "--bare и --separate-git-dir Ð½ÐµÐ»ÑŒÐ·Ñ Ð¸Ñпользовать одновременно."
-#: builtin/clone.c:794
+#: builtin/clone.c:862
#, c-format
msgid "repository '%s' does not exist"
msgstr "репозиторий «%s» не ÑущеÑтвует"
-#: builtin/clone.c:800 builtin/fetch.c:1160
+#: builtin/clone.c:868 builtin/fetch.c:1168
#, c-format
msgid "depth %s is not a positive number"
msgstr "глубина %s не ÑвлÑетÑÑ Ð¿Ð¾Ð»Ð¾Ð¶Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ñ‹Ð¼ чиÑлом"
-#: builtin/clone.c:810
+#: builtin/clone.c:878
#, c-format
msgid "destination path '%s' already exists and is not an empty directory."
msgstr "целевой путь «%s» уже ÑущеÑтвует и не ÑвлÑетÑÑ Ð¿ÑƒÑтым каталогом."
-#: builtin/clone.c:820
+#: builtin/clone.c:888
#, c-format
msgid "working tree '%s' already exists."
msgstr "рабочий каталог «%s» уже ÑущеÑтвует."
-#: builtin/clone.c:835 builtin/clone.c:846 builtin/worktree.c:193
-#: builtin/worktree.c:220
+#: builtin/clone.c:903 builtin/clone.c:914 builtin/worktree.c:218
+#: builtin/worktree.c:245
#, c-format
msgid "could not create leading directories of '%s'"
msgstr "не удалоÑÑŒ Ñоздать родительÑкие каталоги Ð´Ð»Ñ Â«%s»"
-#: builtin/clone.c:838
+#: builtin/clone.c:906
#, c-format
msgid "could not create work tree dir '%s'"
msgstr "не удалоÑÑŒ Ñоздать рабочий каталог «%s»"
-#: builtin/clone.c:856
+#: builtin/clone.c:924
#, c-format
msgid "Cloning into bare repository '%s'...\n"
msgstr "Клонирование в голый репозиторий «%s»…\n"
-#: builtin/clone.c:858
+#: builtin/clone.c:926
#, c-format
msgid "Cloning into '%s'...\n"
msgstr "Клонирование в «%s»…\n"
-#: builtin/clone.c:883
+#: builtin/clone.c:951
msgid "--dissociate given, but there is no --reference"
msgstr "передана Ð¾Ð¿Ñ†Ð¸Ñ --dissociate, но не передана --reference"
-#: builtin/clone.c:900
+#: builtin/clone.c:968
msgid "--depth is ignored in local clones; use file:// instead."
msgstr "--depth игнорируетÑÑ Ð½Ð° локальных клонах; вмеÑто Ñтого иÑпользуйте file://."
-#: builtin/clone.c:903
+#: builtin/clone.c:971
msgid "source repository is shallow, ignoring --local"
msgstr "иÑходный репозиторий ÑвлÑетÑÑ Ñ‡Ð°Ñтичным, --local игнорируетÑÑ"
-#: builtin/clone.c:908
+#: builtin/clone.c:976
msgid "--local is ignored"
msgstr "--local игнорируетÑÑ"
-#: builtin/clone.c:912
+#: builtin/clone.c:980
#, c-format
msgid "Don't know how to clone %s"
msgstr "Ðе знаю как клонировать %s"
-#: builtin/clone.c:961 builtin/clone.c:969
+#: builtin/clone.c:1029 builtin/clone.c:1037
#, c-format
msgid "Remote branch %s not found in upstream %s"
msgstr "ВнешнÑÑ Ð²ÐµÑ‚ÐºÐ° %s не найдена в вышеÑтоÑщем репозитории %s"
-#: builtin/clone.c:972
+#: builtin/clone.c:1040
msgid "You appear to have cloned an empty repository."
msgstr "Похоже, что вы клонировали пуÑтой репозиторий."
@@ -4196,108 +4664,99 @@ msgstr "ЕÑли вы хотите пропуÑтит Ñтот коммит, иÑ
msgid "failed to unpack HEAD tree object"
msgstr "Ñбой раÑпаковки объекта дерева HEAD"
-#: builtin/commit.c:344
+#: builtin/commit.c:345
msgid "unable to create temporary index"
msgstr "не удалоÑÑŒ Ñоздать временный индекÑ"
-#: builtin/commit.c:350
+#: builtin/commit.c:351
msgid "interactive add failed"
msgstr "Ñбой интерактивного добавлениÑ"
-#: builtin/commit.c:361
-msgid "unable to write index file"
-msgstr "не удалоÑÑŒ запиÑать индекÑ"
-
-#: builtin/commit.c:363
+#: builtin/commit.c:364
msgid "unable to update temporary index"
msgstr "не удалоÑÑŒ обновить временный индекÑ"
-#: builtin/commit.c:365
+#: builtin/commit.c:366
msgid "Failed to update main cache tree"
msgstr "Сбой при обновлении оÑновного кÑша дерева"
-#: builtin/commit.c:389 builtin/commit.c:414 builtin/commit.c:463
+#: builtin/commit.c:390 builtin/commit.c:413 builtin/commit.c:462
msgid "unable to write new_index file"
msgstr "не удалоÑÑŒ запиÑать файл new_index"
-#: builtin/commit.c:445
+#: builtin/commit.c:444
msgid "cannot do a partial commit during a merge."
msgstr "Ð½ÐµÐ»ÑŒÐ·Ñ Ñоздать чаÑтичный коммит во Ð²Ñ€ÐµÐ¼Ñ ÑлиÑниÑ."
-#: builtin/commit.c:447
+#: builtin/commit.c:446
msgid "cannot do a partial commit during a cherry-pick."
msgstr "Ð½ÐµÐ»ÑŒÐ·Ñ Ñоздать чаÑтичный коммит во Ð²Ñ€ÐµÐ¼Ñ Ð¾Ñ‚Ð±Ð¾Ñ€Ð° лучшего коммита."
-#: builtin/commit.c:456
+#: builtin/commit.c:455
msgid "cannot read the index"
msgstr "не удалоÑÑŒ прочитать индекÑ"
-#: builtin/commit.c:475
+#: builtin/commit.c:474
msgid "unable to write temporary index file"
msgstr "не удалоÑÑŒ запиÑать временный файл индекÑа"
-#: builtin/commit.c:580
+#: builtin/commit.c:579
#, c-format
msgid "commit '%s' lacks author header"
msgstr "у коммита «%s» отÑутÑтвует автор в заголовке"
-#: builtin/commit.c:582
+#: builtin/commit.c:581
#, c-format
msgid "commit '%s' has malformed author line"
msgstr "у коммита «%s» автор в неверном формате"
-#: builtin/commit.c:601
+#: builtin/commit.c:600
msgid "malformed --author parameter"
msgstr "параметр --author в неверном формате"
-#: builtin/commit.c:609
+#: builtin/commit.c:608
#, c-format
msgid "invalid date format: %s"
msgstr "неправильный формат даты: %s"
-#: builtin/commit.c:653
+#: builtin/commit.c:652
msgid ""
"unable to select a comment character that is not used\n"
"in the current commit message"
msgstr "Ð½ÐµÐ»ÑŒÐ·Ñ Ð²Ñ‹Ð±Ñ€Ð°Ñ‚ÑŒ Ñимвол комментариÑ, который\nне иÑпользуетÑÑ Ð² текущем Ñообщении коммита"
-#: builtin/commit.c:690 builtin/commit.c:723 builtin/commit.c:1080
+#: builtin/commit.c:689 builtin/commit.c:722 builtin/commit.c:1079
#, c-format
msgid "could not lookup commit %s"
msgstr "не удалоÑÑŒ запроÑить коммит %s"
-#: builtin/commit.c:702 builtin/shortlog.c:273
+#: builtin/commit.c:701 builtin/shortlog.c:273
#, c-format
msgid "(reading log message from standard input)\n"
msgstr "(чтение файла журнала из Ñтандартного ввода)\n"
-#: builtin/commit.c:704
+#: builtin/commit.c:703
msgid "could not read log from standard input"
msgstr "не удалоÑÑŒ прочитать файл журнала из Ñтандартного ввода"
-#: builtin/commit.c:708
+#: builtin/commit.c:707
#, c-format
msgid "could not read log file '%s'"
msgstr "не удалоÑÑŒ прочитать файл журнала «%s»"
-#: builtin/commit.c:730
+#: builtin/commit.c:729
msgid "could not read MERGE_MSG"
msgstr "не удалоÑÑŒ прочитать MERGE_MSG"
-#: builtin/commit.c:734
+#: builtin/commit.c:733
msgid "could not read SQUASH_MSG"
msgstr "не удалоÑÑŒ прочитать SQUASH_MSG"
-#: builtin/commit.c:738 builtin/merge.c:1079
-#, c-format
-msgid "could not read '%s'"
-msgstr "не удалоÑÑŒ прочитать «%s»"
-
-#: builtin/commit.c:785
+#: builtin/commit.c:784
msgid "could not write commit template"
msgstr "не удалоÑÑŒ запиÑать шаблон опиÑÐ°Ð½Ð¸Ñ ÐºÐ¾Ð¼Ð¼Ð¸Ñ‚Ð°"
-#: builtin/commit.c:803
+#: builtin/commit.c:802
#, c-format
msgid ""
"\n"
@@ -4307,7 +4766,7 @@ msgid ""
"and try again.\n"
msgstr "\nПохоже, что вы пытаетеÑÑŒ закоммитить ÑлиÑние.\nЕÑли Ñто ошибка, пожалуйÑта удалите файл\n\t%s\nи попробуйте Ñнова.\n"
-#: builtin/commit.c:808
+#: builtin/commit.c:807
#, c-format
msgid ""
"\n"
@@ -4317,14 +4776,14 @@ msgid ""
"and try again.\n"
msgstr "\nПохоже, что вы пытаетеÑÑŒ закоммитить отбор лучшего.\nЕÑли Ñто ошибка, пожалуйÑта удалите файл\n\t%s\nи попробуйте Ñнова.\n"
-#: builtin/commit.c:821
+#: builtin/commit.c:820
#, c-format
msgid ""
"Please enter the commit message for your changes. Lines starting\n"
"with '%c' will be ignored, and an empty message aborts the commit.\n"
msgstr "ПожалуйÑта, введите Ñообщение коммита Ð´Ð»Ñ Ð²Ð°ÑˆÐ¸Ñ… изменений. Строки,\nначинающиеÑÑ Ñ Â«%c» будут проигнорированы, а пуÑтое Ñообщение\nотменÑет процеÑÑ ÐºÐ¾Ð¼Ð¼Ð¸Ñ‚Ð°.\n"
-#: builtin/commit.c:828
+#: builtin/commit.c:827
#, c-format
msgid ""
"Please enter the commit message for your changes. Lines starting\n"
@@ -4332,348 +4791,335 @@ msgid ""
"An empty message aborts the commit.\n"
msgstr "ПожалуйÑта, введите Ñообщение коммита Ð´Ð»Ñ Ð²Ð°ÑˆÐ¸Ñ… изменений. Строки,\nначинающиеÑÑ Ñ Â«%c» будут оÑтавлены; вы можете удалить их вручную,\nеÑли хотите. ПуÑтое Ñообщение отменÑет процеÑÑ ÐºÐ¾Ð¼Ð¼Ð¸Ñ‚Ð°.\n"
-#: builtin/commit.c:848
+#: builtin/commit.c:847
#, c-format
msgid "%sAuthor: %.*s <%.*s>"
msgstr "%sÐвтор: %.*s <%.*s>"
-#: builtin/commit.c:856
+#: builtin/commit.c:855
#, c-format
msgid "%sDate: %s"
msgstr "%sДата: %s"
-#: builtin/commit.c:863
+#: builtin/commit.c:862
#, c-format
msgid "%sCommitter: %.*s <%.*s>"
msgstr "%sКоммитер: %.*s <%.*s>"
-#: builtin/commit.c:881
+#: builtin/commit.c:880
msgid "Cannot read index"
msgstr "Ðе удалоÑÑŒ прочитать индекÑ"
-#: builtin/commit.c:938
+#: builtin/commit.c:937
msgid "Error building trees"
msgstr "Ошибка при поÑтроении деревьев"
-#: builtin/commit.c:953 builtin/tag.c:495
+#: builtin/commit.c:952 builtin/tag.c:495
#, c-format
msgid "Please supply the message using either -m or -F option.\n"
msgstr "ПожалуйÑта, укажите Ñообщение, при указании опций -m или -F.\n"
-#: builtin/commit.c:1055
+#: builtin/commit.c:1054
#, c-format
msgid "--author '%s' is not 'Name <email>' and matches no existing author"
msgstr "--author «%s» не в формате Â«Ð˜Ð¼Ñ <почта>» и не Ñовпадает Ñ ÑущеÑтвующим автором"
-#: builtin/commit.c:1070 builtin/commit.c:1310
+#: builtin/commit.c:1069 builtin/commit.c:1309
#, c-format
msgid "Invalid untracked files mode '%s'"
msgstr "Ðеправильный режим неотÑлеживаемых файлов «%s»"
-#: builtin/commit.c:1107
+#: builtin/commit.c:1106
msgid "--long and -z are incompatible"
msgstr "--long и -z Ð½ÐµÐ»ÑŒÐ·Ñ Ð¸Ñпользовать одновременно"
-#: builtin/commit.c:1137
+#: builtin/commit.c:1136
msgid "Using both --reset-author and --author does not make sense"
msgstr "Указание одновременно опций --reset-author и --author не имеет ÑмыÑла"
-#: builtin/commit.c:1146
+#: builtin/commit.c:1145
msgid "You have nothing to amend."
msgstr "Ðечего иÑправлÑÑ‚ÑŒ."
-#: builtin/commit.c:1149
+#: builtin/commit.c:1148
msgid "You are in the middle of a merge -- cannot amend."
msgstr "Ð’Ñ‹ в процеÑÑе ÑлиÑÐ½Ð¸Ñ â€” ÑÐµÐ¹Ñ‡Ð°Ñ Ð½ÐµÐ»ÑŒÐ·Ñ Ð¸ÑправлÑÑ‚ÑŒ."
-#: builtin/commit.c:1151
+#: builtin/commit.c:1150
msgid "You are in the middle of a cherry-pick -- cannot amend."
msgstr "Ð’Ñ‹ в процеÑÑе отбора лучшего — ÑÐµÐ¹Ñ‡Ð°Ñ Ð½ÐµÐ»ÑŒÐ·Ñ Ð¸ÑправлÑÑ‚ÑŒ."
-#: builtin/commit.c:1154
+#: builtin/commit.c:1153
msgid "Options --squash and --fixup cannot be used together"
msgstr "Опции --squash и --fixup не могут иÑпользоватьÑÑ Ð¾Ð´Ð½Ð¾Ð²Ñ€ÐµÐ¼ÐµÐ½Ð½Ð¾"
-#: builtin/commit.c:1164
+#: builtin/commit.c:1163
msgid "Only one of -c/-C/-F/--fixup can be used."
msgstr "Может иÑпользоватьÑÑ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ одна из опций -c/-C/-F/--fixup."
-#: builtin/commit.c:1166
+#: builtin/commit.c:1165
msgid "Option -m cannot be combined with -c/-C/-F/--fixup."
msgstr "ÐžÐ¿Ñ†Ð¸Ñ -m не может иÑпользоватьÑÑ Ñ -c/-C/-F/--fixup."
-#: builtin/commit.c:1174
+#: builtin/commit.c:1173
msgid "--reset-author can be used only with -C, -c or --amend."
msgstr "--reset-author может иÑпользоватьÑÑ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ одновременно Ñ Ð¾Ð¿Ñ†Ð¸Ñми -C, -c или --amend."
-#: builtin/commit.c:1191
+#: builtin/commit.c:1190
msgid "Only one of --include/--only/--all/--interactive/--patch can be used."
msgstr "Может иÑпользоватьÑÑ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ одна из опций --include/--only/--all/--interactive/--patch."
-#: builtin/commit.c:1193
+#: builtin/commit.c:1192
msgid "No paths with --include/--only does not make sense."
msgstr "Указание путей каталогов Ñ Ð¾Ð¿Ñ†Ð¸Ñми --include/--only не имеет ÑмыÑла."
-#: builtin/commit.c:1195
+#: builtin/commit.c:1194
msgid "Clever... amending the last one with dirty index."
msgstr "Умно… отмена поÑледнего Ñ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð½Ñ‹Ð¼ индекÑом."
-#: builtin/commit.c:1197
+#: builtin/commit.c:1196
msgid "Explicit paths specified without -i or -o; assuming --only paths..."
msgstr "Пути Ñвно указаны пути без опций -i или -o; предполагаю опцию --only…"
-#: builtin/commit.c:1209 builtin/tag.c:728
+#: builtin/commit.c:1208 builtin/tag.c:730
#, c-format
msgid "Invalid cleanup mode %s"
msgstr "Ðеправильное значение режима очиÑтки %s"
-#: builtin/commit.c:1214
+#: builtin/commit.c:1213
msgid "Paths with -a does not make sense."
msgstr "С опцией -a указание пути не имеет ÑмыÑла."
-#: builtin/commit.c:1324 builtin/commit.c:1603
+#: builtin/commit.c:1323 builtin/commit.c:1602
msgid "show status concisely"
msgstr "кратко показать ÑтатуÑ"
-#: builtin/commit.c:1326 builtin/commit.c:1605
+#: builtin/commit.c:1325 builtin/commit.c:1604
msgid "show branch information"
msgstr "показать информацию о верÑии"
-#: builtin/commit.c:1328 builtin/commit.c:1607 builtin/push.c:509
+#: builtin/commit.c:1327 builtin/commit.c:1606 builtin/push.c:546
msgid "machine-readable output"
msgstr "машиночитаемый вывод"
-#: builtin/commit.c:1331 builtin/commit.c:1609
+#: builtin/commit.c:1330 builtin/commit.c:1608
msgid "show status in long format (default)"
msgstr "показать ÑÑ‚Ð°Ñ‚ÑƒÑ Ð² длинном формате (по умолчанию)"
-#: builtin/commit.c:1334 builtin/commit.c:1612
+#: builtin/commit.c:1333 builtin/commit.c:1611
msgid "terminate entries with NUL"
msgstr "завершать запиÑи ÐУЛЕВЫМ байтом"
-#: builtin/commit.c:1336 builtin/commit.c:1615 builtin/fast-export.c:980
-#: builtin/fast-export.c:983 builtin/tag.c:603
+#: builtin/commit.c:1335 builtin/commit.c:1614 builtin/fast-export.c:981
+#: builtin/fast-export.c:984 builtin/tag.c:604
msgid "mode"
msgstr "режим"
-#: builtin/commit.c:1337 builtin/commit.c:1615
+#: builtin/commit.c:1336 builtin/commit.c:1614
msgid "show untracked files, optional modes: all, normal, no. (Default: all)"
msgstr "показать неотÑлеживаемые файлы, опциональные режимы: all (вÑе), normal (как обычно), no (нет). (По умолчанию: all)"
-#: builtin/commit.c:1340
+#: builtin/commit.c:1339
msgid "show ignored files"
msgstr "показать игнорируемые файлы"
-#: builtin/commit.c:1341 parse-options.h:152
+#: builtin/commit.c:1340 parse-options.h:155
msgid "when"
msgstr "когда"
-#: builtin/commit.c:1342
+#: builtin/commit.c:1341
msgid ""
"ignore changes to submodules, optional when: all, dirty, untracked. "
"(Default: all)"
msgstr "игнорировать Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð² подмодулÑÑ…, опционально когда: all (вÑегда), dirty (измененные), untracked (неотÑлеживаемые). (По умолчанию: all)"
-#: builtin/commit.c:1344
+#: builtin/commit.c:1343
msgid "list untracked files in columns"
msgstr "показать неотÑлеживаемые файлы по Ñтолбцам"
-#: builtin/commit.c:1430
+#: builtin/commit.c:1429
msgid "couldn't look up newly created commit"
msgstr "Ð½ÐµÐ»ÑŒÐ·Ñ Ð·Ð°Ð¿Ñ€Ð¾Ñить новоÑозданный коммит"
-#: builtin/commit.c:1432
+#: builtin/commit.c:1431
msgid "could not parse newly created commit"
msgstr "Ð½ÐµÐ»ÑŒÐ·Ñ Ñ€Ð°Ð·Ð¾Ð±Ñ€Ð°Ñ‚ÑŒ новоÑозданный коммит"
-#: builtin/commit.c:1477
+#: builtin/commit.c:1476
msgid "detached HEAD"
msgstr "отделенный HEAD"
-#: builtin/commit.c:1480
+#: builtin/commit.c:1479
msgid " (root-commit)"
msgstr " (корневой коммит)"
-#: builtin/commit.c:1573
+#: builtin/commit.c:1572
msgid "suppress summary after successful commit"
msgstr "не выводить Ñводку поÑле уÑпешного коммита"
-#: builtin/commit.c:1574
+#: builtin/commit.c:1573
msgid "show diff in commit message template"
msgstr "добавить ÑпиÑок изменений в шаблон ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ ÐºÐ¾Ð¼Ð¼Ð¸Ñ‚Ð°"
-#: builtin/commit.c:1576
+#: builtin/commit.c:1575
msgid "Commit message options"
msgstr "Опции ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ ÐºÐ¾Ð¼Ð¼Ð¸Ñ‚Ð°"
-#: builtin/commit.c:1577 builtin/tag.c:601
+#: builtin/commit.c:1576 builtin/tag.c:602
msgid "read message from file"
msgstr "прочитать Ñообщение из файла"
-#: builtin/commit.c:1578
+#: builtin/commit.c:1577
msgid "author"
msgstr "автор"
-#: builtin/commit.c:1578
+#: builtin/commit.c:1577
msgid "override author for commit"
msgstr "подменить автора коммита"
-#: builtin/commit.c:1579 builtin/gc.c:280
+#: builtin/commit.c:1578 builtin/gc.c:268
msgid "date"
msgstr "дата"
-#: builtin/commit.c:1579
+#: builtin/commit.c:1578
msgid "override date for commit"
msgstr "подменить дату коммита"
-#: builtin/commit.c:1580 builtin/merge.c:218 builtin/notes.c:391
-#: builtin/notes.c:554 builtin/tag.c:599
+#: builtin/commit.c:1579 builtin/merge.c:218 builtin/notes.c:392
+#: builtin/notes.c:555 builtin/tag.c:600
msgid "message"
msgstr "Ñообщение"
-#: builtin/commit.c:1580
+#: builtin/commit.c:1579
msgid "commit message"
msgstr "Ñообщение коммита"
-#: builtin/commit.c:1581
+#: builtin/commit.c:1580
msgid "reuse and edit message from specified commit"
msgstr "иÑпользовать и отредактировать Ñообщение от указанного коммита"
-#: builtin/commit.c:1582
+#: builtin/commit.c:1581
msgid "reuse message from specified commit"
msgstr "иÑпользовать Ñообщение указанного коммита"
-#: builtin/commit.c:1583
+#: builtin/commit.c:1582
msgid "use autosquash formatted message to fixup specified commit"
msgstr "иÑпользовать форматированное Ñообщение Ð°Ð²Ñ‚Ð¾ÑƒÐ¿Ð»Ð¾Ñ‚Ð½ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ð¸ÑÐ¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð¸Ñ ÑƒÐºÐ°Ð·Ð°Ð½Ð½Ð¾Ð³Ð¾ коммита"
-#: builtin/commit.c:1584
+#: builtin/commit.c:1583
msgid "use autosquash formatted message to squash specified commit"
msgstr "иÑпользовать форматированное Ñообщение Ð°Ð²Ñ‚Ð¾ÑƒÐ¿Ð»Ð¾Ñ‚Ð½ÐµÐ½Ð¸Ñ Ð´Ð»Ñ ÑƒÐ¿Ð»Ð¾Ñ‚Ð½ÐµÐ½Ð¸Ñ ÑƒÐºÐ°Ð·Ð°Ð½Ð½Ð¾Ð³Ð¾ коммита"
-#: builtin/commit.c:1585
+#: builtin/commit.c:1584
msgid "the commit is authored by me now (used with -C/-c/--amend)"
msgstr "коммит теперь за моим авторÑтвом (Ñ Ð¸Ñпользованием -C/-c/--amend)"
-#: builtin/commit.c:1586 builtin/log.c:1191 builtin/revert.c:86
+#: builtin/commit.c:1585 builtin/log.c:1216 builtin/revert.c:86
msgid "add Signed-off-by:"
msgstr "добавить Signed-off-by:"
-#: builtin/commit.c:1587
+#: builtin/commit.c:1586
msgid "use specified template file"
msgstr "иÑпользовать указанный файл шаблона"
-#: builtin/commit.c:1588
+#: builtin/commit.c:1587
msgid "force edit of commit"
msgstr "принудительно редактировать коммит"
-#: builtin/commit.c:1589
+#: builtin/commit.c:1588
msgid "default"
msgstr "по-умолчанию"
-#: builtin/commit.c:1589 builtin/tag.c:604
+#: builtin/commit.c:1588 builtin/tag.c:605
msgid "how to strip spaces and #comments from message"
msgstr "как удалÑÑ‚ÑŒ пробелы и #комментарии из ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ ÐºÐ¾Ð¼Ð¼Ð¸Ñ‚Ð°"
-#: builtin/commit.c:1590
+#: builtin/commit.c:1589
msgid "include status in commit message template"
msgstr "включить ÑÑ‚Ð°Ñ‚ÑƒÑ Ñ„Ð°Ð¹Ð»Ð¾Ð² в шаблон ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ ÐºÐ¾Ð¼Ð¼Ð¸Ñ‚Ð°"
-#: builtin/commit.c:1591 builtin/merge.c:225 builtin/revert.c:92
-#: builtin/tag.c:605
-msgid "key-id"
-msgstr "key-id"
-
-#: builtin/commit.c:1592 builtin/merge.c:226 builtin/revert.c:93
+#: builtin/commit.c:1591 builtin/merge.c:226 builtin/pull.c:156
+#: builtin/revert.c:93
msgid "GPG sign commit"
msgstr "подпиÑать коммит Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ GPG"
-#: builtin/commit.c:1595
+#: builtin/commit.c:1594
msgid "Commit contents options"
msgstr "Опции Ñодержимого коммита"
-#: builtin/commit.c:1596
+#: builtin/commit.c:1595
msgid "commit all changed files"
msgstr "закоммитить вÑе измененные файлы"
-#: builtin/commit.c:1597
+#: builtin/commit.c:1596
msgid "add specified files to index for commit"
msgstr "добавить указанные файлы в Ð¸Ð½Ð´ÐµÐºÑ Ð´Ð»Ñ ÐºÐ¾Ð¼Ð¼Ð¸Ñ‚Ð°"
-#: builtin/commit.c:1598
+#: builtin/commit.c:1597
msgid "interactively add files"
msgstr "интерактивное добавление файлов"
-#: builtin/commit.c:1599
+#: builtin/commit.c:1598
msgid "interactively add changes"
msgstr "интерактивное добавление изменений"
-#: builtin/commit.c:1600
+#: builtin/commit.c:1599
msgid "commit only specified files"
msgstr "закоммитить только указанные файлы"
-#: builtin/commit.c:1601
+#: builtin/commit.c:1600
msgid "bypass pre-commit hook"
msgstr "пропуÑтить перехватчик перед-коммитом"
-#: builtin/commit.c:1602
+#: builtin/commit.c:1601
msgid "show what would be committed"
msgstr "показать, что будет закоммичено"
-#: builtin/commit.c:1613
+#: builtin/commit.c:1612
msgid "amend previous commit"
msgstr "иÑправить предыдущий коммит"
-#: builtin/commit.c:1614
+#: builtin/commit.c:1613
msgid "bypass post-rewrite hook"
msgstr "пропуÑтить перехватчик поÑле-перезапиÑи"
-#: builtin/commit.c:1619
+#: builtin/commit.c:1618
msgid "ok to record an empty change"
msgstr "разрешить запиÑÑŒ пуÑтого коммита"
-#: builtin/commit.c:1621
+#: builtin/commit.c:1620
msgid "ok to record a change with an empty message"
msgstr "разрешить запиÑÑŒ изменений Ñ Ð¿ÑƒÑтым Ñообщением"
-#: builtin/commit.c:1650
+#: builtin/commit.c:1649
msgid "could not parse HEAD commit"
msgstr "не удалоÑÑŒ разобрать HEAD коммит"
-#: builtin/commit.c:1689 builtin/merge.c:1076
-#, c-format
-msgid "could not open '%s' for reading"
-msgstr "не удалоÑÑŒ открыть «%s» Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ"
-
-#: builtin/commit.c:1696
+#: builtin/commit.c:1695
#, c-format
msgid "Corrupt MERGE_HEAD file (%s)"
msgstr "Файл MERGE_HEAD поврежден (%s)"
-#: builtin/commit.c:1703
+#: builtin/commit.c:1702
msgid "could not read MERGE_MODE"
msgstr "не удалоÑÑŒ прочитать MERGE_MODE"
-#: builtin/commit.c:1722
+#: builtin/commit.c:1721
#, c-format
msgid "could not read commit message: %s"
msgstr "не удалоÑÑŒ открыть Ñообщение коммита: %s"
-#: builtin/commit.c:1733
+#: builtin/commit.c:1732
#, c-format
msgid "Aborting commit; you did not edit the message.\n"
msgstr "Отмена коммита; вы не изменили Ñообщение.\n"
-#: builtin/commit.c:1738
+#: builtin/commit.c:1737
#, c-format
msgid "Aborting commit due to empty commit message.\n"
msgstr "Отмена коммита из-за пуÑтого ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ ÐºÐ¾Ð¼Ð¼Ð¸Ñ‚Ð°.\n"
-#: builtin/commit.c:1753 builtin/merge.c:829 builtin/merge.c:854
-msgid "failed to write commit object"
-msgstr "Ñбой запиÑи объекта коммита"
-
-#: builtin/commit.c:1786
+#: builtin/commit.c:1785
msgid ""
"Repository has been updated, but unable to write\n"
"new_index file. Check that disk is not full and quota is\n"
@@ -4684,131 +5130,135 @@ msgstr "Репозиторий был обновлен, но не удалоÑÑŒ
msgid "git config [<options>]"
msgstr "git config [<опции>]"
-#: builtin/config.c:53
+#: builtin/config.c:54
msgid "Config file location"
msgstr "Размещение файла конфигурации"
-#: builtin/config.c:54
+#: builtin/config.c:55
msgid "use global config file"
msgstr "иÑпользовать глобальный файл конфигурации"
-#: builtin/config.c:55
+#: builtin/config.c:56
msgid "use system config file"
msgstr "иÑпользовать ÑиÑтемный файл конфигурации"
-#: builtin/config.c:56
+#: builtin/config.c:57
msgid "use repository config file"
msgstr "иÑпользовать файл конфигурации репозиториÑ"
-#: builtin/config.c:57
+#: builtin/config.c:58
msgid "use given config file"
msgstr "иÑпользовать указанный файл конфигурации"
-#: builtin/config.c:58
+#: builtin/config.c:59
msgid "blob-id"
msgstr "идент-двоичн-объекта"
-#: builtin/config.c:58
+#: builtin/config.c:59
msgid "read config from given blob object"
msgstr "прочитать наÑтройки из указанного двоичного объекта"
-#: builtin/config.c:59
+#: builtin/config.c:60
msgid "Action"
msgstr "ДейÑтвие"
-#: builtin/config.c:60
+#: builtin/config.c:61
msgid "get value: name [value-regex]"
msgstr "получить значение: Ð¸Ð¼Ñ [шаблон-значений]"
-#: builtin/config.c:61
+#: builtin/config.c:62
msgid "get all values: key [value-regex]"
msgstr "получить вÑе значениÑ: ключ [шаблон-значений]"
-#: builtin/config.c:62
+#: builtin/config.c:63
msgid "get values for regexp: name-regex [value-regex]"
msgstr "получить Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð¿Ð¾ шаблону: шаблон-имен [шаблон-значений]"
-#: builtin/config.c:63
+#: builtin/config.c:64
msgid "get value specific for the URL: section[.var] URL"
msgstr "получить значение, Ñпецифичное Ð´Ð»Ñ URL: раздел[.переменнаÑ] URL"
-#: builtin/config.c:64
+#: builtin/config.c:65
msgid "replace all matching variables: name value [value_regex]"
msgstr "заменить вÑе ÑоответÑтвующие переменные: Ð¸Ð¼Ñ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ðµ [шаблон-значений]"
-#: builtin/config.c:65
+#: builtin/config.c:66
msgid "add a new variable: name value"
msgstr "добавить новую переменную: Ð¸Ð¼Ñ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ðµ"
-#: builtin/config.c:66
+#: builtin/config.c:67
msgid "remove a variable: name [value-regex]"
msgstr "удалить переменную: Ð¸Ð¼Ñ [шаблон-значений]"
-#: builtin/config.c:67
+#: builtin/config.c:68
msgid "remove all matches: name [value-regex]"
msgstr "удалить вÑе Ñовпадающие: Ð¸Ð¼Ñ [шаблон-значений]"
-#: builtin/config.c:68
+#: builtin/config.c:69
msgid "rename section: old-name new-name"
msgstr "переименовать раздел: Ñтарое-Ð¸Ð¼Ñ Ð½Ð¾Ð²Ð¾Ðµ-имÑ"
-#: builtin/config.c:69
+#: builtin/config.c:70
msgid "remove a section: name"
msgstr "удалить раздел: имÑ"
-#: builtin/config.c:70
+#: builtin/config.c:71
msgid "list all"
msgstr "показать веÑÑŒ ÑпиÑок"
-#: builtin/config.c:71
+#: builtin/config.c:72
msgid "open an editor"
msgstr "открыть в редакторе"
-#: builtin/config.c:72
+#: builtin/config.c:73
msgid "find the color configured: slot [default]"
msgstr "найти наÑтроенный цвет: раздел [по-умолчанию]"
-#: builtin/config.c:73
+#: builtin/config.c:74
msgid "find the color setting: slot [stdout-is-tty]"
msgstr "проверить, ÑущеÑтвует ли наÑтроенный цвет: раздел [stdout-еÑÑ‚ÑŒ-tty]"
-#: builtin/config.c:74
+#: builtin/config.c:75
msgid "Type"
msgstr "Тип"
-#: builtin/config.c:75
+#: builtin/config.c:76
msgid "value is \"true\" or \"false\""
msgstr "значение — Ñто «true» (правда) или «false» (ложь)"
-#: builtin/config.c:76
+#: builtin/config.c:77
msgid "value is decimal number"
msgstr "значение — Ñто деÑÑтичное чиÑло"
-#: builtin/config.c:77
+#: builtin/config.c:78
msgid "value is --bool or --int"
msgstr "значение — Ñто --bool или --int"
-#: builtin/config.c:78
+#: builtin/config.c:79
msgid "value is a path (file or directory name)"
msgstr "значение — Ñто путь (к файлу или каталогу)"
-#: builtin/config.c:79
+#: builtin/config.c:80
msgid "Other"
msgstr "Другое"
-#: builtin/config.c:80
+#: builtin/config.c:81
msgid "terminate values with NUL byte"
msgstr "завершать Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ ÐУЛЕВЫМ байтом"
-#: builtin/config.c:81
+#: builtin/config.c:82
+msgid "show variable names only"
+msgstr "показывать только имена переменных"
+
+#: builtin/config.c:83
msgid "respect include directives on lookup"
msgstr "учитывать директивы include (Ð²ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ñ„Ð°Ð¹Ð»Ð¾Ð²) при запроÑе"
-#: builtin/config.c:316
+#: builtin/config.c:311
msgid "unable to parse default color value"
msgstr "не удалоÑÑŒ разобрать значение цвета по умолчанию"
-#: builtin/config.c:457
+#: builtin/config.c:449
#, c-format
msgid ""
"# This is Git's per-user configuration file.\n"
@@ -4818,7 +5268,7 @@ msgid ""
"#\temail = %s\n"
msgstr "# Это файл конфигурации Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Git.\n[user]\n# ПожалуйÑта, адаптируйте и раÑкомментируйте Ñледующие Ñтроки:\n#\tuser = %s\n#\temail = %s\n"
-#: builtin/config.c:587
+#: builtin/config.c:583
#, c-format
msgid "cannot create configuration file %s"
msgstr "не удалоÑÑŒ Ñоздать файл конфигурации %s"
@@ -4854,7 +5304,7 @@ msgstr "Ð°Ð½Ð½Ð¾Ñ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð½Ð°Ñ Ð¼ÐµÑ‚ÐºÐ° %s не Ñодержит вÑÑ‚Ñ
msgid "tag '%s' is really '%s' here"
msgstr "метка «%s» уже здеÑÑŒ «%s»"
-#: builtin/describe.c:250 builtin/log.c:452
+#: builtin/describe.c:250 builtin/log.c:459
#, c-format
msgid "Not a valid object name %s"
msgstr "ÐедейÑтвительное Ð¸Ð¼Ñ Ð¾Ð±ÑŠÐµÐºÑ‚Ð° %s"
@@ -4994,502 +5444,491 @@ msgstr "передано больше двух двоичных объектов
msgid "unhandled object '%s' given."
msgstr "передан необработанный объект «%s»."
-#: builtin/fast-export.c:24
+#: builtin/fast-export.c:25
msgid "git fast-export [rev-list-opts]"
msgstr "git fast-export [опции-rev-list]"
-#: builtin/fast-export.c:979
+#: builtin/fast-export.c:980
msgid "show progress after <n> objects"
msgstr "показать прогреÑÑ Ð¿Ð¾Ñле <n> объектов"
-#: builtin/fast-export.c:981
+#: builtin/fast-export.c:982
msgid "select handling of signed tags"
msgstr "выбор обработки подпиÑанных меток"
-#: builtin/fast-export.c:984
+#: builtin/fast-export.c:985
msgid "select handling of tags that tag filtered objects"
msgstr "выбор обработки меток, которыми помечены отфильтрованные объекты"
-#: builtin/fast-export.c:987
+#: builtin/fast-export.c:988
msgid "Dump marks to this file"
msgstr "ЗапиÑать пометки в Ñтот файл"
-#: builtin/fast-export.c:989
+#: builtin/fast-export.c:990
msgid "Import marks from this file"
msgstr "Импортировать пометки из Ñтого файла"
-#: builtin/fast-export.c:991
+#: builtin/fast-export.c:992
msgid "Fake a tagger when tags lack one"
msgstr "Подделать автора метки, еÑли у метки он отÑутÑтвует"
-#: builtin/fast-export.c:993
+#: builtin/fast-export.c:994
msgid "Output full tree for each commit"
msgstr "ВывеÑти полное дерево Ð´Ð»Ñ ÐºÐ°Ð¶Ð´Ð¾Ð³Ð¾ коммита"
-#: builtin/fast-export.c:995
+#: builtin/fast-export.c:996
msgid "Use the done feature to terminate the stream"
msgstr "ИÑпользовать пометку Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð¸Ñ Ð² конце потока"
-#: builtin/fast-export.c:996
+#: builtin/fast-export.c:997
msgid "Skip output of blob data"
msgstr "ПропуÑтить вывод данных двоичных объектов"
-#: builtin/fast-export.c:997
+#: builtin/fast-export.c:998
msgid "refspec"
msgstr "ÑÐ¿ÐµÑ†Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ñ ÑÑылки"
-#: builtin/fast-export.c:998
+#: builtin/fast-export.c:999
msgid "Apply refspec to exported refs"
msgstr "Применить Ñпецификацию ÑÑылки к ÑкÑпортируемым ÑÑылкам"
-#: builtin/fast-export.c:999
+#: builtin/fast-export.c:1000
msgid "anonymize output"
msgstr "Ñделать вывод анонимным"
-#: builtin/fetch.c:19
+#: builtin/fetch.c:20
msgid "git fetch [<options>] [<repository> [<refspec>...]]"
msgstr "git fetch [<опции>] [<репозиторий> [<ÑпецификациÑ-ÑÑылки>…]]"
-#: builtin/fetch.c:20
+#: builtin/fetch.c:21
msgid "git fetch [<options>] <group>"
msgstr "git fetch [<опции>] <группа>"
-#: builtin/fetch.c:21
+#: builtin/fetch.c:22
msgid "git fetch --multiple [<options>] [(<repository> | <group>)...]"
msgstr "git fetch --multiple [<опции>] [(<репозиторий> | <группа>)…]"
-#: builtin/fetch.c:22
+#: builtin/fetch.c:23
msgid "git fetch --all [<options>]"
msgstr "git fetch --all [<опции>]"
-#: builtin/fetch.c:89
+#: builtin/fetch.c:90 builtin/pull.c:162
msgid "fetch from all remotes"
msgstr "извлечь Ñо вÑех внешних репозиториев"
-#: builtin/fetch.c:91
+#: builtin/fetch.c:92 builtin/pull.c:165
msgid "append to .git/FETCH_HEAD instead of overwriting"
msgstr "допиÑать к .git/FETCH_HEAD вмеÑто перезапиÑи"
-#: builtin/fetch.c:93
+#: builtin/fetch.c:94 builtin/pull.c:168
msgid "path to upload pack on remote end"
msgstr "путь к программе упаковки пакета на машине Ñ Ð²Ð½ÐµÑˆÐ½Ð¸Ð¼ репозиторием"
-#: builtin/fetch.c:94
+#: builtin/fetch.c:95 builtin/pull.c:170
msgid "force overwrite of local branch"
msgstr "Ð¿Ñ€Ð¸Ð½ÑƒÐ´Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð°Ñ Ð¿ÐµÑ€ÐµÐ·Ð°Ð¿Ð¸ÑÑŒ локальной ветки"
-#: builtin/fetch.c:96
+#: builtin/fetch.c:97
msgid "fetch from multiple remotes"
msgstr "извлечь Ñ Ð½ÐµÑкольких внешних репозиториев"
-#: builtin/fetch.c:98
+#: builtin/fetch.c:99 builtin/pull.c:172
msgid "fetch all tags and associated objects"
msgstr "извлечь вÑе метки и ÑвÑзанные объекты"
-#: builtin/fetch.c:100
+#: builtin/fetch.c:101
msgid "do not fetch all tags (--no-tags)"
msgstr "не извлекать вÑе метки (--no-tags)"
-#: builtin/fetch.c:102
+#: builtin/fetch.c:103 builtin/pull.c:175
msgid "prune remote-tracking branches no longer on remote"
msgstr "почиÑтить отÑлеживаемые внешние ветки, которых уже нет на внешнем репозитории"
-#: builtin/fetch.c:103
+#: builtin/fetch.c:104 builtin/pull.c:178
msgid "on-demand"
msgstr "по требованию"
-#: builtin/fetch.c:104
+#: builtin/fetch.c:105 builtin/pull.c:179
msgid "control recursive fetching of submodules"
msgstr "управление рекурÑивным извлечением подмодулей"
-#: builtin/fetch.c:108
+#: builtin/fetch.c:109 builtin/pull.c:184
msgid "keep downloaded pack"
msgstr "оÑтавить загруженный пакет данных"
-#: builtin/fetch.c:110
+#: builtin/fetch.c:111
msgid "allow updating of HEAD ref"
msgstr "разрешить обновление ÑÑылки HEAD"
-#: builtin/fetch.c:113
+#: builtin/fetch.c:114 builtin/pull.c:187
msgid "deepen history of shallow clone"
msgstr "Ð³Ð»ÑƒÐ±Ð¾ÐºÐ°Ñ Ð¸ÑÑ‚Ð¾Ñ€Ð¸Ñ Ñ‡Ð°Ñтичного клона"
-#: builtin/fetch.c:115
+#: builtin/fetch.c:116 builtin/pull.c:190
msgid "convert to a complete repository"
msgstr "преобразовать в полный репозиторий"
-#: builtin/fetch.c:117 builtin/log.c:1208
+#: builtin/fetch.c:118 builtin/log.c:1233
msgid "dir"
msgstr "каталог"
-#: builtin/fetch.c:118
+#: builtin/fetch.c:119
msgid "prepend this to submodule path output"
msgstr "приÑоединÑÑ‚ÑŒ Ñто Ñпереди к выводу путей подмодулÑ"
-#: builtin/fetch.c:121
+#: builtin/fetch.c:122
msgid "default mode for recursion"
msgstr "режим по умолчанию Ð´Ð»Ñ Ñ€ÐµÐºÑƒÑ€Ñии"
-#: builtin/fetch.c:123
+#: builtin/fetch.c:124 builtin/pull.c:193
msgid "accept refs that update .git/shallow"
msgstr "принимать ÑÑылки, которые обновлÑÑŽÑ‚ .git/shallow"
-#: builtin/fetch.c:124
+#: builtin/fetch.c:125 builtin/pull.c:195
msgid "refmap"
msgstr "ÑоответÑтвие-ÑÑылок"
-#: builtin/fetch.c:125
+#: builtin/fetch.c:126 builtin/pull.c:196
msgid "specify fetch refmap"
msgstr "указать ÑоответÑтвие ÑÑылок при извлечении"
-#: builtin/fetch.c:377
+#: builtin/fetch.c:378
msgid "Couldn't find remote ref HEAD"
msgstr "Ðе удалоÑÑŒ найти ÑÑылку HEAD на внешнем репозитории"
-#: builtin/fetch.c:457
+#: builtin/fetch.c:458
#, c-format
msgid "object %s not found"
msgstr "объект %s не найден"
-#: builtin/fetch.c:462
+#: builtin/fetch.c:463
msgid "[up to date]"
msgstr "[актуально]"
-#: builtin/fetch.c:476
+#: builtin/fetch.c:477
#, c-format
msgid "! %-*s %-*s -> %s (can't fetch in current branch)"
msgstr "! %-*s %-*s → %s (не удалоÑÑŒ извлечь в текущую ветку)"
-#: builtin/fetch.c:477 builtin/fetch.c:563
+#: builtin/fetch.c:478 builtin/fetch.c:564
msgid "[rejected]"
msgstr "[отклонено]"
-#: builtin/fetch.c:488
+#: builtin/fetch.c:489
msgid "[tag update]"
msgstr "[обновление метки]"
-#: builtin/fetch.c:490 builtin/fetch.c:525 builtin/fetch.c:543
+#: builtin/fetch.c:491 builtin/fetch.c:526 builtin/fetch.c:544
msgid " (unable to update local ref)"
msgstr " (не удалоÑÑŒ обновить локальную ÑÑылку)"
-#: builtin/fetch.c:508
+#: builtin/fetch.c:509
msgid "[new tag]"
msgstr "[Ð½Ð¾Ð²Ð°Ñ Ð¼ÐµÑ‚ÐºÐ°]"
-#: builtin/fetch.c:511
+#: builtin/fetch.c:512
msgid "[new branch]"
msgstr "[Ð½Ð¾Ð²Ð°Ñ Ð²ÐµÑ‚ÐºÐ°]"
-#: builtin/fetch.c:514
+#: builtin/fetch.c:515
msgid "[new ref]"
msgstr "[Ð½Ð¾Ð²Ð°Ñ ÑÑылка]"
-#: builtin/fetch.c:559
+#: builtin/fetch.c:560
msgid "unable to update local ref"
msgstr "не удалоÑÑŒ обновить локальную ÑÑылку"
-#: builtin/fetch.c:559
+#: builtin/fetch.c:560
msgid "forced update"
msgstr "принудительное обновление"
-#: builtin/fetch.c:565
+#: builtin/fetch.c:566
msgid "(non-fast-forward)"
msgstr "(без перемотки вперед)"
-#: builtin/fetch.c:599 builtin/fetch.c:832
+#: builtin/fetch.c:600 builtin/fetch.c:842
#, c-format
msgid "cannot open %s: %s\n"
msgstr "не удалоÑÑŒ открыть %s: %s\n"
-#: builtin/fetch.c:608
+#: builtin/fetch.c:609
#, c-format
msgid "%s did not send all necessary objects\n"
msgstr "%s не отправил вÑе необходимые объекты\n"
-#: builtin/fetch.c:626
+#: builtin/fetch.c:627
#, c-format
msgid "reject %s because shallow roots are not allowed to be updated"
msgstr "%s отклонено из-за того, что чаÑтичные корни не разрешено обновлÑÑ‚ÑŒ"
-#: builtin/fetch.c:714 builtin/fetch.c:797
+#: builtin/fetch.c:715 builtin/fetch.c:807
#, c-format
msgid "From %.*s\n"
msgstr "Из %.*s\n"
-#: builtin/fetch.c:725
+#: builtin/fetch.c:726
#, c-format
msgid ""
"some local refs could not be updated; try running\n"
" 'git remote prune %s' to remove any old, conflicting branches"
msgstr "не удалоÑÑŒ обновить некоторые локальные ÑÑылки; попробуйте запуÑтить «git remote prune %s», чтобы почиÑтить Ñтарые, конфликтующие ветки"
-#: builtin/fetch.c:777
+#: builtin/fetch.c:778
#, c-format
msgid " (%s will become dangling)"
msgstr " (%s будет виÑÑщей веткой)"
-#: builtin/fetch.c:778
+#: builtin/fetch.c:779
#, c-format
msgid " (%s has become dangling)"
msgstr " (%s Ñтала виÑÑщей веткой)"
-#: builtin/fetch.c:802
+#: builtin/fetch.c:811
msgid "[deleted]"
msgstr "[удалено]"
-#: builtin/fetch.c:803 builtin/remote.c:1057
+#: builtin/fetch.c:812 builtin/remote.c:1034
msgid "(none)"
msgstr "(нет)"
-#: builtin/fetch.c:822
+#: builtin/fetch.c:832
#, c-format
msgid "Refusing to fetch into current branch %s of non-bare repository"
msgstr "Отказ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð² текущую ветку %s не голого репозиториÑ"
-#: builtin/fetch.c:841
+#: builtin/fetch.c:851
#, c-format
msgid "Option \"%s\" value \"%s\" is not valid for %s"
msgstr "Ðеправильное значение «%2$s» Ð´Ð»Ñ Ð¿Ð°Ñ€Ð°Ð¼ÐµÑ‚Ñ€Ð° «%1$s» Ð´Ð»Ñ %3$s"
-#: builtin/fetch.c:844
+#: builtin/fetch.c:854
#, c-format
msgid "Option \"%s\" is ignored for %s\n"
msgstr "Параметр «%s» игнорируетÑÑ Ð´Ð»Ñ %s\n"
-#: builtin/fetch.c:900
+#: builtin/fetch.c:910
#, c-format
msgid "Don't know how to fetch from %s"
msgstr "Ðе знаю как извлечь Ñ %s"
-#: builtin/fetch.c:1063
+#: builtin/fetch.c:1071
#, c-format
msgid "Fetching %s\n"
msgstr "Извлечение из %s\n"
-#: builtin/fetch.c:1065 builtin/remote.c:90
+#: builtin/fetch.c:1073 builtin/remote.c:90
#, c-format
msgid "Could not fetch %s"
msgstr "Ðе удалоÑÑŒ извлечь %s"
-#: builtin/fetch.c:1083
+#: builtin/fetch.c:1091
msgid ""
"No remote repository specified. Please, specify either a URL or a\n"
"remote name from which new revisions should be fetched."
msgstr "Ðе указан внешний репозиторий. Укажите URL или Ð¸Ð¼Ñ Ð²Ð½ÐµÑˆÐ½ÐµÐ³Ð¾ Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ð¸Ñ Ð¸Ð· которого должны извлекатьÑÑ Ð½Ð¾Ð²Ñ‹Ðµ редакции."
-#: builtin/fetch.c:1106
+#: builtin/fetch.c:1114
msgid "You need to specify a tag name."
msgstr "Вам нужно указать Ð¸Ð¼Ñ Ð¼ÐµÑ‚ÐºÐ¸."
-#: builtin/fetch.c:1148
+#: builtin/fetch.c:1156
msgid "--depth and --unshallow cannot be used together"
msgstr "Ð½ÐµÐ»ÑŒÐ·Ñ Ð¸Ñпользовать одновременно --depth и --unshallow"
-#: builtin/fetch.c:1150
+#: builtin/fetch.c:1158
msgid "--unshallow on a complete repository does not make sense"
msgstr "--unshallow не имеет ÑмыÑла на полном репозитории"
-#: builtin/fetch.c:1173
+#: builtin/fetch.c:1181
msgid "fetch --all does not take a repository argument"
msgstr "fetch --all не принимает Ð¸Ð¼Ñ Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ð¸Ñ ÐºÐ°Ðº аргумент"
-#: builtin/fetch.c:1175
+#: builtin/fetch.c:1183
msgid "fetch --all does not make sense with refspecs"
msgstr "fetch --all не имеет ÑмыÑла при указании Ñпецификаций ÑÑылок"
-#: builtin/fetch.c:1186
+#: builtin/fetch.c:1194
#, c-format
msgid "No such remote or remote group: %s"
msgstr "Ðет такого внешнего Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ð¸Ñ Ð¸Ð»Ð¸ группы: %s"
-#: builtin/fetch.c:1194
+#: builtin/fetch.c:1202
msgid "Fetching a group and specifying refspecs does not make sense"
msgstr "Получение группы и указание Ñпецификаций ÑÑылок не имеет ÑмыÑла"
-#: builtin/fmt-merge-msg.c:13
+#: builtin/fmt-merge-msg.c:14
msgid ""
"git fmt-merge-msg [-m <message>] [--log[=<n>] | --no-log] [--file <file>]"
msgstr "git fmt-merge-msg [-m <Ñообщение>] [--log[=<n>] | --no-log] [--file <файл>]"
-#: builtin/fmt-merge-msg.c:668 builtin/fmt-merge-msg.c:671 builtin/grep.c:698
-#: builtin/merge.c:198 builtin/repack.c:178 builtin/repack.c:182
-#: builtin/show-branch.c:664 builtin/show-ref.c:180 builtin/tag.c:590
-#: parse-options.h:131 parse-options.h:238
-msgid "n"
-msgstr "n"
-
-#: builtin/fmt-merge-msg.c:669
+#: builtin/fmt-merge-msg.c:670
msgid "populate log with at most <n> entries from shortlog"
msgstr "отправить в журнал <n> запиÑей из короткого журнала"
-#: builtin/fmt-merge-msg.c:672
+#: builtin/fmt-merge-msg.c:673
msgid "alias for --log (deprecated)"
msgstr "Ñокращение Ð´Ð»Ñ --log (уÑтаревшее)"
-#: builtin/fmt-merge-msg.c:675
+#: builtin/fmt-merge-msg.c:676
msgid "text"
msgstr "текÑÑ‚"
-#: builtin/fmt-merge-msg.c:676
+#: builtin/fmt-merge-msg.c:677
msgid "use <text> as start of message"
msgstr "иÑпользовать <текÑÑ‚> как начальное Ñообщение"
-#: builtin/fmt-merge-msg.c:677
+#: builtin/fmt-merge-msg.c:678
msgid "file to read from"
msgstr "файл Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ"
-#: builtin/for-each-ref.c:687
-msgid "unable to parse format"
-msgstr "не удалоÑÑŒ разобрать формат"
-
-#: builtin/for-each-ref.c:1083
+#: builtin/for-each-ref.c:9
msgid "git for-each-ref [<options>] [<pattern>]"
msgstr "git for-each-ref [<опции>] [<шаблон>]"
-#: builtin/for-each-ref.c:1098
+#: builtin/for-each-ref.c:24
msgid "quote placeholders suitably for shells"
msgstr "выводить указатели меÑта Ð·Ð°Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ð² подходÑщем формате Ð´Ð»Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð½Ð¾Ð³Ð¾ процеÑÑора"
-#: builtin/for-each-ref.c:1100
+#: builtin/for-each-ref.c:26
msgid "quote placeholders suitably for perl"
msgstr "выводить указатели меÑта Ð·Ð°Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ð² подходÑщем формате Ð´Ð»Ñ perl"
-#: builtin/for-each-ref.c:1102
+#: builtin/for-each-ref.c:28
msgid "quote placeholders suitably for python"
msgstr "выводить указатели меÑта Ð·Ð°Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ð² подходÑщем формате Ð´Ð»Ñ python"
-#: builtin/for-each-ref.c:1104
+#: builtin/for-each-ref.c:30
msgid "quote placeholders suitably for Tcl"
msgstr "выводить указатели меÑта Ð·Ð°Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ð² подходÑщем формате Ð´Ð»Ñ Tcl"
-#: builtin/for-each-ref.c:1107
+#: builtin/for-each-ref.c:33
msgid "show only <n> matched refs"
msgstr "показать только <n> Ñовпадающих ÑÑылок"
-#: builtin/for-each-ref.c:1108 builtin/replace.c:438
-msgid "format"
-msgstr "формат"
-
-#: builtin/for-each-ref.c:1108
+#: builtin/for-each-ref.c:34
msgid "format to use for the output"
msgstr "иÑпользовать формат Ð´Ð»Ñ Ð²Ñ‹Ð²Ð¾Ð´Ð°"
-#: builtin/for-each-ref.c:1109
+#: builtin/for-each-ref.c:35
msgid "key"
msgstr "ключ"
-#: builtin/for-each-ref.c:1110
+#: builtin/for-each-ref.c:36
msgid "field name to sort on"
msgstr "Ð¸Ð¼Ñ Ð¿Ð¾Ð»Ñ, по которому выполнить Ñортировку"
-#: builtin/fsck.c:147 builtin/prune.c:137
+#: builtin/fsck.c:163 builtin/prune.c:137
msgid "Checking connectivity"
msgstr "Проверка ÑоединениÑ"
-#: builtin/fsck.c:548
+#: builtin/fsck.c:568
msgid "Checking object directories"
msgstr "Проверка каталогов объектов"
-#: builtin/fsck.c:611
+#: builtin/fsck.c:631
msgid "git fsck [<options>] [<object>...]"
msgstr "git fsck [<опции>] [<объект>…]"
-#: builtin/fsck.c:617
+#: builtin/fsck.c:637
msgid "show unreachable objects"
msgstr "показать недоÑтупные объекты"
-#: builtin/fsck.c:618
+#: builtin/fsck.c:638
msgid "show dangling objects"
msgstr "показать объекты, на которые нет ÑÑылок"
-#: builtin/fsck.c:619
+#: builtin/fsck.c:639
msgid "report tags"
msgstr "вывеÑти отчет по меткам"
-#: builtin/fsck.c:620
+#: builtin/fsck.c:640
msgid "report root nodes"
msgstr "вывеÑти отчет по корневым узлам"
-#: builtin/fsck.c:621
+#: builtin/fsck.c:641
msgid "make index objects head nodes"
msgstr "воÑпринимать объекты в индекÑе как корневые узлы"
-#: builtin/fsck.c:622
+#: builtin/fsck.c:642
msgid "make reflogs head nodes (default)"
msgstr "Ñоздать корневые узлы журналов ÑÑылок (по умолчанию)"
-#: builtin/fsck.c:623
+#: builtin/fsck.c:643
msgid "also consider packs and alternate objects"
msgstr "также проверÑÑ‚ÑŒ пакеты и альтернативные объекты"
-#: builtin/fsck.c:624
+#: builtin/fsck.c:644
+msgid "check only connectivity"
+msgstr "только проверить Ñоединение"
+
+#: builtin/fsck.c:645
msgid "enable more strict checking"
msgstr "иÑпользовать более Ñтрогую проверку"
-#: builtin/fsck.c:626
+#: builtin/fsck.c:647
msgid "write dangling objects in .git/lost-found"
msgstr "запиÑать объекты на которые нет ÑÑылок в .git/lost-found"
-#: builtin/fsck.c:627 builtin/prune.c:107
+#: builtin/fsck.c:648 builtin/prune.c:107
msgid "show progress"
msgstr "показать прогреÑÑ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ"
-#: builtin/fsck.c:677
+#: builtin/fsck.c:707
msgid "Checking objects"
msgstr "Проверка объектов"
-#: builtin/gc.c:24
+#: builtin/gc.c:25
msgid "git gc [<options>]"
msgstr "git gc [<опции>]"
-#: builtin/gc.c:67
+#: builtin/gc.c:55
#, c-format
msgid "Invalid %s: '%s'"
msgstr "ÐедейÑтвительный %s: «%s»"
-#: builtin/gc.c:112
+#: builtin/gc.c:100
#, c-format
msgid "insanely long object directory %.*s"
msgstr "Ñлишком длинный путь к каталогу объекта %.*s"
-#: builtin/gc.c:281
+#: builtin/gc.c:269
msgid "prune unreferenced objects"
msgstr "почиÑтить объекты, на которые нет ÑÑылок"
-#: builtin/gc.c:283
+#: builtin/gc.c:271
msgid "be more thorough (increased runtime)"
msgstr "проверÑÑ‚ÑŒ более внимательно (занимает больше времени)"
-#: builtin/gc.c:284
+#: builtin/gc.c:272
msgid "enable auto-gc mode"
msgstr "включить режим auto-gc"
-#: builtin/gc.c:285
+#: builtin/gc.c:273
msgid "force running gc even if there may be another gc running"
msgstr "принудительно запуÑтить gc, даже еÑÑ‚ÑŒ Ð´Ñ€ÑƒÐ³Ð°Ñ ÐºÐ¾Ð¿Ð¸Ñ gc уже запущена"
-#: builtin/gc.c:327
+#: builtin/gc.c:315
#, c-format
msgid "Auto packing the repository in background for optimum performance.\n"
msgstr "ÐвтоматичеÑÐºÐ°Ñ ÑƒÐ¿Ð°ÐºÐ¾Ð²ÐºÐ° Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ð¸Ñ Ð² фоне, Ð´Ð»Ñ Ð¾Ð¿Ñ‚Ð¸Ð¼Ð°Ð»ÑŒÐ½Ð¾Ð¹ производительноÑти.\n"
-#: builtin/gc.c:329
+#: builtin/gc.c:317
#, c-format
msgid "Auto packing the repository for optimum performance.\n"
msgstr "ÐвтоматичеÑÐºÐ°Ñ ÑƒÐ¿Ð°ÐºÐ¾Ð²ÐºÐ° репозиториÑ, Ð´Ð»Ñ Ð¾Ð¿Ñ‚Ð¸Ð¼Ð°Ð»ÑŒÐ½Ð¾Ð¹ производительноÑти.\n"
-#: builtin/gc.c:330
+#: builtin/gc.c:318
#, c-format
msgid "See \"git help gc\" for manual housekeeping.\n"
msgstr "Смотрите «git help gc» руководÑтва по ручной очиÑтке.\n"
-#: builtin/gc.c:348
+#: builtin/gc.c:336
#, c-format
msgid ""
"gc is already running on machine '%s' pid %<PRIuMAX> (use --force if not)"
msgstr "gc уже запущен на Ñтом компьютере «%s» pid %<PRIuMAX> (еÑли нет, иÑпользуйте --force)"
-#: builtin/gc.c:376
+#: builtin/gc.c:364
msgid ""
"There are too many unreachable loose objects; run 'git prune' to remove "
"them."
@@ -5730,7 +6169,7 @@ msgstr "git hash-object [-t <тип>] [-w] [--path=<файл> | --no-filters] [-
msgid "git hash-object --stdin-paths < <list-of-paths>"
msgstr "git hash-object --stdin-paths < <ÑпиÑок-путей>"
-#: builtin/hash-object.c:92 builtin/tag.c:612
+#: builtin/hash-object.c:92 builtin/tag.c:614
msgid "type"
msgstr "тип"
@@ -5879,27 +6318,27 @@ msgstr "иÑпользование: %s%s"
msgid "`git %s' is aliased to `%s'"
msgstr "«git %s» — Ñто Ñокращение Ð´Ð»Ñ Â«%s»"
-#: builtin/index-pack.c:151
+#: builtin/index-pack.c:152
#, c-format
msgid "unable to open %s"
msgstr "не удалоÑÑŒ открыть %s"
-#: builtin/index-pack.c:201
+#: builtin/index-pack.c:202
#, c-format
msgid "object type mismatch at %s"
msgstr "неÑоответÑтвие типа объекта на %s"
-#: builtin/index-pack.c:221
+#: builtin/index-pack.c:222
#, c-format
msgid "did not receive expected object %s"
msgstr "ожидаемый объект не получен на %s"
-#: builtin/index-pack.c:224
+#: builtin/index-pack.c:225
#, c-format
msgid "object %s: expected type %s, found %s"
msgstr "объект %s: ожидаемый тип %s, получен %s"
-#: builtin/index-pack.c:266
+#: builtin/index-pack.c:267
#, c-format
msgid "cannot fill %d byte"
msgid_plural "cannot fill %d bytes"
@@ -5908,69 +6347,69 @@ msgstr[1] "не удалоÑÑŒ заполнить %d байта"
msgstr[2] "не удалоÑÑŒ заполнить %d байтов"
msgstr[3] "не удалоÑÑŒ заполнить %d байтов"
-#: builtin/index-pack.c:276
+#: builtin/index-pack.c:277
msgid "early EOF"
msgstr "неожиданный конец файла"
-#: builtin/index-pack.c:277
+#: builtin/index-pack.c:278
msgid "read error on input"
msgstr "ошибка Ñ‡Ñ‚ÐµÐ½Ð¸Ñ Ð²Ð²Ð¾Ð´Ð°"
-#: builtin/index-pack.c:289
+#: builtin/index-pack.c:290
msgid "used more bytes than were available"
msgstr "иÑпользовано больше байт, чем было доÑтупно"
-#: builtin/index-pack.c:296
+#: builtin/index-pack.c:297
msgid "pack too large for current definition of off_t"
msgstr "пакет Ñлишком большой Ð´Ð»Ñ Ñ‚ÐµÐºÑƒÑ‰ÐµÐ³Ð¾ Ð¾Ð¿Ñ€ÐµÐ´ÐµÐ»ÐµÐ½Ð¸Ñ off_t"
-#: builtin/index-pack.c:312
+#: builtin/index-pack.c:313
#, c-format
msgid "unable to create '%s'"
msgstr "не удалоÑÑŒ Ñоздать «%s»"
-#: builtin/index-pack.c:317
+#: builtin/index-pack.c:318
#, c-format
msgid "cannot open packfile '%s'"
msgstr "не удалоÑÑŒ открыть файл пакета «%s»"
-#: builtin/index-pack.c:331
+#: builtin/index-pack.c:332
msgid "pack signature mismatch"
msgstr "неÑоответÑтвие подпиÑи пакета"
-#: builtin/index-pack.c:333
+#: builtin/index-pack.c:334
#, c-format
msgid "pack version %<PRIu32> unsupported"
msgstr "верÑÐ¸Ñ Ð¿Ð°ÐºÐµÑ‚Ð° %<PRIu32> не поддерживаетÑÑ"
-#: builtin/index-pack.c:351
+#: builtin/index-pack.c:352
#, c-format
msgid "pack has bad object at offset %lu: %s"
msgstr "в пакете ÑодержитÑÑ Ð¿Ð¾Ð²Ñ€ÐµÐ¶Ð´ÐµÐ½Ð½Ñ‹Ð¹ объект по Ñмещению %lu: %s"
-#: builtin/index-pack.c:472
+#: builtin/index-pack.c:473
#, c-format
msgid "inflate returned %d"
msgstr "программа ÑÐ¶Ð°Ñ‚Ð¸Ñ Ð²ÐµÑ€Ð½ÑƒÐ»Ð° %d"
-#: builtin/index-pack.c:521
+#: builtin/index-pack.c:522
msgid "offset value overflow for delta base object"
msgstr "переполнение Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ ÑÐ¼ÐµÑ‰ÐµÐ½Ð¸Ñ Ñƒ базового объекта дельты"
-#: builtin/index-pack.c:529
+#: builtin/index-pack.c:530
msgid "delta base offset is out of bound"
msgstr "Ñмещение базовой дельты вышло за допуÑтимые пределы"
-#: builtin/index-pack.c:537
+#: builtin/index-pack.c:538
#, c-format
msgid "unknown object type %d"
msgstr "неизвеÑтный тип объекта %d"
-#: builtin/index-pack.c:568
+#: builtin/index-pack.c:569
msgid "cannot pread pack file"
msgstr "не удалоÑÑŒ выполнить pread Ð´Ð»Ñ Ñ„Ð°Ð¹Ð»Ð° пакета"
-#: builtin/index-pack.c:570
+#: builtin/index-pack.c:571
#, c-format
msgid "premature end of pack file, %lu byte missing"
msgid_plural "premature end of pack file, %lu bytes missing"
@@ -5979,33 +6418,33 @@ msgstr[1] "преждевременное окончание файла паке
msgstr[2] "преждевременное окончание файла пакета, %lu байтов отÑутÑтвует"
msgstr[3] "преждевременное окончание файла пакета, %lu байтов отÑутÑтвует"
-#: builtin/index-pack.c:596
+#: builtin/index-pack.c:597
msgid "serious inflate inconsistency"
msgstr "Ñерьезное неÑоответÑтвие при раÑпаковке"
-#: builtin/index-pack.c:742 builtin/index-pack.c:748 builtin/index-pack.c:771
-#: builtin/index-pack.c:805 builtin/index-pack.c:814
+#: builtin/index-pack.c:743 builtin/index-pack.c:749 builtin/index-pack.c:772
+#: builtin/index-pack.c:806 builtin/index-pack.c:815
#, c-format
msgid "SHA1 COLLISION FOUND WITH %s !"
msgstr "ÐÐЙДЕÐРКОЛЛИЗИЯ SHA1 С %s !"
-#: builtin/index-pack.c:745 builtin/pack-objects.c:162
+#: builtin/index-pack.c:746 builtin/pack-objects.c:162
#: builtin/pack-objects.c:254
#, c-format
msgid "unable to read %s"
msgstr "не удалоÑÑŒ прочитать %s"
-#: builtin/index-pack.c:811
+#: builtin/index-pack.c:812
#, c-format
msgid "cannot read existing object %s"
msgstr "не удалоÑÑŒ прочитать ÑущеÑтвующий объект %s"
-#: builtin/index-pack.c:825
+#: builtin/index-pack.c:826
#, c-format
msgid "invalid blob object %s"
msgstr "неправильный файл двоичного объекта %s"
-#: builtin/index-pack.c:839
+#: builtin/index-pack.c:840
#, c-format
msgid "invalid %s"
msgstr "неправильный %s"
@@ -6121,7 +6560,7 @@ msgstr "плохой pack.indexversion=%<PRIu32>"
msgid "invalid number of threads specified (%d)"
msgstr "указано неправильное количеÑтво потоков (%d)"
-#: builtin/index-pack.c:1479 builtin/index-pack.c:1658
+#: builtin/index-pack.c:1479 builtin/index-pack.c:1663
#, c-format
msgid "no threads support, ignoring %s"
msgstr "нет поддержки потоков, игнорирование %s"
@@ -6154,110 +6593,110 @@ msgstr[1] "длина цепочки = %d: %lu объекта"
msgstr[2] "длина цепочки = %d: %lu объектов"
msgstr[3] "длина цепочки = %d: %lu объектов"
-#: builtin/index-pack.c:1622
+#: builtin/index-pack.c:1623
msgid "Cannot come back to cwd"
msgstr "Ðе удалоÑÑŒ вернутьÑÑ Ð² текущий рабочий каталог"
-#: builtin/index-pack.c:1670 builtin/index-pack.c:1673
-#: builtin/index-pack.c:1685 builtin/index-pack.c:1689
+#: builtin/index-pack.c:1675 builtin/index-pack.c:1678
+#: builtin/index-pack.c:1690 builtin/index-pack.c:1694
#, c-format
msgid "bad %s"
msgstr "плохой %s"
-#: builtin/index-pack.c:1703
+#: builtin/index-pack.c:1708
msgid "--fix-thin cannot be used without --stdin"
msgstr "--fix-thin Ð½ÐµÐ»ÑŒÐ·Ñ Ð¸Ñпользовать без --stdin"
-#: builtin/index-pack.c:1707 builtin/index-pack.c:1716
+#: builtin/index-pack.c:1712 builtin/index-pack.c:1721
#, c-format
msgid "packfile name '%s' does not end with '.pack'"
msgstr "Ð¸Ð¼Ñ Ð¿Ð°ÐºÐµÑ‚Ð° «%s» не оканчиваетÑÑ Ð½Ð° «.pack»"
-#: builtin/index-pack.c:1724
+#: builtin/index-pack.c:1729
msgid "--verify with no packfile name given"
msgstr "--verify без ÑƒÐºÐ°Ð·Ð°Ð½Ð¸Ñ Ð¸Ð¼ÐµÐ½Ð¸ файла пакета"
-#: builtin/init-db.c:35
+#: builtin/init-db.c:36
#, c-format
msgid "Could not make %s writable by group"
msgstr "Ðе удалоÑÑŒ предоÑтавить доÑтуп к %s на запиÑÑŒ"
-#: builtin/init-db.c:62
+#: builtin/init-db.c:63
#, c-format
msgid "insanely long template name %s"
msgstr "Ñлишком длинное Ð¸Ð¼Ñ ÑˆÐ°Ð±Ð»Ð¾Ð½Ð° %s"
-#: builtin/init-db.c:67
+#: builtin/init-db.c:68
#, c-format
msgid "cannot stat '%s'"
msgstr "не удалоÑÑŒ выполнить stat Ð´Ð»Ñ Â«%s»"
-#: builtin/init-db.c:73
+#: builtin/init-db.c:74
#, c-format
msgid "cannot stat template '%s'"
msgstr "не удалоÑÑŒ выполнить stat Ð´Ð»Ñ ÑˆÐ°Ð±Ð»Ð¾Ð½Ð° «%s»"
-#: builtin/init-db.c:80
+#: builtin/init-db.c:81
#, c-format
msgid "cannot opendir '%s'"
msgstr "не удалоÑÑŒ выполнить opendir Ð´Ð»Ñ Â«%s»"
-#: builtin/init-db.c:97
+#: builtin/init-db.c:98
#, c-format
msgid "cannot readlink '%s'"
msgstr "не удалоÑÑŒ выполнить readlink Ð´Ð»Ñ Â«%s»"
-#: builtin/init-db.c:99
+#: builtin/init-db.c:100
#, c-format
msgid "insanely long symlink %s"
msgstr "Ñлишком Ð´Ð»Ð¸Ð½Ð½Ð°Ñ ÑÐ¸Ð¼Ð²Ð¾Ð»ÑŒÐ½Ð°Ñ ÑÑылка %s"
-#: builtin/init-db.c:102
+#: builtin/init-db.c:103
#, c-format
msgid "cannot symlink '%s' '%s'"
msgstr "не удалоÑÑŒ Ñоздать Ñимвольную ÑÑылку «%s» на «%s»"
-#: builtin/init-db.c:106
+#: builtin/init-db.c:107
#, c-format
msgid "cannot copy '%s' to '%s'"
msgstr "не удалоÑÑŒ Ñкопировать файл «%s» в «%s»"
-#: builtin/init-db.c:110
+#: builtin/init-db.c:111
#, c-format
msgid "ignoring template %s"
msgstr "игнорирование шаблона %s"
-#: builtin/init-db.c:136
+#: builtin/init-db.c:137
#, c-format
msgid "insanely long template path %s"
msgstr "Ñлишком длинный путь шаблона %s"
-#: builtin/init-db.c:144
+#: builtin/init-db.c:145
#, c-format
msgid "templates not found %s"
msgstr "шаблоны не найдены %s"
-#: builtin/init-db.c:157
+#: builtin/init-db.c:158
#, c-format
msgid "not copying templates of a wrong format version %d from '%s'"
msgstr "не копирую шаблоны в неправильной верÑии формата %d из «%s»"
-#: builtin/init-db.c:211
+#: builtin/init-db.c:212
#, c-format
msgid "insane git directory %s"
msgstr "Ñлишком длинный путь к каталогу git %s"
-#: builtin/init-db.c:343 builtin/init-db.c:346
+#: builtin/init-db.c:344 builtin/init-db.c:347
#, c-format
msgid "%s already exists"
msgstr "%s уже ÑущеÑтвует"
-#: builtin/init-db.c:374
+#: builtin/init-db.c:375
#, c-format
msgid "unable to handle file type %d"
msgstr "не удаетÑÑ Ð¾Ð±Ñ€Ð°Ð±Ð¾Ñ‚Ð°Ñ‚ÑŒ файл типа %d"
-#: builtin/init-db.c:377
+#: builtin/init-db.c:378
#, c-format
msgid "unable to move %s to %s"
msgstr "не удаетÑÑ Ð¿ÐµÑ€ÐµÐ¼ÐµÑтить файл %s в %s"
@@ -6265,59 +6704,55 @@ msgstr "не удаетÑÑ Ð¿ÐµÑ€ÐµÐ¼ÐµÑтить файл %s в %s"
#. TRANSLATORS: The first '%s' is either "Reinitialized
#. existing" or "Initialized empty", the second " shared" or
#. "", and the last '%s%s' is the verbatim directory name.
-#: builtin/init-db.c:433
+#: builtin/init-db.c:434
#, c-format
msgid "%s%s Git repository in %s%s\n"
msgstr "%s%s репозиторий Git в %s%s\n"
-#: builtin/init-db.c:434
+#: builtin/init-db.c:435
msgid "Reinitialized existing"
msgstr "Ð ÐµÐ¸Ð½Ð¸Ñ†Ð¸Ð°Ð»Ð¸Ð·Ð°Ñ†Ð¸Ñ ÑущеÑтвующего"
-#: builtin/init-db.c:434
+#: builtin/init-db.c:435
msgid "Initialized empty"
msgstr "Инициализирован пуÑтой"
-#: builtin/init-db.c:435
+#: builtin/init-db.c:436
msgid " shared"
msgstr " общий"
-#: builtin/init-db.c:482
+#: builtin/init-db.c:483
msgid ""
"git init [-q | --quiet] [--bare] [--template=<template-directory>] "
"[--shared[=<permissions>]] [<directory>]"
msgstr "git init [-q | --quiet] [--bare] [--template=<каталог-шаблонов>] [--shared[=<права-доÑтупа>]] [<каталог>]"
-#: builtin/init-db.c:505
+#: builtin/init-db.c:506
msgid "permissions"
msgstr "права-доÑтупа"
-#: builtin/init-db.c:506
+#: builtin/init-db.c:507
msgid "specify that the git repository is to be shared amongst several users"
msgstr "укажите, еÑли репозиторий git будет иÑпользоватьÑÑ Ð½ÐµÑколькими пользователÑми"
-#: builtin/init-db.c:508 builtin/prune-packed.c:57 builtin/repack.c:171
-msgid "be quiet"
-msgstr "тихий режим"
-
-#: builtin/init-db.c:540 builtin/init-db.c:545
+#: builtin/init-db.c:541 builtin/init-db.c:546
#, c-format
msgid "cannot mkdir %s"
msgstr "не удалоÑÑŒ выполнить mkdir %s"
-#: builtin/init-db.c:549
+#: builtin/init-db.c:550
#, c-format
msgid "cannot chdir to %s"
msgstr "не удалоÑÑŒ выполнить chdir в %s"
-#: builtin/init-db.c:570
+#: builtin/init-db.c:571
#, c-format
msgid ""
"%s (or --work-tree=<directory>) not allowed without specifying %s (or --git-"
"dir=<directory>)"
msgstr "%s (или --work-tree=<каталог>) Ð½ÐµÐ»ÑŒÐ·Ñ Ð¸Ñпользовать без ÑƒÐºÐ°Ð·Ð°Ð½Ð¸Ñ %s (или --git-dir=<каталог>)"
-#: builtin/init-db.c:598
+#: builtin/init-db.c:599
#, c-format
msgid "Cannot access work tree '%s'"
msgstr "Ðе удалоÑÑŒ получить доÑтуп к рабочему каталогу «%s»"
@@ -6340,284 +6775,279 @@ msgstr "завершитель"
msgid "trailer(s) to add"
msgstr "завершители Ð´Ð»Ñ Ð´Ð¾Ð±Ð°Ð²Ð»ÐµÐ½Ð¸Ñ"
-#: builtin/log.c:41
+#: builtin/log.c:43
msgid "git log [<options>] [<revision-range>] [[--] <path>...]"
msgstr "git log [<опции>] [<диапазон-редакций>] [[--] <путь>…]"
-#: builtin/log.c:42
+#: builtin/log.c:44
msgid "git show [<options>] <object>..."
msgstr "git show [<опции>] <объект>…"
-#: builtin/log.c:81
+#: builtin/log.c:83
#, c-format
msgid "invalid --decorate option: %s"
msgstr "неправильный параметр Ð´Ð»Ñ --decorate: %s"
-#: builtin/log.c:127
+#: builtin/log.c:131
msgid "suppress diff output"
msgstr "не выводить различиÑ"
-#: builtin/log.c:128
+#: builtin/log.c:132
msgid "show source"
msgstr "показать иÑточник"
-#: builtin/log.c:129
+#: builtin/log.c:133
msgid "Use mail map file"
msgstr "ИÑпользовать файл ÑоответÑÑ‚Ð²Ð¸Ñ Ð¿Ð¾Ñ‡Ñ‚Ð¾Ð²Ñ‹Ñ… адреÑов"
-#: builtin/log.c:130
+#: builtin/log.c:134
msgid "decorate options"
msgstr "опции формата вывода ÑÑылок"
-#: builtin/log.c:133
+#: builtin/log.c:137
msgid "Process line range n,m in file, counting from 1"
msgstr "Обработать диапазон Ñтрок n,m из файла, Ð½Ð°Ñ‡Ð¸Ð½Ð°Ñ Ñ 1"
-#: builtin/log.c:229
+#: builtin/log.c:233
#, c-format
msgid "Final output: %d %s\n"
msgstr "Финальный вывод: %d %s\n"
-#: builtin/log.c:458
+#: builtin/log.c:465
#, c-format
msgid "git show %s: bad file"
msgstr "git show %s: плохой файл"
-#: builtin/log.c:472 builtin/log.c:564
+#: builtin/log.c:479 builtin/log.c:572
#, c-format
msgid "Could not read object %s"
msgstr "Ðе удалоÑÑŒ прочитать объект %s"
-#: builtin/log.c:588
+#: builtin/log.c:596
#, c-format
msgid "Unknown type: %d"
msgstr "ÐеизвеÑтный тип объекта: %d"
-#: builtin/log.c:689
+#: builtin/log.c:714
msgid "format.headers without value"
msgstr "в format.headers не указано значение"
-#: builtin/log.c:773
+#: builtin/log.c:798
msgid "name of output directory is too long"
msgstr "Ñлишком длинное Ð¸Ð¼Ñ Ð²Ñ‹Ñ…Ð¾Ð´Ð½Ð¾Ð³Ð¾ каталога"
-#: builtin/log.c:789
+#: builtin/log.c:814
#, c-format
msgid "Cannot open patch file %s"
msgstr "Ðу удалоÑÑŒ открыть файл изменений %s"
-#: builtin/log.c:803
+#: builtin/log.c:828
msgid "Need exactly one range."
msgstr "Ðужен только один диапазон."
-#: builtin/log.c:813
+#: builtin/log.c:838
msgid "Not a range."
msgstr "Ðе ÑвлÑетÑÑ Ð´Ð¸Ð°Ð¿Ð°Ð·Ð¾Ð½Ð¾Ð¼."
-#: builtin/log.c:919
+#: builtin/log.c:944
msgid "Cover letter needs email format"
msgstr "Сопроводительное пиÑьмо должно быть в формате Ñлектронной почты"
-#: builtin/log.c:998
+#: builtin/log.c:1023
#, c-format
msgid "insane in-reply-to: %s"
msgstr "ошибка в поле in-reply-to: %s"
-#: builtin/log.c:1026
+#: builtin/log.c:1051
msgid "git format-patch [<options>] [<since> | <revision-range>]"
msgstr "git format-patch [<опции>] [<начинаÑ-Ñ> | <диапазон-редакций>]"
-#: builtin/log.c:1071
+#: builtin/log.c:1096
msgid "Two output directories?"
msgstr "Два выходных каталога?"
-#: builtin/log.c:1186
+#: builtin/log.c:1211
msgid "use [PATCH n/m] even with a single patch"
msgstr "выводить [PATCH n/m] даже когда один патч"
-#: builtin/log.c:1189
+#: builtin/log.c:1214
msgid "use [PATCH] even with multiple patches"
msgstr "выводить [PATCH] даже когда неÑколько патчей"
-#: builtin/log.c:1193
+#: builtin/log.c:1218
msgid "print patches to standard out"
msgstr "выводить патчи на Ñтандартный вывод"
-#: builtin/log.c:1195
+#: builtin/log.c:1220
msgid "generate a cover letter"
msgstr "генерировать Ñопроводительное пиÑьмо"
-#: builtin/log.c:1197
+#: builtin/log.c:1222
msgid "use simple number sequence for output file names"
msgstr "иÑпользовать проÑтую поÑледовательноÑÑ‚ÑŒ чиÑел Ð´Ð»Ñ Ð¸Ð¼ÐµÐ½ выходных файлов"
-#: builtin/log.c:1198
+#: builtin/log.c:1223
msgid "sfx"
msgstr "ÑуффикÑ"
-#: builtin/log.c:1199
+#: builtin/log.c:1224
msgid "use <sfx> instead of '.patch'"
msgstr "иÑпользовать ÑÑƒÑ„Ñ„Ð¸ÐºÑ <ÑуффикÑ> вмеÑто «.patch»"
-#: builtin/log.c:1201
+#: builtin/log.c:1226
msgid "start numbering patches at <n> instead of 1"
msgstr "начать нумерацию патчей Ñ <n>, а не Ñ 1"
-#: builtin/log.c:1203
+#: builtin/log.c:1228
msgid "mark the series as Nth re-roll"
msgstr "пометить Ñерию как Ñнную попытку"
-#: builtin/log.c:1205
+#: builtin/log.c:1230
msgid "Use [<prefix>] instead of [PATCH]"
msgstr "ИÑпользовать [<префикÑ>] вмеÑто [PATCH]"
-#: builtin/log.c:1208
+#: builtin/log.c:1233
msgid "store resulting files in <dir>"
msgstr "Ñохранить результирующие файлы в <каталог>"
-#: builtin/log.c:1211
+#: builtin/log.c:1236
msgid "don't strip/add [PATCH]"
msgstr "не обрезать/добавлÑÑ‚ÑŒ [PATCH]"
-#: builtin/log.c:1214
+#: builtin/log.c:1239
msgid "don't output binary diffs"
msgstr "не выводить двоичные различиÑ"
-#: builtin/log.c:1216
+#: builtin/log.c:1241
msgid "don't include a patch matching a commit upstream"
msgstr "не включать патч, еÑли коммит уже еÑÑ‚ÑŒ в вышеÑтоÑщей ветке"
-#: builtin/log.c:1218
+#: builtin/log.c:1243
msgid "show patch format instead of default (patch + stat)"
msgstr "выводить в формате патча, а не в Ñтандартном (патч + ÑтатиÑтика)"
-#: builtin/log.c:1220
+#: builtin/log.c:1245
msgid "Messaging"
msgstr "Передача Ñообщений"
-#: builtin/log.c:1221
+#: builtin/log.c:1246
msgid "header"
msgstr "заголовок"
-#: builtin/log.c:1222
+#: builtin/log.c:1247
msgid "add email header"
msgstr "добавить заголовок ÑообщениÑ"
-#: builtin/log.c:1223 builtin/log.c:1225
+#: builtin/log.c:1248 builtin/log.c:1250
msgid "email"
msgstr "почта"
-#: builtin/log.c:1223
+#: builtin/log.c:1248
msgid "add To: header"
msgstr "добавить заголовок To:"
-#: builtin/log.c:1225
+#: builtin/log.c:1250
msgid "add Cc: header"
msgstr "добавить заголовок Cc:"
-#: builtin/log.c:1227
+#: builtin/log.c:1252
msgid "ident"
msgstr "идентификатор"
-#: builtin/log.c:1228
+#: builtin/log.c:1253
msgid "set From address to <ident> (or committer ident if absent)"
msgstr "уÑтановить Ð°Ð´Ñ€ÐµÑ Ð¾Ñ‚Ð¿Ñ€Ð°Ð²Ð¸Ñ‚ÐµÐ»Ñ Ð½Ð° <идентификатор> (или на идентификатор коммитера, еÑли отÑутÑтвует)"
-#: builtin/log.c:1230
+#: builtin/log.c:1255
msgid "message-id"
msgstr "идентификатор-ÑообщениÑ"
-#: builtin/log.c:1231
+#: builtin/log.c:1256
msgid "make first mail a reply to <message-id>"
msgstr "Ñделать первое пиÑьмо ответом на <идентификатор-ÑообщениÑ>"
-#: builtin/log.c:1232 builtin/log.c:1235
+#: builtin/log.c:1257 builtin/log.c:1260
msgid "boundary"
msgstr "вложение"
-#: builtin/log.c:1233
+#: builtin/log.c:1258
msgid "attach the patch"
msgstr "приложить патч"
-#: builtin/log.c:1236
+#: builtin/log.c:1261
msgid "inline the patch"
msgstr "включить патч в текÑÑ‚ пиÑьма"
-#: builtin/log.c:1240
+#: builtin/log.c:1265
msgid "enable message threading, styles: shallow, deep"
msgstr "включить в пиÑьмах иерархичноÑÑ‚ÑŒ, Ñтили: shallow (чаÑтичную), deep (глубокую)"
-#: builtin/log.c:1242
+#: builtin/log.c:1267
msgid "signature"
msgstr "подпиÑÑŒ"
-#: builtin/log.c:1243
+#: builtin/log.c:1268
msgid "add a signature"
msgstr "добавить подпиÑÑŒ"
-#: builtin/log.c:1245
+#: builtin/log.c:1270
msgid "add a signature from a file"
msgstr "добавить подпиÑÑŒ из файла"
-#: builtin/log.c:1246
+#: builtin/log.c:1271
msgid "don't print the patch filenames"
msgstr "не выводить имена файлов патчей"
-#: builtin/log.c:1320
-#, c-format
-msgid "invalid ident line: %s"
-msgstr "Ð½ÐµÐ¿Ñ€Ð°Ð²Ð¸Ð»ÑŒÐ½Ð°Ñ Ñтрока идентификации: %s"
-
-#: builtin/log.c:1335
+#: builtin/log.c:1360
msgid "-n and -k are mutually exclusive."
msgstr "-n и -k Ð½ÐµÐ»ÑŒÐ·Ñ Ð¸Ñпользовать одновременно"
-#: builtin/log.c:1337
+#: builtin/log.c:1362
msgid "--subject-prefix and -k are mutually exclusive."
msgstr "--subject-prefix и -k Ð½ÐµÐ»ÑŒÐ·Ñ Ð¸Ñпользовать одновременно."
-#: builtin/log.c:1345
+#: builtin/log.c:1370
msgid "--name-only does not make sense"
msgstr "--name-only не имеет ÑмыÑла"
-#: builtin/log.c:1347
+#: builtin/log.c:1372
msgid "--name-status does not make sense"
msgstr "--name-status не имеет ÑмыÑла"
-#: builtin/log.c:1349
+#: builtin/log.c:1374
msgid "--check does not make sense"
msgstr "--check не имеет ÑмыÑла"
-#: builtin/log.c:1372
+#: builtin/log.c:1397
msgid "standard output, or directory, which one?"
msgstr "Ñтандартный вывод или каталог?"
-#: builtin/log.c:1374
+#: builtin/log.c:1399
#, c-format
msgid "Could not create directory '%s'"
msgstr "Ðе удалоÑÑŒ Ñоздать каталог «%s»"
-#: builtin/log.c:1472
+#: builtin/log.c:1496
#, c-format
msgid "unable to read signature file '%s'"
msgstr "не удалоÑÑŒ прочитать файл подпиÑи «%s»"
-#: builtin/log.c:1535
+#: builtin/log.c:1559
msgid "Failed to create output files"
msgstr "Сбой при Ñоздании выходных файлов"
-#: builtin/log.c:1583
+#: builtin/log.c:1607
msgid "git cherry [-v] [<upstream> [<head> [<limit>]]]"
msgstr "git cherry [-v] [<вышеÑтоÑщаÑ-ветка> [<голова> [<ограничение>]]]"
-#: builtin/log.c:1637
+#: builtin/log.c:1661
#, c-format
msgid ""
"Could not find a tracked remote branch, please specify <upstream> "
"manually.\n"
msgstr "Ðе удалоÑÑŒ найти отÑлеживаемую внешнюю ветку, укажите <вышеÑтоÑщую-ветку> вручную.\n"
-#: builtin/log.c:1648 builtin/log.c:1650 builtin/log.c:1662
+#: builtin/log.c:1672 builtin/log.c:1674 builtin/log.c:1686
#, c-format
msgid "Unknown commit %s"
msgstr "ÐеизвеÑтный коммит %s"
@@ -6781,31 +7211,31 @@ msgstr "ДоÑтупные Ñтратегии:"
msgid "Available custom strategies are:"
msgstr "ДоÑтупные пользовательÑкие Ñтратегии:"
-#: builtin/merge.c:193
+#: builtin/merge.c:193 builtin/pull.c:119
msgid "do not show a diffstat at the end of the merge"
msgstr "не выводить ÑтатиÑтику изменений поÑле Ð¾ÐºÐ¾Ð½Ñ‡Ð°Ð½Ð¸Ñ ÑлиÑниÑ"
-#: builtin/merge.c:196
+#: builtin/merge.c:196 builtin/pull.c:122
msgid "show a diffstat at the end of the merge"
msgstr "вывеÑти ÑтатиÑтику изменений поÑле Ð¾ÐºÐ¾Ð½Ñ‡Ð°Ð½Ð¸Ñ ÑлиÑниÑ"
-#: builtin/merge.c:197
+#: builtin/merge.c:197 builtin/pull.c:125
msgid "(synonym to --stat)"
msgstr "(Ñиноним Ð´Ð»Ñ --stat)"
-#: builtin/merge.c:199
+#: builtin/merge.c:199 builtin/pull.c:128
msgid "add (at most <n>) entries from shortlog to merge commit message"
msgstr "добавить (макÑимум <n>) запиÑей из короткого журнала в Ñообщение коммита у ÑлиÑниÑ"
-#: builtin/merge.c:202
+#: builtin/merge.c:202 builtin/pull.c:131
msgid "create a single commit instead of doing a merge"
msgstr "Ñоздать один коммит, вмеÑто Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ ÑлиÑниÑ"
-#: builtin/merge.c:204
+#: builtin/merge.c:204 builtin/pull.c:134
msgid "perform a commit if the merge succeeds (default)"
msgstr "Ñделать коммит, еÑли ÑлиÑние прошло уÑпешно (по умолчанию)"
-#: builtin/merge.c:206
+#: builtin/merge.c:206 builtin/pull.c:137
msgid "edit message before committing"
msgstr "отредактировать Ñообщение перед выполнением коммита"
@@ -6813,7 +7243,7 @@ msgstr "отредактировать Ñообщение перед выполÐ
msgid "allow fast-forward (default)"
msgstr "разрешить перемотку вперед (по умолчанию)"
-#: builtin/merge.c:209
+#: builtin/merge.c:209 builtin/pull.c:143
msgid "abort if fast-forward is not possible"
msgstr "отменить выполнение ÑлиÑниÑ, еÑли перемотка вперед не возможна"
@@ -6821,19 +7251,20 @@ msgstr "отменить выполнение ÑлиÑниÑ, еÑли переÐ
msgid "Verify that the named commit has a valid GPG signature"
msgstr "Проверить, что указанный коммит имеет верную Ñлектронную подпиÑÑŒ GPG"
-#: builtin/merge.c:214 builtin/notes.c:753 builtin/revert.c:89
+#: builtin/merge.c:214 builtin/notes.c:767 builtin/pull.c:148
+#: builtin/revert.c:89
msgid "strategy"
msgstr "ÑтратегиÑ"
-#: builtin/merge.c:215
+#: builtin/merge.c:215 builtin/pull.c:149
msgid "merge strategy to use"
msgstr "иÑÐ¿Ð¾Ð»ÑŒÐ·ÑƒÐµÐ¼Ð°Ñ ÑÑ‚Ñ€Ð°Ñ‚ÐµÐ³Ð¸Ñ ÑлиÑниÑ"
-#: builtin/merge.c:216
+#: builtin/merge.c:216 builtin/pull.c:152
msgid "option=value"
msgstr "опциÑ=значение"
-#: builtin/merge.c:217
+#: builtin/merge.c:217 builtin/pull.c:153
msgid "option for selected merge strategy"
msgstr "опции Ð´Ð»Ñ Ð²Ñ‹Ð±Ñ€Ð°Ð½Ð½Ð¾Ð¹ Ñтратегии ÑлиÑниÑ"
@@ -6871,6 +7302,12 @@ msgstr " (нечего уплотнÑÑ‚ÑŒ)"
msgid "Squash commit -- not updating HEAD\n"
msgstr "Уплотнение коммита — не обновлÑÑ HEAD\n"
+#: builtin/merge.c:344 builtin/merge.c:763 builtin/merge.c:975
+#: builtin/merge.c:988
+#, c-format
+msgid "Could not write to '%s'"
+msgstr "Ðе удалоÑÑŒ запиÑать в «%s»"
+
#: builtin/merge.c:372
msgid "Writing SQUASH_MSG"
msgstr "ЗапиÑÑŒ SQUASH_MSG"
@@ -6894,10 +7331,6 @@ msgstr "«%s» не указывает на коммит"
msgid "Bad branch.%s.mergeoptions string: %s"
msgstr "ÐÐµÐ¿Ñ€Ð°Ð²Ð¸Ð»ÑŒÐ½Ð°Ñ Ñтрока branch.%s.mergeoptions: %s"
-#: builtin/merge.c:632
-msgid "git write-tree failed to write a tree"
-msgstr "git write-tree не удалоÑÑŒ запиÑать дерево"
-
#: builtin/merge.c:656
msgid "Not handling anything other than two heads merge."
msgstr "Ðе обрабатываю ничего, кроме ÑлиÑÐ½Ð¸Ñ Ð´Ð²ÑƒÑ… указателей на коммиты."
@@ -6983,10 +7416,6 @@ msgid ""
"Please, commit your changes before you merge."
msgstr "Ð’Ñ‹ не завершили ÑлиÑние (приÑутÑтвует файл MERGE_HEAD).\nВыполните коммит ваших изменений, перед ÑлиÑнием."
-#: builtin/merge.c:1227 git-pull.sh:74
-msgid "You have not concluded your merge (MERGE_HEAD exists)."
-msgstr "Ð’Ñ‹ не завершили ÑлиÑние (приÑутÑтвует файл MERGE_HEAD)."
-
#: builtin/merge.c:1231
msgid ""
"You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).\n"
@@ -7272,7 +7701,7 @@ msgstr "%s, откуда=%s, куда=%s"
msgid "Renaming %s to %s\n"
msgstr "Переименование %s в %s\n"
-#: builtin/mv.c:256 builtin/remote.c:725 builtin/repack.c:361
+#: builtin/mv.c:256 builtin/remote.c:722 builtin/repack.c:362
#, c-format
msgid "renaming '%s' failed"
msgstr "Ñбой при переименовании «%s»"
@@ -7317,332 +7746,329 @@ msgstr "разрешить вывод «undefined», еÑли не найденÐ
msgid "dereference tags in the input (internal use)"
msgstr "разыменовывать введенные метки (Ð´Ð»Ñ Ð²Ð½ÑƒÑ‚Ñ€ÐµÐ½Ð½ÐµÐ³Ð¾ иÑпользованиÑ)"
-#: builtin/notes.c:24
+#: builtin/notes.c:25
msgid "git notes [--ref <notes-ref>] [list [<object>]]"
msgstr "git notes [--ref <ÑÑылка-на-заметку>] [list [<объект>]]"
-#: builtin/notes.c:25
+#: builtin/notes.c:26
msgid ""
"git notes [--ref <notes-ref>] add [-f] [--allow-empty] [-m <msg> | -F <file>"
" | (-c | -C) <object>] [<object>]"
msgstr "git notes [--ref <ÑÑылка-на-заметку>] add [-f] [--allow-empty] [-m <Ñообщение> | -F <файл> | (-c | -C) <объект>] [<объект>]"
-#: builtin/notes.c:26
+#: builtin/notes.c:27
msgid "git notes [--ref <notes-ref>] copy [-f] <from-object> <to-object>"
msgstr "git notes [--ref <ÑÑылка-на-заметку>] copy [-f] <из-объекта> <в-объект>"
-#: builtin/notes.c:27
+#: builtin/notes.c:28
msgid ""
"git notes [--ref <notes-ref>] append [--allow-empty] [-m <msg> | -F <file> |"
" (-c | -C) <object>] [<object>]"
msgstr "git notes [--ref <ÑÑылка-на-заметку>] append [--allow-empty] [-m <Ñообщение> | -F <файл> | (-c | -C) <объект>] [<объект>]"
-#: builtin/notes.c:28
+#: builtin/notes.c:29
msgid "git notes [--ref <notes-ref>] edit [--allow-empty] [<object>]"
msgstr "git notes [--ref <ÑÑылка-на-заметку>] edit [--allow-empty] [<объект>]"
-#: builtin/notes.c:29
+#: builtin/notes.c:30
msgid "git notes [--ref <notes-ref>] show [<object>]"
msgstr "git notes [--ref <ÑÑылка-на-заметку>] show [<объект>]"
-#: builtin/notes.c:30
+#: builtin/notes.c:31
msgid ""
"git notes [--ref <notes-ref>] merge [-v | -q] [-s <strategy>] <notes-ref>"
msgstr "git notes [--ref <ÑÑылка-на-заметку>] merge [-v | -q] [-s <ÑтратегиÑ>] <ÑÑылка-на-заметку>"
-#: builtin/notes.c:31
+#: builtin/notes.c:32
msgid "git notes merge --commit [-v | -q]"
msgstr "git notes merge --commit [-v | -q]"
-#: builtin/notes.c:32
+#: builtin/notes.c:33
msgid "git notes merge --abort [-v | -q]"
msgstr "git notes merge --abort [-v | -q]"
-#: builtin/notes.c:33
+#: builtin/notes.c:34
msgid "git notes [--ref <notes-ref>] remove [<object>...]"
msgstr "git notes [--ref <ÑÑылка-на-заметку>] remove [<объект>…]"
-#: builtin/notes.c:34
+#: builtin/notes.c:35
msgid "git notes [--ref <notes-ref>] prune [-n | -v]"
msgstr "git notes [--ref <ÑÑылка-на-заметку>] prune [-n | -v]"
-#: builtin/notes.c:35
+#: builtin/notes.c:36
msgid "git notes [--ref <notes-ref>] get-ref"
msgstr "git notes [--ref <ÑÑылка-на-заметку>] get-ref"
-#: builtin/notes.c:40
+#: builtin/notes.c:41
msgid "git notes [list [<object>]]"
msgstr "git notes [list [<объект>]]"
-#: builtin/notes.c:45
+#: builtin/notes.c:46
msgid "git notes add [<options>] [<object>]"
msgstr "git notes add [<опции>] [<объект>]"
-#: builtin/notes.c:50
+#: builtin/notes.c:51
msgid "git notes copy [<options>] <from-object> <to-object>"
msgstr "git notes copy [<опции>] <из-объекта> <в-объект>"
-#: builtin/notes.c:51
+#: builtin/notes.c:52
msgid "git notes copy --stdin [<from-object> <to-object>]..."
msgstr "git notes copy --stdin [<из-объекта> <в-объект>]…"
-#: builtin/notes.c:56
+#: builtin/notes.c:57
msgid "git notes append [<options>] [<object>]"
msgstr "git notes append [<опции>] [<объект>]"
-#: builtin/notes.c:61
+#: builtin/notes.c:62
msgid "git notes edit [<object>]"
msgstr "git notes edit [<объект>]"
-#: builtin/notes.c:66
+#: builtin/notes.c:67
msgid "git notes show [<object>]"
msgstr "git notes show [<объект>]"
-#: builtin/notes.c:71
+#: builtin/notes.c:72
msgid "git notes merge [<options>] <notes-ref>"
msgstr "git notes merge [<опции>] <ÑÑылка-на-заметку>"
-#: builtin/notes.c:72
+#: builtin/notes.c:73
msgid "git notes merge --commit [<options>]"
msgstr "git notes merge --commit [<опции>]"
-#: builtin/notes.c:73
+#: builtin/notes.c:74
msgid "git notes merge --abort [<options>]"
msgstr "git notes merge --abort [<опции>]"
-#: builtin/notes.c:78
+#: builtin/notes.c:79
msgid "git notes remove [<object>]"
msgstr "git notes remove [<опции>]"
-#: builtin/notes.c:83
+#: builtin/notes.c:84
msgid "git notes prune [<options>]"
msgstr "git notes prune [<опции>]"
-#: builtin/notes.c:88
+#: builtin/notes.c:89
msgid "git notes get-ref"
msgstr "git notes get-ref"
-#: builtin/notes.c:146
+#: builtin/notes.c:147
#, c-format
msgid "unable to start 'show' for object '%s'"
msgstr "не удалоÑÑŒ запуÑтить «show» Ð´Ð»Ñ Ð¾Ð±ÑŠÐµÐºÑ‚Ð° «%s»"
-#: builtin/notes.c:150
+#: builtin/notes.c:151
msgid "could not read 'show' output"
msgstr "не удалоÑÑŒ прочитать вывод «show»"
-#: builtin/notes.c:158
+#: builtin/notes.c:159
#, c-format
msgid "failed to finish 'show' for object '%s'"
msgstr "не удалоÑÑŒ завершить «show» Ð´Ð»Ñ Ð¾Ð±ÑŠÐµÐºÑ‚Ð° «%s»"
-#: builtin/notes.c:173 builtin/tag.c:477
+#: builtin/notes.c:174 builtin/tag.c:477
#, c-format
msgid "could not create file '%s'"
msgstr "не удалоÑÑŒ Ñоздать файл «%s»"
-#: builtin/notes.c:192
+#: builtin/notes.c:193
msgid "Please supply the note contents using either -m or -F option"
msgstr "ПожалуйÑта, укажите Ñодержимое заметки, иÑÐ¿Ð¾Ð»ÑŒÐ·ÑƒÑ Ð¾Ð¿Ñ†Ð¸ÑŽ -m или -F"
-#: builtin/notes.c:201
+#: builtin/notes.c:202
msgid "unable to write note object"
msgstr "не удалоÑÑŒ запиÑать объект заметки"
-#: builtin/notes.c:203
+#: builtin/notes.c:204
#, c-format
msgid "The note contents have been left in %s"
msgstr "Содержимое заметки оÑталоÑÑŒ в %s"
-#: builtin/notes.c:231 builtin/tag.c:693
+#: builtin/notes.c:232 builtin/tag.c:695
#, c-format
msgid "cannot read '%s'"
msgstr "не удалоÑÑŒ прочитать «%s»"
-#: builtin/notes.c:233 builtin/tag.c:696
+#: builtin/notes.c:234 builtin/tag.c:698
#, c-format
msgid "could not open or read '%s'"
msgstr "не удалоÑÑŒ открыть или прочитать «%s»"
-#: builtin/notes.c:252 builtin/notes.c:303 builtin/notes.c:305
-#: builtin/notes.c:365 builtin/notes.c:420 builtin/notes.c:506
-#: builtin/notes.c:511 builtin/notes.c:589 builtin/notes.c:652
-#: builtin/notes.c:854 builtin/tag.c:709
+#: builtin/notes.c:253 builtin/notes.c:304 builtin/notes.c:306
+#: builtin/notes.c:366 builtin/notes.c:421 builtin/notes.c:507
+#: builtin/notes.c:512 builtin/notes.c:590 builtin/notes.c:653
+#: builtin/notes.c:877 builtin/tag.c:711
#, c-format
msgid "Failed to resolve '%s' as a valid ref."
msgstr "Ðе удалоÑÑŒ разрешить «%s» как ÑÑылку."
-#: builtin/notes.c:255
+#: builtin/notes.c:256
#, c-format
msgid "Failed to read object '%s'."
msgstr "Ðе удалоÑÑŒ прочитать объект «%s»."
-#: builtin/notes.c:259
+#: builtin/notes.c:260
#, c-format
msgid "Cannot read note data from non-blob object '%s'."
msgstr "Ðе удалоÑÑŒ прочитать данные заметки из недвоичного объекта «%s»."
-#: builtin/notes.c:299
-#, c-format
-msgid "Malformed input line: '%s'."
-msgstr "ÐŸÐ»Ð¾Ñ…Ð°Ñ Ñтрока ввода: «%s»."
-
-#: builtin/notes.c:314
-#, c-format
-msgid "Failed to copy notes from '%s' to '%s'"
-msgstr "Ðе удалоÑÑŒ Ñкопировать заметку из «%s» в «%s»"
-
-#: builtin/notes.c:358 builtin/notes.c:413 builtin/notes.c:489
-#: builtin/notes.c:501 builtin/notes.c:577 builtin/notes.c:645
-#: builtin/notes.c:919
+#: builtin/notes.c:359 builtin/notes.c:414 builtin/notes.c:490
+#: builtin/notes.c:502 builtin/notes.c:578 builtin/notes.c:646
+#: builtin/notes.c:942
msgid "too many parameters"
msgstr "передано Ñлишком много параметров"
-#: builtin/notes.c:371 builtin/notes.c:658
+#: builtin/notes.c:372 builtin/notes.c:659
#, c-format
msgid "No note found for object %s."
msgstr "Ðе найдена заметка Ð´Ð»Ñ Ð¾Ð±ÑŠÐµÐºÑ‚Ð° %s."
-#: builtin/notes.c:392 builtin/notes.c:555
+#: builtin/notes.c:393 builtin/notes.c:556
msgid "note contents as a string"
msgstr "текÑтовое Ñодержимое заметки"
-#: builtin/notes.c:395 builtin/notes.c:558
+#: builtin/notes.c:396 builtin/notes.c:559
msgid "note contents in a file"
msgstr "Ñодержимое заметки в файле"
-#: builtin/notes.c:397 builtin/notes.c:400 builtin/notes.c:560
-#: builtin/notes.c:563 builtin/tag.c:628
+#: builtin/notes.c:398 builtin/notes.c:401 builtin/notes.c:561
+#: builtin/notes.c:564 builtin/tag.c:630
msgid "object"
msgstr "объект"
-#: builtin/notes.c:398 builtin/notes.c:561
+#: builtin/notes.c:399 builtin/notes.c:562
msgid "reuse and edit specified note object"
msgstr "иÑпользовать и отредактировать указанный объект заметки"
-#: builtin/notes.c:401 builtin/notes.c:564
+#: builtin/notes.c:402 builtin/notes.c:565
msgid "reuse specified note object"
msgstr "иÑпользовать указанный объект заметки"
-#: builtin/notes.c:404 builtin/notes.c:567
+#: builtin/notes.c:405 builtin/notes.c:568
msgid "allow storing empty note"
msgstr "разрешить Ñохранение пуÑтой заметки"
-#: builtin/notes.c:405 builtin/notes.c:476
+#: builtin/notes.c:406 builtin/notes.c:477
msgid "replace existing notes"
msgstr "заменить ÑущеÑтвующие заметки"
-#: builtin/notes.c:430
+#: builtin/notes.c:431
#, c-format
msgid ""
"Cannot add notes. Found existing notes for object %s. Use '-f' to overwrite "
"existing notes"
msgstr "Ðе удалоÑÑŒ добавить заметку. Ðайдена ÑущеÑÑ‚Ð²ÑƒÑŽÑ‰Ð°Ñ Ð·Ð°Ð¼ÐµÑ‚ÐºÐ° у объекта %s. ИÑпользуйте параметр «-f» Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ·Ð°Ð¿Ð¸Ñи ÑущеÑтвующих заметок."
-#: builtin/notes.c:445 builtin/notes.c:524
+#: builtin/notes.c:446 builtin/notes.c:525
#, c-format
msgid "Overwriting existing notes for object %s\n"
msgstr "ПерезапиÑÑŒ ÑущеÑтвующих заметок у объекта %s\n"
-#: builtin/notes.c:456 builtin/notes.c:617 builtin/notes.c:859
+#: builtin/notes.c:457 builtin/notes.c:618 builtin/notes.c:882
#, c-format
msgid "Removing note for object %s\n"
msgstr "Удаление заметки у объекта %s\n"
-#: builtin/notes.c:477
+#: builtin/notes.c:478
msgid "read objects from stdin"
msgstr "прочитать объекты из Ñтандартного ввода"
-#: builtin/notes.c:479
+#: builtin/notes.c:480
msgid "load rewriting config for <command> (implies --stdin)"
msgstr "загрузить наÑтройки перезапиÑи Ð´Ð»Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ñ‹ <команда> (включает в ÑÐµÐ±Ñ --stdin)"
-#: builtin/notes.c:497
+#: builtin/notes.c:498
msgid "too few parameters"
msgstr "передано Ñлишком мало параметров"
-#: builtin/notes.c:518
+#: builtin/notes.c:519
#, c-format
msgid ""
"Cannot copy notes. Found existing notes for object %s. Use '-f' to overwrite"
" existing notes"
msgstr "Ðе удалоÑÑŒ Ñкопировать заметку. Ðайдена ÑущеÑÑ‚Ð²ÑƒÑŽÑ‰Ð°Ñ Ð·Ð°Ð¼ÐµÑ‚ÐºÐ° у объекта %s. ИÑпользуйте параметр «-f» Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ·Ð°Ð¿Ð¸Ñи ÑущеÑтвующих заметок."
-#: builtin/notes.c:530
+#: builtin/notes.c:531
#, c-format
msgid "Missing notes on source object %s. Cannot copy."
msgstr "Ðет заметок у иÑходного объекта %s. ÐÐµÐ»ÑŒÐ·Ñ Ñкопировать."
-#: builtin/notes.c:582
+#: builtin/notes.c:583
#, c-format
msgid ""
"The -m/-F/-c/-C options have been deprecated for the 'edit' subcommand.\n"
"Please use 'git notes add -f -m/-F/-c/-C' instead.\n"
msgstr "Опции -m/-F/-c/-C Ð´Ð»Ñ Ð¿Ð¾Ð´ÐºÐ¾Ð¼Ð°Ð½Ð´Ñ‹ «edit» уÑтарели.\nИÑпользуйте вмеÑто них «git notes add -f -m/-F/-c/-C».\n"
-#: builtin/notes.c:750
+#: builtin/notes.c:764
msgid "General options"
msgstr "Общие опции"
-#: builtin/notes.c:752
+#: builtin/notes.c:766
msgid "Merge options"
msgstr "Опции ÑлиÑниÑ"
-#: builtin/notes.c:754
+#: builtin/notes.c:768
msgid ""
"resolve notes conflicts using the given strategy "
"(manual/ours/theirs/union/cat_sort_uniq)"
msgstr "разрешить конфликты заметок Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ указанной Ñтратегии (manual/ours/theirs/union/cat_sort_uniq)"
-#: builtin/notes.c:756
+#: builtin/notes.c:770
msgid "Committing unmerged notes"
msgstr "Коммит не Ñлитых заметок"
-#: builtin/notes.c:758
+#: builtin/notes.c:772
msgid "finalize notes merge by committing unmerged notes"
msgstr "завершить ÑлиÑние заметок коммитом не Ñлитых заметок"
-#: builtin/notes.c:760
+#: builtin/notes.c:774
msgid "Aborting notes merge resolution"
msgstr "Отмена Ñ€Ð°Ð·Ñ€ÐµÑˆÐµÐ½Ð¸Ñ ÑлиÑÐ½Ð¸Ñ Ð·Ð°Ð¼ÐµÑ‚Ð¾Ðº"
-#: builtin/notes.c:762
+#: builtin/notes.c:776
msgid "abort notes merge"
msgstr "отменить ÑлиÑние заметок"
-#: builtin/notes.c:857
+#: builtin/notes.c:853
+#, c-format
+msgid "A notes merge into %s is already in-progress at %s"
+msgstr "СлиÑние заметок в %s уже выполнÑетÑÑ Ð½Ð° %s"
+
+#: builtin/notes.c:880
#, c-format
msgid "Object %s has no note\n"
msgstr "У объекта %s нет заметки\n"
-#: builtin/notes.c:869
+#: builtin/notes.c:892
msgid "attempt to remove non-existent note is not an error"
msgstr "попытка ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ Ð½ÐµÑущеÑтвующей заметки не ÑвлÑетÑÑ Ð¾ÑˆÐ¸Ð±ÐºÐ¾Ð¹"
-#: builtin/notes.c:872
+#: builtin/notes.c:895
msgid "read object names from the standard input"
msgstr "прочитать имена объектов из Ñтандартного ввода"
-#: builtin/notes.c:953
+#: builtin/notes.c:976
msgid "notes-ref"
msgstr "ÑÑылка-на-заметку"
-#: builtin/notes.c:954
+#: builtin/notes.c:977
msgid "use notes from <notes-ref>"
msgstr "иÑпользовать заметку из <ÑÑылка-на-заметку>"
-#: builtin/notes.c:989 builtin/remote.c:1618
+#: builtin/notes.c:1012 builtin/remote.c:1588
#, c-format
msgid "Unknown subcommand: %s"
msgstr "ÐеизвеÑÑ‚Ð½Ð°Ñ Ð¿Ð¾Ð´ÐºÐ¾Ð¼Ð°Ð½Ð´Ð°: %s"
#: builtin/pack-objects.c:28
-msgid "git pack-objects --stdout [options...] [< ref-list | < object-list]"
-msgstr "git pack-objects --stdout [опции…] [< ÑпиÑок-ÑÑылок | < ÑпиÑок-объектов]"
+msgid ""
+"git pack-objects --stdout [<options>...] [< <ref-list> | < <object-list>]"
+msgstr "git pack-objects --stdout [<опции>…] [< <ÑпиÑок-ÑÑылок> | < <ÑпиÑок-объектов>]"
#: builtin/pack-objects.c:29
-msgid "git pack-objects [options...] base-name [< ref-list | < object-list]"
-msgstr "git pack-objects [опции…] имÑ-базы [< ÑпиÑок-ÑÑылок | < ÑпиÑок-объектов]"
+msgid ""
+"git pack-objects [<options>...] <base-name> [< <ref-list> | < <object-list>]"
+msgstr "git pack-objects [<опции>…] <имÑ-базы> [< <ÑпиÑок-ÑÑылок> | < <ÑпиÑок-объектов>]"
#: builtin/pack-objects.c:175 builtin/pack-objects.c:178
#, c-format
@@ -7671,153 +8097,143 @@ msgstr "Ð½ÐµÐ¿Ð¾Ð´Ð´ÐµÑ€Ð¶Ð¸Ð²Ð°ÐµÐ¼Ð°Ñ Ð²ÐµÑ€ÑÐ¸Ñ Ð¸Ð½Ð´ÐµÐºÑа %s"
msgid "bad index version '%s'"
msgstr "Ð¿Ð»Ð¾Ñ…Ð°Ñ Ð²ÐµÑ€ÑÐ¸Ñ Ð¸Ð½Ð´ÐµÐºÑа «%s»"
-#: builtin/pack-objects.c:2595
-#, c-format
-msgid "option %s does not accept negative form"
-msgstr "Ð¾Ð¿Ñ†Ð¸Ñ %s не принимает отрицательные значениÑ"
-
-#: builtin/pack-objects.c:2599
-#, c-format
-msgid "unable to parse value '%s' for option %s"
-msgstr "не удалоÑÑŒ разобрать значение «%s» Ð´Ð»Ñ Ð¾Ð¿Ñ†Ð¸Ð¸ %s"
-
-#: builtin/pack-objects.c:2619
+#: builtin/pack-objects.c:2602
msgid "do not show progress meter"
msgstr "не выводить прогреÑÑ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ"
-#: builtin/pack-objects.c:2621
+#: builtin/pack-objects.c:2604
msgid "show progress meter"
msgstr "показать прогреÑÑ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ"
-#: builtin/pack-objects.c:2623
+#: builtin/pack-objects.c:2606
msgid "show progress meter during object writing phase"
msgstr "показать прогреÑÑ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ð²Ð¾ Ð²Ñ€ÐµÐ¼Ñ Ð·Ð°Ð¿Ð¸Ñи объектов"
-#: builtin/pack-objects.c:2626
+#: builtin/pack-objects.c:2609
msgid "similar to --all-progress when progress meter is shown"
msgstr "похоже на --all-progress при включенном прогреÑÑе выполнениÑ"
-#: builtin/pack-objects.c:2627
+#: builtin/pack-objects.c:2610
msgid "version[,offset]"
msgstr "верÑиÑ[,Ñмещение]"
-#: builtin/pack-objects.c:2628
+#: builtin/pack-objects.c:2611
msgid "write the pack index file in the specified idx format version"
msgstr "запиÑать файл индекÑа пакета в указанной верÑии формата"
-#: builtin/pack-objects.c:2631
+#: builtin/pack-objects.c:2614
msgid "maximum size of each output pack file"
msgstr "макÑимальный размер каждого выходного файла пакета"
-#: builtin/pack-objects.c:2633
+#: builtin/pack-objects.c:2616
msgid "ignore borrowed objects from alternate object store"
msgstr "игнорировать чужие объекты, взÑтые из альтернативного хранилища объектов"
-#: builtin/pack-objects.c:2635
+#: builtin/pack-objects.c:2618
msgid "ignore packed objects"
msgstr "игнорировать упакованные объекты"
-#: builtin/pack-objects.c:2637
+#: builtin/pack-objects.c:2620
msgid "limit pack window by objects"
msgstr "ограничить окно пакета по количеÑтву объектов"
-#: builtin/pack-objects.c:2639
+#: builtin/pack-objects.c:2622
msgid "limit pack window by memory in addition to object limit"
msgstr "дополнительно к количеÑтву объектов ограничить окно пакета по памÑти"
-#: builtin/pack-objects.c:2641
+#: builtin/pack-objects.c:2624
msgid "maximum length of delta chain allowed in the resulting pack"
msgstr "макÑÐ¸Ð¼Ð°Ð»ÑŒÐ½Ð°Ñ Ñ€Ð°Ð·Ñ€ÐµÑˆÐµÐ½Ð½Ð°Ñ Ð´Ð»Ð¸Ð½Ð° цепочки дельт в результирующем пакете"
-#: builtin/pack-objects.c:2643
+#: builtin/pack-objects.c:2626
msgid "reuse existing deltas"
msgstr "иÑпользовать повторно ÑущеÑтвующие дельты"
-#: builtin/pack-objects.c:2645
+#: builtin/pack-objects.c:2628
msgid "reuse existing objects"
msgstr "иÑпользовать повторно ÑущеÑтвующие объекты"
-#: builtin/pack-objects.c:2647
+#: builtin/pack-objects.c:2630
msgid "use OFS_DELTA objects"
msgstr "иÑпользовать объекты OFS_DELTA"
-#: builtin/pack-objects.c:2649
+#: builtin/pack-objects.c:2632
msgid "use threads when searching for best delta matches"
msgstr "иÑпользовать многопоточноÑÑ‚ÑŒ при поиÑке лучших Ñовпадений дельт"
-#: builtin/pack-objects.c:2651
+#: builtin/pack-objects.c:2634
msgid "do not create an empty pack output"
msgstr "не Ñоздавать пуÑтые выходные пакеты"
-#: builtin/pack-objects.c:2653
+#: builtin/pack-objects.c:2636
msgid "read revision arguments from standard input"
msgstr "прочитать аргументы редакций из Ñтандартного ввода"
-#: builtin/pack-objects.c:2655
+#: builtin/pack-objects.c:2638
msgid "limit the objects to those that are not yet packed"
msgstr "ограничитьÑÑ Ð¾Ð±ÑŠÐµÐºÑ‚Ð°Ð¼Ð¸, которые еще не упакованы"
-#: builtin/pack-objects.c:2658
+#: builtin/pack-objects.c:2641
msgid "include objects reachable from any reference"
msgstr "включить объекты, которые доÑтижимы по любой из ÑÑылок"
-#: builtin/pack-objects.c:2661
+#: builtin/pack-objects.c:2644
msgid "include objects referred by reflog entries"
msgstr "включить объекты, на которые ÑÑылаютÑÑ Ð·Ð°Ð¿Ð¸Ñи журнала ÑÑылок"
-#: builtin/pack-objects.c:2664
+#: builtin/pack-objects.c:2647
msgid "include objects referred to by the index"
msgstr "включить объекты, на которые ÑÑылаетÑÑ Ð¸Ð½Ð´ÐµÐºÑ"
-#: builtin/pack-objects.c:2667
+#: builtin/pack-objects.c:2650
msgid "output pack to stdout"
msgstr "вывеÑти пакет на Ñтандартный вывод"
-#: builtin/pack-objects.c:2669
+#: builtin/pack-objects.c:2652
msgid "include tag objects that refer to objects to be packed"
msgstr "включить объекты меток, которые ÑÑылаютÑÑ Ð½Ð° упаковываемые объекты"
-#: builtin/pack-objects.c:2671
+#: builtin/pack-objects.c:2654
msgid "keep unreachable objects"
msgstr "ÑохранÑÑ‚ÑŒ ÑÑылки на недоÑтупные объекты"
-#: builtin/pack-objects.c:2672 parse-options.h:139
+#: builtin/pack-objects.c:2655 parse-options.h:142
msgid "time"
msgstr "времÑ"
-#: builtin/pack-objects.c:2673
+#: builtin/pack-objects.c:2656
msgid "unpack unreachable objects newer than <time>"
msgstr "раÑпаковать недоÑтупные объекты, которые новее, чем <времÑ>"
-#: builtin/pack-objects.c:2676
+#: builtin/pack-objects.c:2659
msgid "create thin packs"
msgstr "Ñоздавать тонкие пакеты"
-#: builtin/pack-objects.c:2678
+#: builtin/pack-objects.c:2661
msgid "create packs suitable for shallow fetches"
msgstr "Ñоздавать пакеты, подходÑщие Ð´Ð»Ñ Ñ‡Ð°Ñтичных извлечений"
-#: builtin/pack-objects.c:2680
+#: builtin/pack-objects.c:2663
msgid "ignore packs that have companion .keep file"
msgstr "игнорировать пакеты, Ñ€Ñдом Ñ ÐºÐ¾Ñ‚Ð¾Ñ€Ñ‹Ð¼Ð¸ лежит .keep файл"
-#: builtin/pack-objects.c:2682
+#: builtin/pack-objects.c:2665
msgid "pack compression level"
msgstr "уровень ÑÐ¶Ð°Ñ‚Ð¸Ñ Ð¿Ð°ÐºÐµÑ‚Ð°"
-#: builtin/pack-objects.c:2684
+#: builtin/pack-objects.c:2667
msgid "do not hide commits by grafts"
msgstr "не Ñкрывать коммиты ÑращениÑми"
-#: builtin/pack-objects.c:2686
+#: builtin/pack-objects.c:2669
msgid "use a bitmap index if available to speed up counting objects"
msgstr "по возможноÑти иÑпользовать Ð¸Ð½Ð´ÐµÐºÑ Ð² битовых картах, Ð´Ð»Ñ ÑƒÑÐºÐ¾Ñ€ÐµÐ½Ð¸Ñ Ð¿Ð¾Ð´Ñчета объектов"
-#: builtin/pack-objects.c:2688
+#: builtin/pack-objects.c:2671
msgid "write a bitmap index together with the pack index"
msgstr "запиÑÑŒ индекÑа в битовых картах вмеÑте Ñ Ð¸Ð½Ð´ÐµÐºÑом пакета"
-#: builtin/pack-objects.c:2779
+#: builtin/pack-objects.c:2762
msgid "Counting objects"
msgstr "ПодÑчет объектов"
@@ -7845,37 +8261,169 @@ msgstr "Удаление дублирующихÑÑ Ð¾Ð±ÑŠÐµÐºÑ‚Ð¾Ð²"
msgid "git prune [-n] [-v] [--expire <time>] [--] [<head>...]"
msgstr "git prune [-n] [-v] [--expire <времÑ>] [--] [<имÑ-ветки>…]"
-#: builtin/prune.c:105 builtin/worktree.c:112
+#: builtin/prune.c:105 builtin/worktree.c:121
msgid "do not remove, show only"
msgstr "не удалÑÑ‚ÑŒ, только показать ÑпиÑок"
-#: builtin/prune.c:106 builtin/worktree.c:113
+#: builtin/prune.c:106 builtin/worktree.c:122
msgid "report pruned objects"
msgstr "вывеÑти ÑпиÑок удаленных объектов"
-#: builtin/prune.c:109 builtin/worktree.c:115
+#: builtin/prune.c:109 builtin/worktree.c:124
msgid "expire objects older than <time>"
msgstr "удалить объекты Ñтарее чем <дата-окончаниÑ>"
-#: builtin/push.c:14
+#: builtin/pull.c:69
+msgid "git pull [options] [<repository> [<refspec>...]]"
+msgstr "git pull [опции] [<репозиторий> [<ÑпецификациÑ-ÑÑылки>…]]"
+
+#: builtin/pull.c:113
+msgid "Options related to merging"
+msgstr "Опции, ÑвÑзанные Ñо ÑлиÑнием"
+
+#: builtin/pull.c:116
+msgid "incorporate changes by rebasing rather than merging"
+msgstr "забрать Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ перебазированиÑ, а не ÑлиÑниÑ"
+
+#: builtin/pull.c:140 builtin/revert.c:105
+msgid "allow fast-forward"
+msgstr "разрешить перемотку вперед"
+
+#: builtin/pull.c:146
+msgid "verify that the named commit has a valid GPG signature"
+msgstr "проверить, что указанный коммит имеет верную Ñлектронную подпиÑÑŒ GPG"
+
+#: builtin/pull.c:160
+msgid "Options related to fetching"
+msgstr "Опции, ÑвÑзанные Ñ Ð¸Ð·Ð²Ð»ÐµÑ‡ÐµÐ½Ð¸ÐµÐ¼"
+
+#: builtin/pull.c:268
+#, c-format
+msgid "Invalid value for pull.ff: %s"
+msgstr "Ðеправильное значение Ð´Ð»Ñ pull.ff: %s"
+
+#: builtin/pull.c:352
+msgid "Cannot pull with rebase: You have unstaged changes."
+msgstr "Ðе удалоÑÑŒ получить Ñ Ð¿ÐµÑ€ÐµÐ¼ÐµÑ‰ÐµÐ½Ð¸ÐµÐ¼: У Ð²Ð°Ñ ÐµÑÑ‚ÑŒ непроиндекÑированные изменениÑ."
+
+#: builtin/pull.c:358
+msgid "Additionally, your index contains uncommitted changes."
+msgstr "К тому же, в вашем индекÑе еÑÑ‚ÑŒ незакоммиченные изменениÑ."
+
+#: builtin/pull.c:360
+msgid "Cannot pull with rebase: Your index contains uncommitted changes."
+msgstr "Ðе удалоÑÑŒ получить Ñ Ð¿ÐµÑ€ÐµÐ¼ÐµÑ‰ÐµÐ½Ð¸ÐµÐ¼: Ð’ вашем индекÑе еÑÑ‚ÑŒ незакоммиченные изменениÑ."
+
+#: builtin/pull.c:436
+msgid ""
+"There is no candidate for rebasing against among the refs that you just "
+"fetched."
+msgstr "Ðет претендентов Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ¼ÐµÑ‰ÐµÐ½Ð¸Ñ Ñреди ÑÑылок, которые вы только что получили."
+
+#: builtin/pull.c:438
+msgid ""
+"There are no candidates for merging among the refs that you just fetched."
+msgstr "Ðет претендентов Ð´Ð»Ñ ÑлиÑÐ½Ð¸Ñ Ñреди ÑÑылок, которые вы только что получили."
+
+#: builtin/pull.c:439
+msgid ""
+"Generally this means that you provided a wildcard refspec which had no\n"
+"matches on the remote end."
+msgstr "Обычно Ñто означает, что вы передали Ñпецификацию ÑÑылки Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ шаблона и Ñтот шаблон не Ñовпал ни Ñ Ð¾Ð´Ð½Ð¾Ð¹ из ÑÑылок на внешнем репозитории."
+
+#: builtin/pull.c:442
+#, c-format
+msgid ""
+"You asked to pull from the remote '%s', but did not specify\n"
+"a branch. Because this is not the default configured remote\n"
+"for your current branch, you must specify a branch on the command line."
+msgstr "Ð’Ñ‹ попроÑили получить Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ñо внешнего Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ð¸Ñ Â«%s», но не указали ветку. Так как Ñто не репозиторий по умолчанию Ð´Ð»Ñ Ð²Ð°ÑˆÐµÐ¹ текущей ветки, вы должны указать ветку в командной Ñтроке."
+
+#: builtin/pull.c:447
+msgid "You are not currently on a branch."
+msgstr "Ð’Ñ‹ ÑÐµÐ¹Ñ‡Ð°Ñ Ð½Ð¸ на одной из веток."
+
+#: builtin/pull.c:449 builtin/pull.c:464
+msgid "Please specify which branch you want to rebase against."
+msgstr "ПожалуйÑта, укажите на какую ветку вы хотите перемеÑтить изменениÑ."
+
+#: builtin/pull.c:451 builtin/pull.c:466
+msgid "Please specify which branch you want to merge with."
+msgstr "ПожалуйÑта, укажите Ñ ÐºÐ°ÐºÐ¾Ð¹ веткой вы хотите Ñлить изменениÑ."
+
+#: builtin/pull.c:452 builtin/pull.c:467
+msgid "See git-pull(1) for details."
+msgstr "Ð”Ð»Ñ Ð´Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ð¹ информации Ñмотрите git-pull(1)."
+
+#: builtin/pull.c:462
+msgid "There is no tracking information for the current branch."
+msgstr "У текущей ветки нет информации об отÑлеживании."
+
+#: builtin/pull.c:471
+#, c-format
+msgid ""
+"If you wish to set tracking information for this branch you can do so with:\n"
+"\n"
+" git branch --set-upstream-to=%s/<branch> %s\n"
+msgstr "ЕÑли вы хотите указать информацию о отÑлеживаемой ветке, выполните:\n\n git branch --set-upstream-to=%s/<branch> %s\n"
+
+#: builtin/pull.c:476
+#, c-format
+msgid ""
+"Your configuration specifies to merge with the ref '%s'\n"
+"from the remote, but no such ref was fetched."
+msgstr "Ваша ÐºÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ñ ÑƒÐºÐ°Ð·Ñ‹Ð²Ð°ÐµÑ‚, что нужно Ñлить Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ñо ÑÑылкой\n«%s» из внешнего репозиториÑ, но Ñ‚Ð°ÐºÐ°Ñ ÑÑылка не была получена."
+
+#: builtin/pull.c:830
+msgid "Updating an unborn branch with changes added to the index."
+msgstr "Обновление еще не начавшейÑÑ Ð²ÐµÑ‚ÐºÐ¸ Ñ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñми, добавленными в индекÑ."
+
+#: builtin/pull.c:859
+#, c-format
+msgid ""
+"fetch updated the current branch head.\n"
+"fast-forwarding your working tree from\n"
+"commit %s."
+msgstr "извлечение обновило голову вашей текущей ветки.\nперемотка вашего рабочего каталога\nÑ ÐºÐ¾Ð¼Ð¼Ð¸Ñ‚Ð° %s."
+
+#: builtin/pull.c:864
+#, c-format
+msgid ""
+"Cannot fast-forward your working tree.\n"
+"After making sure that you saved anything precious from\n"
+"$ git diff %s\n"
+"output, run\n"
+"$ git reset --hard\n"
+"to recover."
+msgstr "Ðе удалоÑÑŒ перемотать вперёд Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð² вашем рабочем каталоге.\nПоÑле того, как вы убедитеÑÑŒ, что вы Ñохранили вÑÑ‘ необходимое из вывода\n$ git diff %s\n, запуÑтите\n$ git reset --hard\nÐ´Ð»Ñ Ð²Ð¾ÑÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð¸Ñходного ÑоÑтоÑниÑ."
+
+#: builtin/pull.c:879
+msgid "Cannot merge multiple branches into empty head."
+msgstr "ÐÐµÐ»ÑŒÐ·Ñ Ñлить неÑколько веток в пуÑтой указатель на коммит."
+
+#: builtin/pull.c:883
+msgid "Cannot rebase onto multiple branches."
+msgstr "Ðевозможно перемеÑтить над неÑколькими ветками."
+
+#: builtin/push.c:15
msgid "git push [<options>] [<repository> [<refspec>...]]"
msgstr "git push [<опции>] [<репозиторий> [<ÑпецификациÑ-ÑÑылки>…]]"
-#: builtin/push.c:85
+#: builtin/push.c:86
msgid "tag shorthand without <tag>"
msgstr "указано Ñокращение tag, но не указана Ñама <метка>"
-#: builtin/push.c:95
+#: builtin/push.c:96
msgid "--delete only accepts plain target ref names"
msgstr "Ð¾Ð¿Ñ†Ð¸Ñ --delete принимает только проÑтые целевые имена ÑÑылок"
-#: builtin/push.c:139
+#: builtin/push.c:140
msgid ""
"\n"
"To choose either option permanently, see push.default in 'git help config'."
msgstr "\nЧтобы выбрать любую из опций на поÑтоÑнной оÑнове, Ñмотрите push.default в «git help config»."
-#: builtin/push.c:142
+#: builtin/push.c:143
#, c-format
msgid ""
"The upstream branch of your current branch does not match\n"
@@ -7890,7 +8438,7 @@ msgid ""
"%s"
msgstr "Ð˜Ð¼Ñ Ð²Ñ‹ÑˆÐµÑтоÑщей ветки и вашей текущей ветки различаютÑÑ. Чтобы отправить Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð² вышеÑтоÑщую ветку на внешнем репозитории, иÑпользуйте:\n\n git push %s HEAD:%s\n\nЧтобы отправить Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð² ветку Ñ Ñ‚Ð°ÐºÐ¸Ð¼ же именем на внешнем репозитории, иÑпользуйте:\n\n git push %s %s\n%s"
-#: builtin/push.c:157
+#: builtin/push.c:158
#, c-format
msgid ""
"You are not currently on a branch.\n"
@@ -7900,7 +8448,7 @@ msgid ""
" git push %s HEAD:<name-of-remote-branch>\n"
msgstr "Ð’Ñ‹ ÑÐµÐ¹Ñ‡Ð°Ñ Ð½Ðµ находитеÑÑŒ ни на одной из веток.\nЧтобы отправить иÑторию, ведущую к текущему (отделенный HEAD) ÑоÑтоÑнию, иÑпользуйте\n\n git push %s HEAD:<имÑ-внешней-ветки>\n"
-#: builtin/push.c:171
+#: builtin/push.c:172
#, c-format
msgid ""
"The current branch %s has no upstream branch.\n"
@@ -7909,13 +8457,13 @@ msgid ""
" git push --set-upstream %s %s\n"
msgstr "Ð¢ÐµÐºÑƒÑ‰Ð°Ñ Ð²ÐµÑ‚ÐºÐ° %s не имеет вышеÑтоÑщей ветки.\nЧтобы отправить текущую ветку и уÑтановить внешнюю ветку как вышеÑтоÑщую Ð´Ð»Ñ Ñтой ветки, иÑпользуйте\n\n git push --set-upstream %s %s\n"
-#: builtin/push.c:179
+#: builtin/push.c:180
#, c-format
msgid ""
"The current branch %s has multiple upstream branches, refusing to push."
msgstr "Ваша Ñ‚ÐµÐºÑƒÑ‰Ð°Ñ Ð²ÐµÑ‚ÐºÐ° %s имеет неÑколько вышеÑтоÑщих веток, отказ в отправке изменений."
-#: builtin/push.c:182
+#: builtin/push.c:183
#, c-format
msgid ""
"You are pushing to remote '%s', which is not the upstream of\n"
@@ -7923,7 +8471,7 @@ msgid ""
"to update which remote branch."
msgstr "Ð’Ñ‹ ÑÐµÐ¹Ñ‡Ð°Ñ Ð¾Ñ‚Ð¿Ñ€Ð°Ð²Ð»Ñете Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð½Ð° внешний репозиторий «%s», который не ÑвлÑетÑÑ Ð²Ñ‹ÑˆÐµÑтоÑщим Ð´Ð»Ñ Ð²Ð°ÑˆÐµÐ¹ текущей ветки «%s», без ÑƒÐºÐ°Ð·Ð°Ð½Ð¸Ñ Ñ‚Ð¾Ð³Ð¾, что отправлÑÑ‚ÑŒ и в какую внешнюю ветку."
-#: builtin/push.c:205
+#: builtin/push.c:206
msgid ""
"push.default is unset; its implicit value has changed in\n"
"Git 2.0 from 'matching' to 'simple'. To squelch this message\n"
@@ -7947,11 +8495,11 @@ msgid ""
"'current' instead of 'simple' if you sometimes use older versions of Git)"
msgstr "push.default не уÑтановлен; его неÑвное значение было изменено в Git верÑии 2.0 Ñ Â«matching» на «simple». Чтобы прекратить вывод Ñтого ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð¸ Ñохранить Ñтарое поведение, иÑпользуйте:\n\n git config --global push.default matching\n\nЧтобы прекратить вывод Ñтого ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð¸ иÑпользовать новое поведение, иÑпользуйте:\n\n git config --global push.default simple\n\nКогда push.default уÑтановлено в «matching», git будет отправлÑÑ‚ÑŒ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð»Ð¾ÐºÐ°Ð»ÑŒÐ½Ñ‹Ñ… веток в ÑущеÑтвующие внешние ветки Ñ Ñ‚Ð°ÐºÐ¸Ð¼ же именем.\n\nÐÐ°Ñ‡Ð¸Ð½Ð°Ñ Ñ Git верÑии 2.0, по умолчанию иÑпользуетÑÑ Ð±Ð¾Ð»ÐµÐµ конÑервативное поведение «simple», которое отправлÑет Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ñ‚ÐµÐºÑƒÑ‰ÐµÐ¹ ветки в ÑоответÑтвующую внешнюю ветку, из которой «git pull» забирает изменениÑ.\n\nСмотрите «git help config» и ищите «push.default» Ð´Ð»Ñ Ð´Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ð¹ информации.\n(режим «simple» поÑвилÑÑ Ð² Git верÑии 1.7.11. ИÑпользуйте похожий режим «current» вмеÑто «simple», еÑли вы иногда иÑпользуете Ñтарые верÑии Git)"
-#: builtin/push.c:272
+#: builtin/push.c:273
msgid "You didn't specify any refspecs to push, and push.default is \"nothing\"."
msgstr "Ð’Ñ‹ не указали Ñпецификацию ÑÑылки Ð´Ð»Ñ Ð¾Ñ‚Ð¿Ñ€Ð°Ð²ÐºÐ¸, а push.default указан как \"nothing\"."
-#: builtin/push.c:279
+#: builtin/push.c:280
msgid ""
"Updates were rejected because the tip of your current branch is behind\n"
"its remote counterpart. Integrate the remote changes (e.g.\n"
@@ -7959,7 +8507,7 @@ msgid ""
"See the 'Note about fast-forwards' in 'git push --help' for details."
msgstr "ÐžÐ±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð±Ñ‹Ð»Ð¸ отклонены, так как верхушка вашей текущей ветки\nпозади ее внешней чаÑти. Заберите и Ñлейте внешние Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ \n(например, Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ «git pull …») перед повторной попыткой отправки\nизменений.\nÐ”Ð»Ñ Ð´Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ð¹ информации Ñмотрите «Note about fast-forwards»\nв «git push --help»."
-#: builtin/push.c:285
+#: builtin/push.c:286
msgid ""
"Updates were rejected because a pushed branch tip is behind its remote\n"
"counterpart. Check out this branch and integrate the remote changes\n"
@@ -7967,7 +8515,7 @@ msgid ""
"See the 'Note about fast-forwards' in 'git push --help' for details."
msgstr "ÐžÐ±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð±Ñ‹Ð»Ð¸ отклонены, так как верхушка отправлÑемой ветки\nпозади ее внешней чаÑти. ПереключитеÑÑŒ на ветку и заберите внешние\nÐ¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ (например, Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ «git pull …») перед повторной\nпопыткой отправки изменений.\nÐ”Ð»Ñ Ð´Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ð¹ информации Ñмотрите «Note about fast-forwards»\nв «git push --help»."
-#: builtin/push.c:291
+#: builtin/push.c:292
msgid ""
"Updates were rejected because the remote contains work that you do\n"
"not have locally. This is usually caused by another repository pushing\n"
@@ -7976,33 +8524,33 @@ msgid ""
"See the 'Note about fast-forwards' in 'git push --help' for details."
msgstr "ÐžÐ±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð±Ñ‹Ð»Ð¸ отклонены, так как внешний репозиторий Ñодержит\nизменениÑ, которых у Ð²Ð°Ñ Ð½ÐµÑ‚ в вашем локальном репозитории.\nОбычно, Ñто ÑвÑзанно Ñ Ñ‚ÐµÐ¼, что кто-то уже отправил Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð² \nто же меÑто. Перед повторной отправкой ваших изменений, вам нужно\nзабрать и Ñлить Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð¸Ð· внешнего Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ð¸Ñ Ñебе\n(например, Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ «git pull …»).\nÐ”Ð»Ñ Ð´Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ð¹ информации Ñмотрите «Note about fast-forwards»\nв «git push --help»."
-#: builtin/push.c:298
+#: builtin/push.c:299
msgid "Updates were rejected because the tag already exists in the remote."
msgstr "ÐžÐ±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð±Ñ‹Ð»Ð¸ отклонены, так как метка уже ÑущеÑтвует во внешнем репозитории."
-#: builtin/push.c:301
+#: builtin/push.c:302
msgid ""
"You cannot update a remote ref that points at a non-commit object,\n"
"or update a remote ref to make it point at a non-commit object,\n"
"without using the '--force' option.\n"
msgstr "Ð’Ñ‹ не можете обновить внешнюю ÑÑылку, ÐºÐ¾Ñ‚Ð¾Ñ€Ð°Ñ ÑƒÐºÐ°Ð·Ñ‹Ð²Ð°ÐµÑ‚ на объект, не ÑвлÑющийÑÑ ÐºÐ¾Ð¼Ð¼Ð¸Ñ‚Ð¾Ð¼ или обновить внешнюю ÑÑылку так, чтобы она указывала на объект, не ÑвлÑющийÑÑ ÐºÐ¾Ð¼Ð¼Ð¸Ñ‚Ð¾Ð¼, без ÑƒÐºÐ°Ð·Ð°Ð½Ð¸Ñ Ð¾Ð¿Ñ†Ð¸Ð¸ «--force».\n"
-#: builtin/push.c:360
+#: builtin/push.c:361
#, c-format
msgid "Pushing to %s\n"
msgstr "Отправка в %s\n"
-#: builtin/push.c:364
+#: builtin/push.c:365
#, c-format
msgid "failed to push some refs to '%s'"
msgstr "не удалоÑÑŒ отправить некоторые ÑÑылки в «%s»"
-#: builtin/push.c:394
+#: builtin/push.c:395
#, c-format
msgid "bad repository '%s'"
msgstr "плохой Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ð¸Ñ Â«%s»"
-#: builtin/push.c:395
+#: builtin/push.c:396
msgid ""
"No configured push destination.\n"
"Either specify the URL from the command-line or configure a remote repository using\n"
@@ -8014,108 +8562,109 @@ msgid ""
" git push <name>\n"
msgstr "Ðе наÑтроена точка Ð½Ð°Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ð¾Ñ‚Ð¿Ñ€Ð°Ð²ÐºÐ¸.\nЛибо укажите URL Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ коммандной Ñтроки, либо наÑтройте внешний репозиторий Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ\n\n git remote add <имÑ> <адреÑ>\n\nа затем отправьте Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ имени внешнего репозиториÑ\n\n git push <имÑ>\n"
-#: builtin/push.c:410
+#: builtin/push.c:411
msgid "--all and --tags are incompatible"
msgstr "--all и --tags Ð½ÐµÐ»ÑŒÐ·Ñ Ð¸Ñпользовать одновременно"
-#: builtin/push.c:411
+#: builtin/push.c:412
msgid "--all can't be combined with refspecs"
msgstr "--all Ð½ÐµÐ»ÑŒÐ·Ñ Ð¸Ñпользовать вмеÑте Ñо ÑпецификациÑми ÑÑылок"
-#: builtin/push.c:416
+#: builtin/push.c:417
msgid "--mirror and --tags are incompatible"
msgstr "--mirror и --tags Ð½ÐµÐ»ÑŒÐ·Ñ Ð¸Ñпользовать одновременно"
-#: builtin/push.c:417
+#: builtin/push.c:418
msgid "--mirror can't be combined with refspecs"
msgstr "--mirror Ð½ÐµÐ»ÑŒÐ·Ñ Ð¸Ñпользовать вмеÑте Ñо ÑпецификациÑми ÑÑылок"
-#: builtin/push.c:422
+#: builtin/push.c:423
msgid "--all and --mirror are incompatible"
msgstr "--all и --mirror Ð½ÐµÐ»ÑŒÐ·Ñ Ð¸Ñпользовать одновременно"
-#: builtin/push.c:502
+#: builtin/push.c:539
msgid "repository"
msgstr "репозиторий"
-#: builtin/push.c:503
+#: builtin/push.c:540 builtin/send-pack.c:161
msgid "push all refs"
msgstr "отправить вÑе ÑÑылки"
-#: builtin/push.c:504
+#: builtin/push.c:541 builtin/send-pack.c:163
msgid "mirror all refs"
msgstr "Ñделать зеркало вÑех ÑÑылок"
-#: builtin/push.c:506
+#: builtin/push.c:543
msgid "delete refs"
msgstr "удалить ÑÑылки"
-#: builtin/push.c:507
+#: builtin/push.c:544
msgid "push tags (can't be used with --all or --mirror)"
msgstr "отправить метки (Ð½ÐµÐ»ÑŒÐ·Ñ Ð¸Ñпользовать вмеÑте Ñ --all или --mirror)"
-#: builtin/push.c:510
+#: builtin/push.c:547 builtin/send-pack.c:164
msgid "force updates"
msgstr "принудительное обновление"
-#: builtin/push.c:512
+#: builtin/push.c:549 builtin/send-pack.c:175
msgid "refname>:<expect"
msgstr "имÑ-ÑÑылки>:<ожидаетÑÑ"
-#: builtin/push.c:513
+#: builtin/push.c:550 builtin/send-pack.c:176
msgid "require old value of ref to be at this value"
msgstr "требовать, чтобы Ñтарое значение ÑÑылки было ожидаемым"
-#: builtin/push.c:516
+#: builtin/push.c:553
msgid "control recursive pushing of submodules"
msgstr "управление рекурÑивной отправкой подмодулей"
-#: builtin/push.c:518
+#: builtin/push.c:555 builtin/send-pack.c:169
msgid "use thin pack"
msgstr "иÑпользовать тонкие пакеты"
-#: builtin/push.c:519 builtin/push.c:520
+#: builtin/push.c:556 builtin/push.c:557 builtin/send-pack.c:158
+#: builtin/send-pack.c:159
msgid "receive pack program"
msgstr "путь к программе упаковки на Ñервере"
-#: builtin/push.c:521
+#: builtin/push.c:558
msgid "set upstream for git pull/status"
msgstr "уÑтановить вышеÑтоÑщую ветку Ð´Ð»Ñ git pull/status"
-#: builtin/push.c:524
+#: builtin/push.c:561
msgid "prune locally removed refs"
msgstr "почиÑтить локально удаленные ÑÑылки"
-#: builtin/push.c:526
+#: builtin/push.c:563
msgid "bypass pre-push hook"
msgstr "пропуÑтить перехватчик перед-отправкой"
-#: builtin/push.c:527
+#: builtin/push.c:564
msgid "push missing but relevant tags"
msgstr "отправить пропущенные, но нужные метки"
-#: builtin/push.c:529
+#: builtin/push.c:567 builtin/send-pack.c:166
msgid "GPG sign the push"
msgstr "подпиÑать отправку Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ GPG"
-#: builtin/push.c:530
+#: builtin/push.c:569 builtin/send-pack.c:170
msgid "request atomic transaction on remote side"
msgstr "запроÑить выполнение атомарной транзакции на внешней Ñтороне"
-#: builtin/push.c:539
+#: builtin/push.c:579
msgid "--delete is incompatible with --all, --mirror and --tags"
msgstr "--delete неÑовмеÑтимо Ñ --all, --mirror и --tags"
-#: builtin/push.c:541
+#: builtin/push.c:581
msgid "--delete doesn't make sense without any refs"
msgstr "--delete не имеет ÑмыÑла без ÑƒÐºÐ°Ð·Ð°Ð½Ð¸Ñ ÑÑылок"
#: builtin/read-tree.c:37
msgid ""
-"git read-tree [[-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>]"
+"git read-tree [(-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>)"
" [-u [--exclude-per-directory=<gitignore>] | -i]] [--no-sparse-checkout] "
"[--index-output=<file>] (--empty | <tree-ish1> [<tree-ish2> [<tree-ish3>]])"
-msgstr "git read-tree [[-m [--trivial] [--aggressive] | --reset | --prefix=<префикÑ>] [-u [--exclude-per-directory=<gitignore>] | -i]] [--no-sparse-checkout] [--index-output=<файл>] (--empty | <указатель-дерева-1> [<указатель-дерева-2> [<указатель-дерева-3>]])"
+msgstr "git read-tree [(-m [--trivial] [--aggressive] | --reset | --prefix=<префикÑ>) [-u [--exclude-per-directory=<gitignore>] | -i]] [--no-sparse-checkout] [--index-output=<файл>] (--empty | <указатель-дерева-1> [<указатель-дерева-2> [<указатель-дерева-3>]])"
#: builtin/read-tree.c:110
msgid "write resulting index to <file>"
@@ -8181,12 +8730,12 @@ msgstr "пропуÑтить применение фильтра чаÑтичнÐ
msgid "debug unpack-trees"
msgstr "отладка unpack-trees"
-#: builtin/reflog.c:430
+#: builtin/reflog.c:432
#, c-format
-msgid "%s' for '%s' is not a valid timestamp"
+msgid "'%s' for '%s' is not a valid timestamp"
msgstr "«%s» Ð´Ð»Ñ Â«%s» не ÑвлÑетÑÑ Ð´Ð¾Ð¿ÑƒÑтимой меткой даты/времени"
-#: builtin/reflog.c:547 builtin/reflog.c:552
+#: builtin/reflog.c:549 builtin/reflog.c:554
#, c-format
msgid "'%s' is not a valid timestamp"
msgstr "«%s» не ÑвлÑетÑÑ Ð´Ð¾Ð¿ÑƒÑтимой меткой даты/времени"
@@ -8318,12 +8867,12 @@ msgstr "указание маÑтер ветки не имеет ÑмыÑла Ñ
msgid "specifying branches to track makes sense only with fetch mirrors"
msgstr "указание отÑлеживаемых веток имеет ÑмыÑл только при зеркальном извлечении"
-#: builtin/remote.c:187 builtin/remote.c:640
+#: builtin/remote.c:187 builtin/remote.c:637
#, c-format
msgid "remote %s already exists."
msgstr "внешний репозиторий %s уже ÑущеÑтвует"
-#: builtin/remote.c:191 builtin/remote.c:644
+#: builtin/remote.c:191 builtin/remote.c:641
#, c-format
msgid "'%s' is not a valid remote name"
msgstr "«%s» не ÑвлÑетÑÑ Ð´Ð¾Ð¿ÑƒÑтимым именем внешнего репозиториÑ."
@@ -8346,27 +8895,27 @@ msgstr "(ÑоответÑтвующаÑ)"
msgid "(delete)"
msgstr "(удаленнаÑ)"
-#: builtin/remote.c:589 builtin/remote.c:595 builtin/remote.c:601
+#: builtin/remote.c:588 builtin/remote.c:594 builtin/remote.c:600
#, c-format
msgid "Could not append '%s' to '%s'"
msgstr "Ðе удалоÑÑŒ добавить «%s» к «%s»"
-#: builtin/remote.c:633 builtin/remote.c:792 builtin/remote.c:892
+#: builtin/remote.c:630 builtin/remote.c:769 builtin/remote.c:869
#, c-format
msgid "No such remote: %s"
msgstr "Ðет такого внешнего репозиториÑ: %s"
-#: builtin/remote.c:650
+#: builtin/remote.c:647
#, c-format
msgid "Could not rename config section '%s' to '%s'"
msgstr "Ðе удалоÑÑŒ переименовать Ñекцию конфигурации Ñ Â«%s» на «%s»"
-#: builtin/remote.c:656 builtin/remote.c:844
+#: builtin/remote.c:653 builtin/remote.c:821
#, c-format
msgid "Could not remove config section '%s'"
msgstr "Ðе удалоÑÑŒ удалить Ñекцию файла конфигурации «%s»"
-#: builtin/remote.c:671
+#: builtin/remote.c:668
#, c-format
msgid ""
"Not updating non-default fetch refspec\n"
@@ -8374,32 +8923,27 @@ msgid ""
"\tPlease update the configuration manually if necessary."
msgstr "Ðе обновлÑÑŽ неÑтандартную Ñпецификацию ÑÑылки Ð´Ð»Ñ Ð¸Ð·Ð²Ð»ÐµÑ‡ÐµÐ½Ð¸Ñ\n\t%s\n\tПожалуйÑта, еÑли требуетÑÑ, обновите конфигурацию вручную."
-#: builtin/remote.c:677
+#: builtin/remote.c:674
#, c-format
msgid "Could not append '%s'"
msgstr "Ðе удалоÑÑŒ добавить «%s»"
-#: builtin/remote.c:688
+#: builtin/remote.c:685
#, c-format
msgid "Could not set '%s'"
msgstr "Ðе удалоÑÑŒ уÑтановить «%s»"
-#: builtin/remote.c:710
+#: builtin/remote.c:707
#, c-format
msgid "deleting '%s' failed"
msgstr "не удалоÑÑŒ удалить «%s»"
-#: builtin/remote.c:744
+#: builtin/remote.c:741
#, c-format
msgid "creating '%s' failed"
msgstr "не удалоÑÑŒ Ñоздать «%s»"
-#: builtin/remote.c:763
-#, c-format
-msgid "Could not remove branch %s"
-msgstr "Ðе удалоÑÑŒ удалить ветку «%s»"
-
-#: builtin/remote.c:830
+#: builtin/remote.c:807
msgid ""
"Note: A branch outside the refs/remotes/ hierarchy was not removed;\n"
"to delete it, use:"
@@ -8411,125 +8955,125 @@ msgstr[1] "Примечание: Ðекоторые ветки вне иерар
msgstr[2] "Примечание: Ðекоторые ветки вне иерархии refs/remotes/ не будут удалены;\nчтобы удалить их, иÑпользуйте:"
msgstr[3] "Примечание: Ðекоторые ветки вне иерархии refs/remotes/ не будут удалены;\nчтобы удалить их, иÑпользуйте:"
-#: builtin/remote.c:945
+#: builtin/remote.c:922
#, c-format
msgid " new (next fetch will store in remotes/%s)"
msgstr " Ð½Ð¾Ð²Ð°Ñ (Ñледующее извлечение Ñохранит ее в remotes/%s)"
-#: builtin/remote.c:948
+#: builtin/remote.c:925
msgid " tracked"
msgstr " отÑлеживаетÑÑ"
-#: builtin/remote.c:950
+#: builtin/remote.c:927
msgid " stale (use 'git remote prune' to remove)"
msgstr " недейÑтвительна (иÑпользуйте «git remote prune», чтобы удалить)"
-#: builtin/remote.c:952
+#: builtin/remote.c:929
msgid " ???"
msgstr " ???"
-#: builtin/remote.c:993
+#: builtin/remote.c:970
#, c-format
msgid "invalid branch.%s.merge; cannot rebase onto > 1 branch"
msgstr "неправильный параметр конфигурации branch.%s.merge; невозможно перемеÑтить более чем над 1 веткой"
-#: builtin/remote.c:1000
+#: builtin/remote.c:977
#, c-format
msgid "rebases onto remote %s"
msgstr "будет перемещена над внешней веткой %s"
-#: builtin/remote.c:1003
+#: builtin/remote.c:980
#, c-format
msgid " merges with remote %s"
msgstr " будет Ñлита Ñ Ð²Ð½ÐµÑˆÐ½ÐµÐ¹ веткой %s"
-#: builtin/remote.c:1004
+#: builtin/remote.c:981
msgid " and with remote"
msgstr " и Ñ Ð²Ð½ÐµÑˆÐ½ÐµÐ¹ веткой"
-#: builtin/remote.c:1006
+#: builtin/remote.c:983
#, c-format
msgid "merges with remote %s"
msgstr "будет Ñлита Ñ Ð²Ð½ÐµÑˆÐ½ÐµÐ¹ веткой %s"
-#: builtin/remote.c:1007
+#: builtin/remote.c:984
msgid " and with remote"
msgstr " и Ñ Ð²Ð½ÐµÑˆÐ½ÐµÐ¹ веткой"
-#: builtin/remote.c:1053
+#: builtin/remote.c:1030
msgid "create"
msgstr "Ñоздана"
-#: builtin/remote.c:1056
+#: builtin/remote.c:1033
msgid "delete"
msgstr "удалена"
-#: builtin/remote.c:1060
+#: builtin/remote.c:1037
msgid "up to date"
msgstr "уже актуальна"
-#: builtin/remote.c:1063
+#: builtin/remote.c:1040
msgid "fast-forwardable"
msgstr "возможна перемотка вперед"
-#: builtin/remote.c:1066
+#: builtin/remote.c:1043
msgid "local out of date"
msgstr "Ð»Ð¾ÐºÐ°Ð»ÑŒÐ½Ð°Ñ Ð²ÐµÑ‚ÐºÐ° уÑтарела"
-#: builtin/remote.c:1073
+#: builtin/remote.c:1050
#, c-format
msgid " %-*s forces to %-*s (%s)"
msgstr " %-*s будет принудительно отправлена в %-*s (%s)"
-#: builtin/remote.c:1076
+#: builtin/remote.c:1053
#, c-format
msgid " %-*s pushes to %-*s (%s)"
msgstr " %-*s будет отправлена в %-*s (%s)"
-#: builtin/remote.c:1080
+#: builtin/remote.c:1057
#, c-format
msgid " %-*s forces to %s"
msgstr " %-*s будет принудительно отправлена в %s"
-#: builtin/remote.c:1083
+#: builtin/remote.c:1060
#, c-format
msgid " %-*s pushes to %s"
msgstr " %-*s будет отправлена в %s"
-#: builtin/remote.c:1151
+#: builtin/remote.c:1128
msgid "do not query remotes"
msgstr "не опрашивать внешние репозитории"
-#: builtin/remote.c:1178
+#: builtin/remote.c:1155
#, c-format
msgid "* remote %s"
msgstr "* внешний репозиторий %s"
-#: builtin/remote.c:1179
+#: builtin/remote.c:1156
#, c-format
msgid " Fetch URL: %s"
msgstr " URL Ð´Ð»Ñ Ð¸Ð·Ð²Ð»ÐµÑ‡ÐµÐ½Ð¸Ñ: %s"
-#: builtin/remote.c:1180 builtin/remote.c:1331
+#: builtin/remote.c:1157 builtin/remote.c:1308
msgid "(no URL)"
msgstr "(нет URL)"
-#: builtin/remote.c:1189 builtin/remote.c:1191
+#: builtin/remote.c:1166 builtin/remote.c:1168
#, c-format
msgid " Push URL: %s"
msgstr " URL Ð´Ð»Ñ Ð¾Ñ‚Ð¿Ñ€Ð°Ð²ÐºÐ¸: %s"
-#: builtin/remote.c:1193 builtin/remote.c:1195 builtin/remote.c:1197
+#: builtin/remote.c:1170 builtin/remote.c:1172 builtin/remote.c:1174
#, c-format
msgid " HEAD branch: %s"
msgstr " HEAD ветка: %s"
-#: builtin/remote.c:1199
+#: builtin/remote.c:1176
#, c-format
msgid " HEAD branch (remote HEAD is ambiguous, may be one of the following):\n"
msgstr " HEAD ветка (HEAD внешнего Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ð¸Ñ Ð½ÐµÐ¾Ð´Ð½Ð¾Ð·Ð½Ð°Ñ‡Ð½Ñ‹Ð¹, может быть одним из):\n"
-#: builtin/remote.c:1211
+#: builtin/remote.c:1188
#, c-format
msgid " Remote branch:%s"
msgid_plural " Remote branches:%s"
@@ -8538,11 +9082,11 @@ msgstr[1] " Внешние ветки:%s"
msgstr[2] " Внешние ветки:%s"
msgstr[3] " Внешние ветки:%s"
-#: builtin/remote.c:1214 builtin/remote.c:1241
+#: builtin/remote.c:1191 builtin/remote.c:1218
msgid " (status not queried)"
msgstr " (ÑÑ‚Ð°Ñ‚ÑƒÑ Ð½Ðµ запрошен)"
-#: builtin/remote.c:1223
+#: builtin/remote.c:1200
msgid " Local branch configured for 'git pull':"
msgid_plural " Local branches configured for 'git pull':"
msgstr[0] " Ð›Ð¾ÐºÐ°Ð»ÑŒÐ½Ð°Ñ Ð²ÐµÑ‚ÐºÐ°, наÑÑ‚Ñ€Ð¾ÐµÐ½Ð½Ð°Ñ Ð´Ð»Ñ Â«git pull»:"
@@ -8550,11 +9094,11 @@ msgstr[1] " Локальные ветки, наÑтроенные Ð´Ð»Ñ Â«git
msgstr[2] " Локальные ветки, наÑтроенные Ð´Ð»Ñ Â«git pull»:"
msgstr[3] " Локальные ветки, наÑтроенные Ð´Ð»Ñ Â«git pull»:"
-#: builtin/remote.c:1231
+#: builtin/remote.c:1208
msgid " Local refs will be mirrored by 'git push'"
msgstr " Локальные ÑÑылки, зеркалируемые Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ «git push»"
-#: builtin/remote.c:1238
+#: builtin/remote.c:1215
#, c-format
msgid " Local ref configured for 'git push'%s:"
msgid_plural " Local refs configured for 'git push'%s:"
@@ -8563,115 +9107,115 @@ msgstr[1] " Локальные ÑÑылки, наÑтроенные Ð´Ð»Ñ Â«gi
msgstr[2] " Локальные ÑÑылки, наÑтроенные Ð´Ð»Ñ Â«git push»%s:"
msgstr[3] " Локальные ÑÑылки, наÑтроенные Ð´Ð»Ñ Â«git push»%s:"
-#: builtin/remote.c:1259
+#: builtin/remote.c:1236
msgid "set refs/remotes/<name>/HEAD according to remote"
msgstr "уÑтановить refs/remotes/<имÑ>/HEAD в завиÑимоÑти от внешнего репозиториÑ"
-#: builtin/remote.c:1261
+#: builtin/remote.c:1238
msgid "delete refs/remotes/<name>/HEAD"
msgstr "удалить refs/remotes/<имÑ>/HEAD"
-#: builtin/remote.c:1276
+#: builtin/remote.c:1253
msgid "Cannot determine remote HEAD"
msgstr "Ðе удалоÑÑŒ определить внешний HEAD"
-#: builtin/remote.c:1278
+#: builtin/remote.c:1255
msgid "Multiple remote HEAD branches. Please choose one explicitly with:"
msgstr "ÐеÑколько внешних HEAD веток. Укажите Ñвно одну из них:"
-#: builtin/remote.c:1288
+#: builtin/remote.c:1265
#, c-format
msgid "Could not delete %s"
msgstr "Ðе удалоÑÑŒ удалить %s"
-#: builtin/remote.c:1296
+#: builtin/remote.c:1273
#, c-format
msgid "Not a valid ref: %s"
msgstr "ÐÐµÐ¿Ñ€Ð°Ð²Ð¸Ð»ÑŒÐ½Ð°Ñ ÑÑылка: %s"
-#: builtin/remote.c:1298
+#: builtin/remote.c:1275
#, c-format
msgid "Could not setup %s"
msgstr "Ðе удалоÑÑŒ наÑтроить %s"
-#: builtin/remote.c:1316
+#: builtin/remote.c:1293
#, c-format
msgid " %s will become dangling!"
msgstr " %s будет виÑÑщей веткой!"
-#: builtin/remote.c:1317
+#: builtin/remote.c:1294
#, c-format
msgid " %s has become dangling!"
msgstr " %s Ñтала виÑÑщей веткой!"
-#: builtin/remote.c:1327
+#: builtin/remote.c:1304
#, c-format
msgid "Pruning %s"
msgstr "Удаление %s"
-#: builtin/remote.c:1328
+#: builtin/remote.c:1305
#, c-format
msgid "URL: %s"
msgstr "URL: %s"
-#: builtin/remote.c:1351
+#: builtin/remote.c:1321
#, c-format
msgid " * [would prune] %s"
msgstr " * [будет удалена] %s"
-#: builtin/remote.c:1354
+#: builtin/remote.c:1324
#, c-format
msgid " * [pruned] %s"
msgstr " * [удалена] %s"
-#: builtin/remote.c:1399
+#: builtin/remote.c:1369
msgid "prune remotes after fetching"
msgstr "почиÑтить внешние репозитории поÑле извлечениÑ"
-#: builtin/remote.c:1465 builtin/remote.c:1539
+#: builtin/remote.c:1435 builtin/remote.c:1509
#, c-format
msgid "No such remote '%s'"
msgstr "Ðет такого внешнего Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ð¸Ñ Â«%s»"
-#: builtin/remote.c:1485
+#: builtin/remote.c:1455
msgid "add branch"
msgstr "добавить ветку"
-#: builtin/remote.c:1492
+#: builtin/remote.c:1462
msgid "no remote specified"
msgstr "не указан внешний репозиторий"
-#: builtin/remote.c:1514
+#: builtin/remote.c:1484
msgid "manipulate push URLs"
msgstr "управление URL отправки"
-#: builtin/remote.c:1516
+#: builtin/remote.c:1486
msgid "add URL"
msgstr "добавить URL"
-#: builtin/remote.c:1518
+#: builtin/remote.c:1488
msgid "delete URLs"
msgstr "удалить URL"
-#: builtin/remote.c:1525
+#: builtin/remote.c:1495
msgid "--add --delete doesn't make sense"
msgstr "--add Ð½ÐµÐ»ÑŒÐ·Ñ Ð¸Ñпользовать одновременно Ñ --delete"
-#: builtin/remote.c:1565
+#: builtin/remote.c:1535
#, c-format
msgid "Invalid old URL pattern: %s"
msgstr "Ðеправильный шаблон Ñтарого URL: %s"
-#: builtin/remote.c:1573
+#: builtin/remote.c:1543
#, c-format
msgid "No such URL found: %s"
msgstr "Ðе найдены ÑÐ¾Ð²Ð¿Ð°Ð´ÐµÐ½Ð¸Ñ URL: %s"
-#: builtin/remote.c:1575
+#: builtin/remote.c:1545
msgid "Will not delete all non-push URLs"
msgstr "ÐÐµÐ»ÑŒÐ·Ñ ÑƒÐ´Ð°Ð»Ð¸Ñ‚ÑŒ вÑе URL не-отправки"
-#: builtin/remote.c:1589
+#: builtin/remote.c:1559
msgid "be verbose; must be placed before a subcommand"
msgstr "быть многоÑловнее; должно ÑтоÑÑ‚ÑŒ перед подкомандой"
@@ -8743,7 +9287,7 @@ msgstr "макÑимальный размер каждого из файлов Ð
msgid "repack objects in packs marked with .keep"
msgstr "переупаковать объекты в пакеты, помеченные файлом .keep"
-#: builtin/repack.c:377
+#: builtin/repack.c:378
#, c-format
msgid "removing '%s' failed"
msgstr "не удалоÑÑŒ удалить «%s»"
@@ -8925,11 +9469,6 @@ msgstr "запиÑать только факт того, что удаленны
msgid "Failed to resolve '%s' as a valid revision."
msgstr "Ðе удалоÑÑŒ раÑпознать «%s» как дейÑтвительную редакцию."
-#: builtin/reset.c:308 builtin/reset.c:316
-#, c-format
-msgid "Could not parse object '%s'."
-msgstr "Ðе удалоÑÑŒ разобрать объект «%s»."
-
#: builtin/reset.c:313
#, c-format
msgid "Failed to resolve '%s' as a valid tree."
@@ -8970,6 +9509,10 @@ msgstr "Ðе удалоÑÑŒ ÑброÑить файл индекÑа на ред
msgid "Could not write new index file."
msgstr "Ðе удалоÑÑŒ запиÑать новый файл индекÑа."
+#: builtin/rev-list.c:354
+msgid "rev-list does not support display of notes"
+msgstr "rev-list не поддерживает отображение заметок"
+
#: builtin/rev-parse.c:361
msgid "git rev-parse --parseopt [<options>] -- [<args>...]"
msgstr "git rev-parse --parseopt [<опции>] -- [<аргументы>…]"
@@ -8986,7 +9529,7 @@ msgstr "оÑтановить разбор поÑле первого аргуме
msgid "output in stuck long form"
msgstr "выводить аргументы в длинном формате"
-#: builtin/rev-parse.c:499
+#: builtin/rev-parse.c:502
msgid ""
"git rev-parse --parseopt [<options>] -- [<args>...]\n"
" or: git rev-parse --sq-quote [<arg>...]\n"
@@ -9056,10 +9599,6 @@ msgstr "Ð¾Ð¿Ñ†Ð¸Ñ Ð´Ð»Ñ Ñтратегии ÑлиÑниÑ"
msgid "append commit name"
msgstr "добавить Ð¸Ð¼Ñ ÐºÐ¾Ð¼Ð¼Ð¸Ñ‚Ð°"
-#: builtin/revert.c:105
-msgid "allow fast-forward"
-msgstr "разрешить перемотку вперед"
-
#: builtin/revert.c:106
msgid "preserve initially empty commits"
msgstr "Ñохранить изначально пуÑтые коммиты"
@@ -9180,6 +9719,28 @@ msgstr "не удалÑÑŽ рекурÑивно «%s» без ÑƒÐºÐ°Ð·Ð°Ð½Ð¸Ñ Ð¾
msgid "git rm: unable to remove %s"
msgstr "git rm: не удалоÑÑŒ удалить %s"
+#: builtin/send-pack.c:18
+msgid ""
+"git send-pack [--all | --mirror] [--dry-run] [--force] [--receive-pack=<git-receive-pack>] [--verbose] [--thin] [--atomic] [<host>:]<directory> [<ref>...]\n"
+" --all and explicit <ref> specification are mutually exclusive."
+msgstr "git send-pack [--all | --mirror] [--dry-run] [--force] [--receive-pack=<git-receive-pack>] [--verbose] [--thin] [--atomic] [<Ñервер>:]<каталог> [<ÑÑылка>…]\n --all и ÑÐ²Ð½Ð°Ñ ÑÐ¿ÐµÑ†Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ñ <ÑÑылки> взаимно иÑключающие."
+
+#: builtin/send-pack.c:160
+msgid "remote name"
+msgstr "Ð¸Ð¼Ñ Ð²Ð½ÐµÑˆÐ½ÐµÐ³Ð¾ репозиториÑ"
+
+#: builtin/send-pack.c:171
+msgid "use stateless RPC protocol"
+msgstr "протокол без ÑÐ¾Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ ÑоÑтоÑÐ½Ð¸Ñ Ð´Ð»Ñ RPC"
+
+#: builtin/send-pack.c:172
+msgid "read refs from stdin"
+msgstr "прочитать ÑÑылки из Ñтандартного ввода"
+
+#: builtin/send-pack.c:173
+msgid "print status from remote helper"
+msgstr "вывеÑти ÑÑ‚Ð°Ñ‚ÑƒÑ Ð¾Ñ‚ Ñкрипта внешнего Ñервера"
+
#: builtin/shortlog.c:13
msgid "git shortlog [<options>] [<revision-range>] [[--] [<path>...]]"
msgstr "git shortlog [<опции>] [<диапазон-редакций>] [[--] [<путь>…]]"
@@ -9292,8 +9853,8 @@ msgid ""
msgstr "git show-ref [-q | --quiet] [--verify] [--head] [-d | --dereference] [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags] [--heads] [--] [<шаблон>…]"
#: builtin/show-ref.c:11
-msgid "git show-ref --exclude-existing[=pattern] < ref-list"
-msgstr "git show-ref --exclude-existing[=шаблон] < ref-list"
+msgid "git show-ref --exclude-existing[=<pattern>] < <ref-list>"
+msgstr "git show-ref --exclude-existing[=<шаблон>] < <ÑпиÑок-ÑÑылок>"
#: builtin/show-ref.c:170
msgid "only show tags (can be combined with heads)"
@@ -9347,11 +9908,11 @@ msgstr "удалить Ñимвольные ÑÑылки"
msgid "shorten ref output"
msgstr "укороченный вывод ÑÑылок"
-#: builtin/symbolic-ref.c:43 builtin/update-ref.c:358
+#: builtin/symbolic-ref.c:43 builtin/update-ref.c:362
msgid "reason"
msgstr "причина"
-#: builtin/symbolic-ref.c:43 builtin/update-ref.c:358
+#: builtin/symbolic-ref.c:43 builtin/update-ref.c:362
msgid "reason of the update"
msgstr "причина обновлениÑ"
@@ -9462,110 +10023,114 @@ msgstr "при указании параметра «points-at» требуетÑ
msgid "malformed object name '%s'"
msgstr "Поврежденное Ð¸Ð¼Ñ Ð¾Ð±ÑŠÐµÐºÑ‚Ð° «%s»"
-#: builtin/tag.c:589
+#: builtin/tag.c:590
msgid "list tag names"
msgstr "ÑпиÑок названий меток"
-#: builtin/tag.c:591
+#: builtin/tag.c:592
msgid "print <n> lines of each tag message"
msgstr "печатать <n> Ñтрок опиÑÐ°Ð½Ð¸Ñ Ð¾Ñ‚ каждой метки"
-#: builtin/tag.c:593
+#: builtin/tag.c:594
msgid "delete tags"
msgstr "удалить метки"
-#: builtin/tag.c:594
+#: builtin/tag.c:595
msgid "verify tags"
msgstr "проверить метки"
-#: builtin/tag.c:596
+#: builtin/tag.c:597
msgid "Tag creation options"
msgstr "ÐаÑтройки ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ð¼ÐµÑ‚ÐºÐ¸"
-#: builtin/tag.c:598
+#: builtin/tag.c:599
msgid "annotated tag, needs a message"
msgstr "Ð´Ð»Ñ Ð°Ð½Ð½Ð¾Ñ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð½Ð¾Ð¹ метки нужно Ñообщение"
-#: builtin/tag.c:600
+#: builtin/tag.c:601
msgid "tag message"
msgstr "опиÑание метки"
-#: builtin/tag.c:602
+#: builtin/tag.c:603
msgid "annotated and GPG-signed tag"
msgstr "Ð°Ð½Ð½Ð¾Ñ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð½Ð°Ñ Ð¸ подпиÑÐ°Ð½Ð½Ð°Ñ Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ GPG метка"
-#: builtin/tag.c:606
+#: builtin/tag.c:607
msgid "use another key to sign the tag"
msgstr "иÑпользовать другой ключ Ð´Ð»Ñ Ð¿Ð¾Ð´Ð¿Ð¸ÑÐ°Ð½Ð¸Ñ Ð¼ÐµÑ‚ÐºÐ¸"
-#: builtin/tag.c:607
+#: builtin/tag.c:608
msgid "replace the tag if exists"
msgstr "замена метки, еÑли она ÑущеÑтвует"
-#: builtin/tag.c:609
+#: builtin/tag.c:609 builtin/update-ref.c:368
+msgid "create a reflog"
+msgstr "Ñоздать журнал ÑÑылок"
+
+#: builtin/tag.c:611
msgid "Tag listing options"
msgstr "ÐаÑтройки вывода ÑпиÑка меток"
-#: builtin/tag.c:610
+#: builtin/tag.c:612
msgid "show tag list in columns"
msgstr "показать ÑпиÑок меток по Ñтолбцам"
-#: builtin/tag.c:612
+#: builtin/tag.c:614
msgid "sort tags"
msgstr "отÑортировать метки"
-#: builtin/tag.c:617 builtin/tag.c:623
+#: builtin/tag.c:619 builtin/tag.c:625
msgid "print only tags that contain the commit"
msgstr "вывод только меток, которые Ñодержат коммит"
-#: builtin/tag.c:629
+#: builtin/tag.c:631
msgid "print only tags of the object"
msgstr "вывод только меток, определенного объекта"
-#: builtin/tag.c:655
+#: builtin/tag.c:657
msgid "--column and -n are incompatible"
msgstr "--column и -n Ð½ÐµÐ»ÑŒÐ·Ñ Ð¸Ñпользовать одновременно"
-#: builtin/tag.c:667
+#: builtin/tag.c:669
msgid "--sort and -n are incompatible"
msgstr "--sort и -n Ð½ÐµÐ»ÑŒÐ·Ñ Ð¸Ñпользовать одновременно"
-#: builtin/tag.c:674
+#: builtin/tag.c:676
msgid "-n option is only allowed with -l."
msgstr "опцию -n можно иÑпользовать только вмеÑте Ñ -l."
-#: builtin/tag.c:676
+#: builtin/tag.c:678
msgid "--contains option is only allowed with -l."
msgstr "опцию --contains можно иÑпользовать только вмеÑте Ñ -l."
-#: builtin/tag.c:678
+#: builtin/tag.c:680
msgid "--points-at option is only allowed with -l."
msgstr "опцию --points-at можно иÑпользовать только вмеÑте Ñ -l."
-#: builtin/tag.c:686
+#: builtin/tag.c:688
msgid "only one -F or -m option is allowed."
msgstr "-F и -m Ð½ÐµÐ»ÑŒÐ·Ñ Ð¸Ñпользовать одновременно."
-#: builtin/tag.c:706
+#: builtin/tag.c:708
msgid "too many params"
msgstr "передано Ñлишком много параметров"
-#: builtin/tag.c:712
+#: builtin/tag.c:714
#, c-format
msgid "'%s' is not a valid tag name."
msgstr "«%s» не ÑвлÑетÑÑ Ð´Ð¾Ð¿ÑƒÑтимым именем метки."
-#: builtin/tag.c:717
+#: builtin/tag.c:719
#, c-format
msgid "tag '%s' already exists"
msgstr "метка «%s» уже ÑущеÑтвует"
-#: builtin/tag.c:741
+#: builtin/tag.c:744
#, c-format
msgid "Updated tag '%s' (was %s)\n"
msgstr "Метка «%s» обновлена (была %s)\n"
-#: builtin/unpack-objects.c:489
+#: builtin/unpack-objects.c:490
msgid "Unpacking objects"
msgstr "РаÑпаковка объектов"
@@ -9763,19 +10328,19 @@ msgstr "git update-ref [<опции>] <имÑ-ÑÑылки> <новое-знÐ
msgid "git update-ref [<options>] --stdin [-z]"
msgstr "git update-ref [<опции>] --stdin [-z]"
-#: builtin/update-ref.c:359
+#: builtin/update-ref.c:363
msgid "delete the reference"
msgstr "удалить ÑÑылку"
-#: builtin/update-ref.c:361
+#: builtin/update-ref.c:365
msgid "update <refname> not the one it points to"
msgstr "обновить <имÑ-ÑÑылки> а не то, на что она указывает"
-#: builtin/update-ref.c:362
+#: builtin/update-ref.c:366
msgid "stdin has NUL-terminated arguments"
msgstr "ввод отделенный ÐУЛЕВЫМИ Ñимволами"
-#: builtin/update-ref.c:363
+#: builtin/update-ref.c:367
msgid "read updates from stdin"
msgstr "прочитать Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð¸Ð· Ñтандартного ввода"
@@ -9791,10 +10356,14 @@ msgstr "обновить информацию о Ñерверах Ñ Ð½ÑƒÐ»Ñ"
msgid "git verify-commit [-v | --verbose] <commit>..."
msgstr "git verify-commit [-v | --verbose] <коммит>…"
-#: builtin/verify-commit.c:75
+#: builtin/verify-commit.c:72
msgid "print commit contents"
msgstr "вывеÑти Ñодержимое коммита"
+#: builtin/verify-commit.c:73 builtin/verify-tag.c:84
+msgid "print raw gpg status output"
+msgstr "выводить Ñырой вывод ÑтатуÑа от gpg"
+
#: builtin/verify-pack.c:54
msgid "git verify-pack [-v | --verbose] [-s | --stat-only] <pack>..."
msgstr "git verify-pack [-v | --verbose] [-s | --stat-only] <пакет>…"
@@ -9811,86 +10380,82 @@ msgstr "вывеÑти только ÑтатиÑтику"
msgid "git verify-tag [-v | --verbose] <tag>..."
msgstr "git verify-tag [-v | --verbose] <метка>…"
-#: builtin/verify-tag.c:73
+#: builtin/verify-tag.c:83
msgid "print tag contents"
msgstr "вывеÑти Ñодержимое метки"
-#: builtin/worktree.c:11
+#: builtin/worktree.c:13
msgid "git worktree add [<options>] <path> <branch>"
msgstr "git worktree add [<опции>] <путь> <ветка>"
-#: builtin/worktree.c:12
+#: builtin/worktree.c:14
msgid "git worktree prune [<options>]"
msgstr "git worktree prune [<опции>]"
-#: builtin/worktree.c:27
+#: builtin/worktree.c:36
#, c-format
msgid "Removing worktrees/%s: not a valid directory"
msgstr "Удаление рабочих каталогов/%s: не ÑвлÑетÑÑ ÐºÐ°Ñ‚Ð°Ð»Ð¾Ð³Ð¾Ð¼"
-#: builtin/worktree.c:33
+#: builtin/worktree.c:42
#, c-format
msgid "Removing worktrees/%s: gitdir file does not exist"
msgstr "Удаление рабочих каталогов/%s: файл gitdir не ÑущеÑтвует"
-#: builtin/worktree.c:38
+#: builtin/worktree.c:47
#, c-format
msgid "Removing worktrees/%s: unable to read gitdir file (%s)"
msgstr "Удаление рабочих каталогов/%s: не удалоÑÑŒ прочитать файл gitdir (%s)"
-#: builtin/worktree.c:49
+#: builtin/worktree.c:58
#, c-format
msgid "Removing worktrees/%s: invalid gitdir file"
msgstr "Удаление рабочих каталогов/%s: недейÑтвительный файл gitdir"
-#: builtin/worktree.c:65
+#: builtin/worktree.c:74
#, c-format
msgid "Removing worktrees/%s: gitdir file points to non-existent location"
msgstr "Удаление рабочих каталогов/%s: gitdir указывает на неÑущеÑтвующее раÑположение"
-#: builtin/worktree.c:100
+#: builtin/worktree.c:109
#, c-format
msgid "failed to remove: %s"
msgstr "не удалоÑÑŒ удалить: %s"
-#: builtin/worktree.c:186
+#: builtin/worktree.c:198
#, c-format
msgid "'%s' already exists"
msgstr "«%s» уже ÑущеÑтвует"
-#: builtin/worktree.c:207
+#: builtin/worktree.c:232
#, c-format
msgid "could not create directory of '%s'"
msgstr "не удалоÑÑŒ Ñоздать каталог «%s»"
-#: builtin/worktree.c:241
-msgid "unable to resolve HEAD"
-msgstr "не удалоÑÑŒ определить HEAD"
-
-#: builtin/worktree.c:249
+#: builtin/worktree.c:268
#, c-format
-msgid "Enter %s (identifier %s)"
-msgstr "Вход в %s (идентификатор %s)"
+msgid "Preparing %s (identifier %s)"
+msgstr "Подготовка %s (идентификатор %s)"
-#: builtin/worktree.c:281
+#: builtin/worktree.c:316
msgid "checkout <branch> even if already checked out in other worktree"
msgstr "перейти на <ветка> даже еÑли она уже активна в другом рабочеÑм каталоге"
-#: builtin/worktree.c:283
+#: builtin/worktree.c:318
msgid "create a new branch"
msgstr "Ñоздать новую ветку"
-#: builtin/worktree.c:285
+#: builtin/worktree.c:320
msgid "create or reset a branch"
msgstr "Ñоздать или перейти на ветку"
-#: builtin/worktree.c:286
+#: builtin/worktree.c:321
msgid "detach HEAD at named commit"
msgstr "отÑоединить HEAD на указанном коммите"
-#: builtin/worktree.c:292
-msgid "-b and -B are mutually exclusive"
-msgstr "-b и -B Ð½ÐµÐ»ÑŒÐ·Ñ Ð¸Ñпользовать одновременно"
+#: builtin/worktree.c:328
+msgid "-b, -B, and --detach are mutually exclusive"
+msgstr "-b, -B и --detach Ð½ÐµÐ»ÑŒÐ·Ñ Ð¸Ñпользовать одновременно"
#: builtin/write-tree.c:13
msgid "git write-tree [--missing-ok] [--prefix=<prefix>/]"
@@ -9908,7 +10473,7 @@ msgstr "вывеÑти объект дерева Ð´Ð»Ñ Ð¿Ð¾Ð´ÐºÐ°Ñ‚Ð°Ð»Ð¾Ð³Ð° Ñ
msgid "only useful for debugging"
msgstr "иÑпользуетÑÑ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ при отладке"
-#: credential-cache--daemon.c:267
+#: credential-cache--daemon.c:255
msgid "print debugging messages to stderr"
msgstr "вывод отладочных Ñообщений на stderr"
@@ -9919,465 +10484,284 @@ msgid ""
"to read about a specific subcommand or concept."
msgstr "«git help -а» и «git help -g» выводит ÑпиÑок доÑтупных подкоманд и\nнекоторые руководÑтва по темам. ЗапуÑтите «git help <команда>» или\n«git help <термин>», чтобы прочеÑÑ‚ÑŒ о конкретных подкоманде или теме."
-#: common-cmds.h:10
+#: common-cmds.h:9
msgid "start a working area (see also: git help tutorial)"
msgstr "Ñоздать рабочую облаÑÑ‚ÑŒ (Ñмотрите также: git help tutorial)"
-#: common-cmds.h:11
+#: common-cmds.h:10
msgid "work on the current change (see also: git help everyday)"
msgstr "работа Ñ Ñ‚ÐµÐºÑƒÑ‰Ð¸Ð¼Ð¸ изменениÑми (Ñмотрите также: git help everyday)"
-#: common-cmds.h:12
+#: common-cmds.h:11
msgid "examine the history and state (see also: git help revisions)"
msgstr "проÑмотр иÑтории и текущего ÑоÑтоÑÐ½Ð¸Ñ (Ñмотрите также: git help revisions)"
-#: common-cmds.h:13
+#: common-cmds.h:12
msgid "grow, mark and tweak your common history"
msgstr "выращивание, отметка и наÑтройка вашей общей иÑтории"
-#: common-cmds.h:14
+#: common-cmds.h:13
msgid "collaborate (see also: git help workflows)"
msgstr "ÑовмеÑÑ‚Ð½Ð°Ñ Ñ€Ð°Ð±Ð¾Ñ‚Ð° (Ñмотрите также: git help workflows)"
-#: common-cmds.h:18
+#: common-cmds.h:17
msgid "Add file contents to the index"
msgstr "Добавление Ñодержимого файла в индекÑ"
-#: common-cmds.h:19
-msgid "Find by binary search the change that introduced a bug"
-msgstr "Двоичный поиÑк изменениÑ, которое вноÑит ошибку"
+#: common-cmds.h:18
+msgid "Use binary search to find the commit that introduced a bug"
+msgstr "ИÑпользовать двоичный поиÑк изменениÑ, которое вноÑит ошибку"
-#: common-cmds.h:20
+#: common-cmds.h:19
msgid "List, create, or delete branches"
msgstr "Вывод ÑпиÑка, Ñоздание или удаление веток"
-#: common-cmds.h:21
+#: common-cmds.h:20
msgid "Switch branches or restore working tree files"
msgstr "Переключение веток или воÑÑтановление файлов в рабочем каталоге"
-#: common-cmds.h:22
+#: common-cmds.h:21
msgid "Clone a repository into a new directory"
msgstr "Клонирование Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ð¸Ñ Ð² новый каталог"
-#: common-cmds.h:23
+#: common-cmds.h:22
msgid "Record changes to the repository"
msgstr "ЗапиÑÑŒ изменений в репозиторий"
-#: common-cmds.h:24
+#: common-cmds.h:23
msgid "Show changes between commits, commit and working tree, etc"
msgstr "Вывод разницы между коммитами, коммитом и рабочим каталогом и т.д."
-#: common-cmds.h:25
+#: common-cmds.h:24
msgid "Download objects and refs from another repository"
msgstr "Загрузка объектов и ÑÑылок из другого репозиториÑ"
-#: common-cmds.h:26
+#: common-cmds.h:25
msgid "Print lines matching a pattern"
msgstr "Вывод Ñтрок, ÑоответÑтвующих шаблону"
-#: common-cmds.h:27
+#: common-cmds.h:26
msgid "Create an empty Git repository or reinitialize an existing one"
msgstr "Создание пуÑтого Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ð¸Ñ Git или Ñ€ÐµÐ¸Ð½Ð¸Ñ†Ð¸Ð°Ð»Ð¸Ð·Ð°Ñ†Ð¸Ñ ÑущеÑтвующего"
-#: common-cmds.h:28
+#: common-cmds.h:27
msgid "Show commit logs"
msgstr "Вывод иÑтории коммитов"
-#: common-cmds.h:29
+#: common-cmds.h:28
msgid "Join two or more development histories together"
msgstr "Объединение одной или неÑкольких иÑторий разработки вмеÑте"
-#: common-cmds.h:30
+#: common-cmds.h:29
msgid "Move or rename a file, a directory, or a symlink"
msgstr "Перемещение или переименование файла, каталога или Ñимвольной ÑÑылки"
-#: common-cmds.h:31
+#: common-cmds.h:30
msgid "Fetch from and integrate with another repository or a local branch"
msgstr "Извлечение изменений и объединение Ñ Ð´Ñ€ÑƒÐ³Ð¸Ð¼ репозиторием или локальной веткой"
-#: common-cmds.h:32
+#: common-cmds.h:31
msgid "Update remote refs along with associated objects"
msgstr "Обновление внешних ÑÑылок и ÑвÑзанных объектов"
-#: common-cmds.h:33
+#: common-cmds.h:32
msgid "Forward-port local commits to the updated upstream head"
msgstr "Перемещение локальных коммитов над обновленной вышеÑтоÑщей веткой"
-#: common-cmds.h:34
+#: common-cmds.h:33
msgid "Reset current HEAD to the specified state"
msgstr "Ð¡Ð±Ñ€Ð¾Ñ Ñ‚ÐµÐºÑƒÑ‰ÐµÐ³Ð¾ ÑоÑтоÑÐ½Ð¸Ñ HEAD на указанное ÑоÑтоÑние"
-#: common-cmds.h:35
+#: common-cmds.h:34
msgid "Remove files from the working tree and from the index"
msgstr "Удаление файлов из рабочего каталога и индекÑа"
-#: common-cmds.h:36
+#: common-cmds.h:35
msgid "Show various types of objects"
msgstr "Вывод различных типов объектов"
-#: common-cmds.h:37
+#: common-cmds.h:36
msgid "Show the working tree status"
msgstr "Вывод ÑоÑтоÑÐ½Ð¸Ñ Ñ€Ð°Ð±Ð¾Ñ‡ÐµÐ³Ð¾ каталога"
-#: common-cmds.h:38
+#: common-cmds.h:37
msgid "Create, list, delete or verify a tag object signed with GPG"
msgstr "Создание, вывод ÑпиÑка, удаление или проверка метки, подпиÑанной Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ GPG"
-#: parse-options.h:142
+#: parse-options.h:145
msgid "expiry-date"
msgstr "дата-окончаниÑ"
-#: parse-options.h:157
+#: parse-options.h:160
msgid "no-op (backward compatibility)"
msgstr "ничего не делает (оÑтавлено Ð´Ð»Ñ Ð¾Ð±Ñ€Ð°Ñ‚Ð½Ð¾Ð¹ ÑовмеÑтимоÑти)"
-#: parse-options.h:231
+#: parse-options.h:236
msgid "be more verbose"
msgstr "быть многоÑловнее"
-#: parse-options.h:233
+#: parse-options.h:238
msgid "be more quiet"
msgstr "тихий режим"
-#: parse-options.h:239
+#: parse-options.h:244
msgid "use <n> digits to display SHA-1s"
msgstr "иÑпользовать <n> цифр Ð´Ð»Ñ Ð²Ñ‹Ð²Ð¾Ð´Ð° SHA-1"
-#: rerere.h:27
+#: rerere.h:28
msgid "update the index with reused conflict resolution if possible"
msgstr "обновить Ð¸Ð½Ð´ÐµÐºÑ Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ переиÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ñ€Ð°Ð·Ñ€ÐµÑˆÐµÐ½Ð¸Ñ ÐºÐ¾Ð½Ñ„Ð»Ð¸ÐºÑ‚Ð°, еÑли возможно"
-#: git-am.sh:53
-msgid "You need to set your committer info first"
-msgstr "Сначала нужно указать вашу информацию о коммитере"
-
-#: git-am.sh:100
-msgid ""
-"You seem to have moved HEAD since the last 'am' failure.\n"
-"Not rewinding to ORIG_HEAD"
-msgstr "Похоже, что вы перемеÑтили HEAD Ñ Ð¼Ð¾Ð¼ÐµÐ½Ñ‚Ð° поÑледней ошибки Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Â«am».\nПеремотка на ORIG_HEAD не выполнÑетÑÑ"
-
-#: git-am.sh:110
-#, sh-format
-msgid ""
-"When you have resolved this problem, run \"$cmdline --continue\".\n"
-"If you prefer to skip this patch, run \"$cmdline --skip\" instead.\n"
-"To restore the original branch and stop patching, run \"$cmdline --abort\"."
-msgstr "Когда вы разрешите Ñтот конфликт, запуÑтите «$cmdline --continue».\nЕÑли вы хотите пропуÑтить Ñтот патч, то запуÑтите «$cmdline --skip».\nЧтобы перейти на оригинальную ветку и оÑтановить применение изменений, запуÑтите «$cmdline --abort»."
-
-#: git-am.sh:126
-msgid "Cannot fall back to three-way merge."
-msgstr "Ðе удалоÑÑŒ откатитьÑÑ Ðº трехходовому ÑлиÑнию."
-
-#: git-am.sh:142
-msgid "Repository lacks necessary blobs to fall back on 3-way merge."
-msgstr "Ð’ репозитории отÑутÑтвуют двоичные объекты, необходимые Ð´Ð»Ñ Ð¾Ñ‚ÐºÐ°Ñ‚Ð° к трехходовому ÑлиÑнию."
-
-#: git-am.sh:144
-msgid "Using index info to reconstruct a base tree..."
-msgstr "ИÑпользую Ð¸Ð½Ð´ÐµÐºÑ Ð´Ð»Ñ Ñ€ÐµÐºÐ¾Ð½Ñтрукции базового дерева…"
-
-#: git-am.sh:159
-msgid ""
-"Did you hand edit your patch?\n"
-"It does not apply to blobs recorded in its index."
-msgstr "Ð’Ñ‹ вручную изменÑли патч?\nОн не накладываетÑÑ Ð±ÐµÐ· ошибок на двоичные объекты, запиÑанные в его заголовке."
-
-#: git-am.sh:168
-msgid "Falling back to patching base and 3-way merge..."
-msgstr "Откат к применению изменений к базовому коммиту Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ трехходового ÑлиÑниÑ…"
-
-#: git-am.sh:185
-msgid "Failed to merge in the changes."
-msgstr "Ðе удалоÑÑŒ Ñлить изменениÑ."
-
-#: git-am.sh:280
-msgid "Only one StGIT patch series can be applied at once"
-msgstr "Только ÑÐµÑ€Ð¸Ñ Ð¿Ð°Ñ‚Ñ‡ÐµÐ¹ StGIT может быть применена за раз"
-
-#: git-am.sh:367
-#, sh-format
-msgid "Patch format $patch_format is not supported."
-msgstr "Ðеподдерживаемый формат патча $patch_format."
-
-#: git-am.sh:369
-msgid "Patch format detection failed."
-msgstr "Сбой Ð¾Ð¿Ñ€ÐµÐ´ÐµÐ»ÐµÐ½Ð¸Ñ Ñ„Ð¾Ñ€Ð¼Ð°Ñ‚Ð° патча."
-
-#: git-am.sh:407
-msgid ""
-"The -b/--binary option has been a no-op for long time, and\n"
-"it will be removed. Please do not use it anymore."
-msgstr "ÐžÐ¿Ñ†Ð¸Ñ -b/--binary уже долгое Ð²Ñ€ÐµÐ¼Ñ Ð½Ð¸Ñ‡ÐµÐ³Ð¾ не делает и будет удалена Ñ Ñледующих верÑиÑÑ… Git. ПожалуйÑта, не иÑпользуйте ее."
-
-#: git-am.sh:507
-#, sh-format
-msgid "previous rebase directory $dotest still exists but mbox given."
-msgstr "предыдущий каталог Ð¿ÐµÑ€ÐµÐ¼ÐµÑ‰ÐµÐ½Ð¸Ñ $dotest еще ÑущеÑтвует, но передан mbox."
-
-#: git-am.sh:512
-msgid "Please make up your mind. --skip or --abort?"
-msgstr "ПожалуйÑта, определитеÑÑŒ: --skip или --abort?"
-
-#: git-am.sh:560
-#, sh-format
-msgid ""
-"Stray $dotest directory found.\n"
-"Use \"git am --abort\" to remove it."
-msgstr "Ðайден забытый каталог $dotest.\nИÑпользуйте «git am --abort», чтобы удалить его."
-
-#: git-am.sh:568
-msgid "Resolve operation not in progress, we are not resuming."
-msgstr "ÐžÐ¿ÐµÑ€Ð°Ñ†Ð¸Ñ Ñ€Ð°Ð·Ñ€ÐµÑˆÐµÐ½Ð¸Ñ ÐºÐ¾Ð½Ñ„Ð»Ð¸ÐºÑ‚Ð¾Ð² не в процеÑÑе выполнениÑ, не продолжаем."
-
-#: git-am.sh:635
-#, sh-format
-msgid "Dirty index: cannot apply patches (dirty: $files)"
-msgstr "Ð˜Ð½Ð´ÐµÐºÑ Ð½Ðµ пуÑтой: Ð½ÐµÐ»ÑŒÐ·Ñ Ð¿Ñ€Ð¸Ð¼ÐµÐ½ÑÑ‚ÑŒ патчи (в индекÑе: $files)"
-
-#: git-am.sh:747
-#, sh-format
-msgid ""
-"Patch is empty. Was it split wrong?\n"
-"If you would prefer to skip this patch, instead run \"$cmdline --skip\".\n"
-"To restore the original branch and stop patching run \"$cmdline --abort\"."
-msgstr "Патч пуÑÑ‚. Возможно, он был неправильно разделен?\nЕÑли вы хотите пропуÑтить Ñтот патч, то вмеÑто Ñтого запуÑтите «$cmdline --skip».\nЧтобы перейти на оригинальную ветку и оÑтановить применение изменений, запуÑтите «$cmdline --abort»."
-
-#: git-am.sh:774
-msgid "Patch does not have a valid e-mail address."
-msgstr "Патч не Ñодержит дейÑтвительный Ð°Ð´Ñ€ÐµÑ Ñлектронной почты."
-
-#: git-am.sh:821
-msgid "cannot be interactive without stdin connected to a terminal."
-msgstr "не удалоÑÑŒ иÑпользовать интерактивное поведение, без stdin подключенного к терминалу."
-
-#: git-am.sh:825
-msgid "Commit Body is:"
-msgstr "Тело коммита:"
-
-#. TRANSLATORS: Make sure to include [y], [n], [e], [v] and [a]
-#. in your translation. The program will only accept English
-#. input at this point.
-#: git-am.sh:832
-msgid "Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all "
-msgstr "Применить? [y] - да/[n] - нет/[e] - редактировать/[v] - проÑмотреть патч/[a] - применить вÑе "
-
-#: git-am.sh:868
-#, sh-format
-msgid "Applying: $FIRSTLINE"
-msgstr "Применение: $FIRSTLINE"
-
-#: git-am.sh:889
-msgid ""
-"No changes - did you forget to use 'git add'?\n"
-"If there is nothing left to stage, chances are that something else\n"
-"already introduced the same changes; you might want to skip this patch."
-msgstr "Ðет изменений — возможно, вы забыли вызвать «git add»?\nЕÑли ничего не оÑталоÑÑŒ Ð´Ð»Ñ Ð¸Ð½Ð´ÐµÐºÑации, то, Ñкорее вÑего, что-то другое уже Ñделало те же изменениÑ; возможно, вам Ñледует пропуÑтить Ñтот патч."
-
-#: git-am.sh:897
-msgid ""
-"You still have unmerged paths in your index\n"
-"did you forget to use 'git add'?"
-msgstr "У Ð²Ð°Ñ Ð²Ñе еще имеютÑÑ Ð½Ðµ Ñлитые пути в индекÑе.\nВозможно, вы забыли вызвать «git add»?"
-
-#: git-am.sh:913
-msgid "No changes -- Patch already applied."
-msgstr "Ðет изменений — Патч уже применен."
-
-#: git-am.sh:923
-#, sh-format
-msgid "Patch failed at $msgnum $FIRSTLINE"
-msgstr "Ошибка Ð¿Ñ€Ð¸Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ð¹ на Ñтроке $msgnum $FIRSTLINE"
-
-#: git-am.sh:926
-#, sh-format
-msgid ""
-"The copy of the patch that failed is found in:\n"
-" $dotest/patch"
-msgstr "Копию изменений, которые не удалоÑÑŒ применить, вы можете найти в:\n $dotest/patch"
-
-#: git-am.sh:945
-msgid "applying to an empty history"
-msgstr "применение к пуÑтой иÑтории"
-
-#: git-bisect.sh:48
+#: git-bisect.sh:50
msgid "You need to start by \"git bisect start\""
msgstr "Вам нужно начать Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ «git bisect start»"
#. TRANSLATORS: Make sure to include [Y] and [n] in your
#. translation. The program will only accept English input
#. at this point.
-#: git-bisect.sh:54
+#: git-bisect.sh:56
msgid "Do you want me to do it for you [Y/n]? "
msgstr "Ð’Ñ‹ уверены, что хотите, чтобы Ñ Ñделал Ñто [Y - да/n - нет]? "
-#: git-bisect.sh:95
+#: git-bisect.sh:99
#, sh-format
msgid "unrecognised option: '$arg'"
msgstr "Ð½ÐµÐ¾Ð¿Ð¾Ð·Ð½Ð°Ð½Ð½Ð°Ñ Ð¾Ð¿Ñ†Ð¸Ñ: «$arg»"
-#: git-bisect.sh:99
+#: git-bisect.sh:103
#, sh-format
msgid "'$arg' does not appear to be a valid revision"
msgstr "«$arg» не похоже на дейÑтвительную редакцию"
-#: git-bisect.sh:117
+#: git-bisect.sh:132
msgid "Bad HEAD - I need a HEAD"
msgstr "Плохой указатель HEAD — Ðеобходим указатель HEAD"
-#: git-bisect.sh:130
+#: git-bisect.sh:145
#, sh-format
msgid ""
"Checking out '$start_head' failed. Try 'git bisect reset <valid-branch>'."
msgstr "Сбой перехода на «$start_head». Попробуйте выполнить «git bisect reset <ÑущеÑтвующаÑ-ветка>»."
-#: git-bisect.sh:140
+#: git-bisect.sh:155
msgid "won't bisect on cg-seek'ed tree"
msgstr "Ð½ÐµÐ»ÑŒÐ·Ñ Ð²Ñ‹Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÑŒ двоичный поиÑк на дереве поÑле cg-seek"
-#: git-bisect.sh:144
+#: git-bisect.sh:159
msgid "Bad HEAD - strange symbolic ref"
msgstr "Плохой указатель HEAD — ÑÑ‚Ñ€Ð°Ð½Ð½Ð°Ñ ÑÐ¸Ð¼Ð²Ð¾Ð»ÑŒÐ½Ð°Ñ ÑÑылка"
-#: git-bisect.sh:189
+#: git-bisect.sh:211
#, sh-format
msgid "Bad bisect_write argument: $state"
msgstr "Плохой аргумент bisect_write: $state"
-#: git-bisect.sh:218
+#: git-bisect.sh:240
#, sh-format
msgid "Bad rev input: $arg"
msgstr "Плохой ввод номера редакции: $arg"
-#: git-bisect.sh:232
+#: git-bisect.sh:255
msgid "Please call 'bisect_state' with at least one argument."
msgstr "ПожалуйÑта, вызывайте «bisect_state» как минимум Ñ Ð¾Ð´Ð½Ð¸Ð¼ аргументом."
-#: git-bisect.sh:244
+#: git-bisect.sh:267
#, sh-format
msgid "Bad rev input: $rev"
msgstr "Плохой ввод номера редакции: $rev"
-#: git-bisect.sh:253
-msgid "'git bisect bad' can take only one argument."
-msgstr "«git bisect bad» может принимать только один аргумент."
-
#: git-bisect.sh:276
-msgid "Warning: bisecting only with a bad commit."
-msgstr "Предупреждение: попытка двоичного поиÑка Ñ ÑƒÐºÐ°Ð·Ð°Ð½Ð¸ÐµÐ¼ только плохого коммита."
+#, sh-format
+msgid "'git bisect $TERM_BAD' can take only one argument."
+msgstr "«git bisect $TERM_BAD» может принимать только один аргумент."
+
+#: git-bisect.sh:299
+#, sh-format
+msgid "Warning: bisecting only with a $TERM_BAD commit."
+msgstr "Предупреждение: попытка двоичного поиÑка Ñ ÑƒÐºÐ°Ð·Ð°Ð½Ð¸ÐµÐ¼ только $TERM_BAD коммита."
#. TRANSLATORS: Make sure to include [Y] and [n] in your
#. translation. The program will only accept English input
#. at this point.
-#: git-bisect.sh:282
+#: git-bisect.sh:305
msgid "Are you sure [Y/n]? "
msgstr "Вы уверены [Y - да/n - нет]? "
-#: git-bisect.sh:292
+#: git-bisect.sh:317
+#, sh-format
msgid ""
-"You need to give me at least one good and one bad revision.\n"
-"(You can use \"git bisect bad\" and \"git bisect good\" for that.)"
-msgstr "Вам нужно передать мне как минимум одну хорошую и одну плохую редакцию.\n(Ð”Ð»Ñ Ñтого вы можете иÑпользовать команды «git bisect bad» и «git bisect good».)"
+"You need to give me at least one $bad_syn and one $good_syn revision.\n"
+"(You can use \"git bisect $bad_syn\" and \"git bisect $good_syn\" for that.)"
+msgstr "Вам нужно передать мне как минимум одну $bad_syn и одну $good_syn редакцию.\n(Ð”Ð»Ñ Ñтого вы можете иÑпользовать команды «git bisect $bad_syn» и «git bisect $good_syn».)"
-#: git-bisect.sh:295
+#: git-bisect.sh:320
+#, sh-format
msgid ""
"You need to start by \"git bisect start\".\n"
-"You then need to give me at least one good and one bad revision.\n"
-"(You can use \"git bisect bad\" and \"git bisect good\" for that.)"
-msgstr "Ð”Ð»Ñ Ð½Ð°Ñ‡Ð°Ð»Ð° нужно запуÑтить «git bisect start».\nПоÑле Ñтого, вам нужно передать мне как минимум одну хорошую и одну плохую редакцию.\n(Ð”Ð»Ñ Ñтого вы можете иÑпользовать команды «git bisect bad» и «git bisect good».)"
+"You then need to give me at least one $good_syn and one $bad_syn revision.\n"
+"(You can use \"git bisect $bad_syn\" and \"git bisect $good_syn\" for that.)"
+msgstr "Ð”Ð»Ñ Ð½Ð°Ñ‡Ð°Ð»Ð° нужно запуÑтить «git bisect start».\nПоÑле Ñтого, вам нужно передать мне как минимум одну $good_syn и одну $bad_syn редакцию.\n(Ð”Ð»Ñ Ñтого вы можете иÑпользовать команды «git bisect $good_syn» и «git bisect $good_syn».)"
-#: git-bisect.sh:366 git-bisect.sh:493
+#: git-bisect.sh:391 git-bisect.sh:521
msgid "We are not bisecting."
msgstr "Ð’Ñ‹ ÑÐµÐ¹Ñ‡Ð°Ñ Ð½Ðµ в процеÑÑе бинарного поиÑка."
-#: git-bisect.sh:373
+#: git-bisect.sh:398
#, sh-format
msgid "'$invalid' is not a valid commit"
msgstr "«$invalid» не ÑвлÑетÑÑ Ð´ÐµÐ¹Ñтвительным коммитом"
-#: git-bisect.sh:382
+#: git-bisect.sh:407
#, sh-format
msgid ""
"Could not check out original HEAD '$branch'.\n"
"Try 'git bisect reset <commit>'."
msgstr "Ðе удалоÑÑŒ перейти на оригинальную ветку HEAD «$branch».\nПопробуйте запуÑтить «git bisect reset <коммит>»."
-#: git-bisect.sh:409
+#: git-bisect.sh:435
msgid "No logfile given"
msgstr "Ðе передан файл журнала"
-#: git-bisect.sh:410
+#: git-bisect.sh:436
#, sh-format
msgid "cannot read $file for replaying"
msgstr "не удалоÑÑŒ прочитать $file Ð´Ð»Ñ Ð¿Ð¾Ð²Ñ‚Ð¾Ñ€Ð° изменений"
-#: git-bisect.sh:427
+#: git-bisect.sh:455
msgid "?? what are you talking about?"
msgstr "?? вы о чем?"
-#: git-bisect.sh:439
+#: git-bisect.sh:467
#, sh-format
msgid "running $command"
msgstr "запуÑкаю $command"
-#: git-bisect.sh:446
+#: git-bisect.sh:474
#, sh-format
msgid ""
"bisect run failed:\n"
"exit code $res from '$command' is < 0 or >= 128"
msgstr "не удалоÑÑŒ выполнить двоичный поиÑк:\nкод Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð¸Ñ $res от «$command» оказалÑÑ < 0 или >= 128"
-#: git-bisect.sh:472
+#: git-bisect.sh:500
msgid "bisect run cannot continue any more"
msgstr "bisect run больше не может продолжать"
-#: git-bisect.sh:478
+#: git-bisect.sh:506
#, sh-format
msgid ""
"bisect run failed:\n"
"'bisect_state $state' exited with error code $res"
msgstr "не удалоÑÑŒ выполнить двоичный поиÑк:\n«bisect_state $state» завершилÑÑ Ñ ÐºÐ¾Ð´Ð¾Ð¼ ошибки $res"
-#: git-bisect.sh:485
+#: git-bisect.sh:513
msgid "bisect run success"
msgstr "bisect run выполнен уÑпешно"
-#: git-pull.sh:61
-msgid ""
-"Pull is not possible because you have unmerged files.\n"
-"Please, fix them up in the work tree, and then use 'git add/rm <file>'\n"
-"as appropriate to mark resolution and make a commit."
-msgstr "Ðевозможно выполнить получение, так как у Ð²Ð°Ñ Ð¸Ð¼ÐµÑŽÑ‚ÑÑ Ð½Ðµ Ñлитые файлы.\nИÑправьте их в рабочем каталоге, затем запуÑтите «git add/rm <файл>»,\nчтобы пометить иÑправление и Ñделайте коммит."
-
-#: git-pull.sh:65
-msgid "Pull is not possible because you have unmerged files."
-msgstr "Ðевозможно выполнить получение, так как у Ð²Ð°Ñ Ð¸Ð¼ÐµÑŽÑ‚ÑÑ Ð½Ðµ Ñлитые файлы."
-
-#: git-pull.sh:71
-msgid ""
-"You have not concluded your merge (MERGE_HEAD exists).\n"
-"Please, commit your changes before you can merge."
-msgstr "Ð’Ñ‹ не завершили ÑлиÑние (приÑутÑтвует файл MERGE_HEAD).\nВыполните коммит ваших изменений, перед ÑлиÑнием."
-
-#: git-pull.sh:285
-msgid "updating an unborn branch with changes added to the index"
-msgstr "обновление еще не начавшейÑÑ Ð²ÐµÑ‚ÐºÐ¸ Ñ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñми, добавленными в индекÑ"
-
-#: git-pull.sh:311
+#: git-bisect.sh:548
#, sh-format
-msgid ""
-"Warning: fetch updated the current branch head.\n"
-"Warning: fast-forwarding your working tree from\n"
-"Warning: commit $orig_head."
-msgstr "Предупреждение: извлечение обновило голову вашей текущей ветки.\nПредупреждение: перемотка вашего рабочего каталога\nПредупреждение: Ñ ÐºÐ¾Ð¼Ð¼Ð¸Ñ‚Ð° $orig_head."
-
-#: git-pull.sh:336
-msgid "Cannot merge multiple branches into empty head"
-msgstr "ÐÐµÐ»ÑŒÐ·Ñ Ñлить неÑколько веток в пуÑтую указатель на коммит"
-
-#: git-pull.sh:340
-msgid "Cannot rebase onto multiple branches"
-msgstr "Ðевозможно перемеÑтить над неÑколькими ветками"
+msgid "Invalid command: you're currently in a $TERM_BAD/$TERM_GOOD bisect."
+msgstr "ÐедопуÑÑ‚Ð¸Ð¼Ð°Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð°: вы ÑÐµÐ¹Ñ‡Ð°Ñ Ð½Ð°Ñ…Ð¾Ð´Ð¸Ñ‚ÐµÑÑŒ на $TERM_BAD/$TERM_GOOD двоичном поиÑке."
#: git-rebase.sh:57
msgid ""
@@ -10540,7 +10924,7 @@ msgstr "Ðе удалоÑÑŒ удалить временный Ð¸Ð½Ð´ÐµÐºÑ (не
msgid "Cannot record working tree state"
msgstr "Ðе удалоÑÑŒ запиÑать ÑоÑтоÑние рабочего каталога"
-#: git-stash.sh:191
+#: git-stash.sh:189
#, sh-format
msgid "Cannot update $ref_stash with $w_commit"
msgstr "Ðе удалоÑÑŒ обновить $ref_stash Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ $w_commit"
@@ -10554,97 +10938,97 @@ msgstr "Ðе удалоÑÑŒ обновить $ref_stash Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ $w_com
#. $ git stash save --blah-blah 2>&1 | head -n 2
#. error: unknown option for 'stash save': --blah-blah
#. To provide a message, use git stash save -- '--blah-blah'
-#: git-stash.sh:241
+#: git-stash.sh:239
#, sh-format
msgid ""
"error: unknown option for 'stash save': $option\n"
" To provide a message, use git stash save -- '$option'"
msgstr "ошибка: неизвеÑÑ‚Ð½Ð°Ñ Ð¾Ð¿Ñ†Ð¸Ñ Ð´Ð»Ñ Â«stash save»: $option\n Ð”Ð»Ñ Ð¿Ñ€ÐµÐ´Ð¾ÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ ÑообщениÑ, иÑпользуйте git stash save -- «$option»"
-#: git-stash.sh:262
+#: git-stash.sh:260
msgid "No local changes to save"
msgstr "Ðет локальных изменений Ð´Ð»Ñ ÑохранениÑ"
-#: git-stash.sh:266
+#: git-stash.sh:264
msgid "Cannot initialize stash"
msgstr "Ðе удалоÑÑŒ инициализировать ÑпрÑтанные изменениÑ"
-#: git-stash.sh:270
+#: git-stash.sh:268
msgid "Cannot save the current status"
msgstr "Ðе удалоÑÑŒ Ñохранить текущий ÑтатуÑ"
-#: git-stash.sh:288
+#: git-stash.sh:286
msgid "Cannot remove worktree changes"
msgstr "Ðе удалоÑÑŒ удалить Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ñ€Ð°Ð±Ð¾Ñ‡ÐµÐ³Ð¾ каталога"
-#: git-stash.sh:389
+#: git-stash.sh:387
#, sh-format
msgid "unknown option: $opt"
msgstr "неизвеÑÑ‚Ð½Ð°Ñ Ð¾Ð¿Ñ†Ð¸Ñ: $opt"
-#: git-stash.sh:399
+#: git-stash.sh:397
msgid "No stash found."
msgstr "Ðе найдены ÑпрÑтанные изменениÑ."
-#: git-stash.sh:406
+#: git-stash.sh:404
#, sh-format
msgid "Too many revisions specified: $REV"
msgstr "Передано Ñлишком много редакций: $REV"
-#: git-stash.sh:412
+#: git-stash.sh:410
#, sh-format
msgid "$reference is not a valid reference"
msgstr "$reference не ÑвлÑетÑÑ Ð´ÐµÐ¹Ñтвительной ÑÑылкой"
-#: git-stash.sh:440
+#: git-stash.sh:438
#, sh-format
msgid "'$args' is not a stash-like commit"
msgstr "«$args» не похоже на коммит Ñо ÑпрÑтанными изменениÑми"
-#: git-stash.sh:451
+#: git-stash.sh:449
#, sh-format
msgid "'$args' is not a stash reference"
msgstr "«$args» не ÑвлÑетÑÑ ÑÑылкой на ÑпрÑтанные изменениÑ"
-#: git-stash.sh:459
+#: git-stash.sh:457
msgid "unable to refresh index"
msgstr "не удалоÑÑŒ обновить индекÑ"
-#: git-stash.sh:463
+#: git-stash.sh:461
msgid "Cannot apply a stash in the middle of a merge"
msgstr "ÐÐµÐ»ÑŒÐ·Ñ Ð¿Ñ€Ð¸Ð¼ÐµÐ½Ð¸Ñ‚ÑŒ ÑпрÑтанные Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð²Ð¾ Ð²Ñ€ÐµÐ¼Ñ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ ÑлиÑниÑ"
-#: git-stash.sh:471
+#: git-stash.sh:469
msgid "Conflicts in index. Try without --index."
msgstr "Конфликты в индекÑе. Попробуйте без --index."
-#: git-stash.sh:473
+#: git-stash.sh:471
msgid "Could not save index tree"
msgstr "Ðе удалоÑÑŒ Ñохранить дерево индекÑа"
-#: git-stash.sh:507
+#: git-stash.sh:505
msgid "Cannot unstage modified files"
msgstr "Ðевозможно убрать из индекÑа измененные файлы"
-#: git-stash.sh:522
+#: git-stash.sh:520
msgid "Index was not unstashed."
msgstr "Ð˜Ð½Ð´ÐµÐºÑ Ð½Ðµ был доÑтат из ÑпрÑтанных изменений."
-#: git-stash.sh:545
+#: git-stash.sh:543
#, sh-format
msgid "Dropped ${REV} ($s)"
msgstr "Отброшено ${REV} ($s)"
-#: git-stash.sh:546
+#: git-stash.sh:544
#, sh-format
msgid "${REV}: Could not drop stash entry"
msgstr "${REV}: Ðе удалоÑÑŒ отброÑить запиÑÑŒ из ÑпрÑтанных изменений"
-#: git-stash.sh:554
+#: git-stash.sh:552
msgid "No branch name specified"
msgstr "Ðе указано Ð¸Ð¼Ñ Ð²ÐµÑ‚ÐºÐ¸"
-#: git-stash.sh:626
+#: git-stash.sh:624
msgid "(To restore them type \"git stash apply\")"
msgstr "(Чтобы воÑÑтановить их, наберите «git stash apply»)"
@@ -10872,7 +11256,7 @@ msgstr "Подмодуль по пути «$displaypath»: Ñлито Ñ Â«$sha1
#: git-submodule.sh:907
#, sh-format
msgid ""
-"Execution of '$command $sha1' failed in submodule path '$prefix$sm_path'"
+"Execution of '$command $sha1' failed in submodule path '$prefix$sm_path'"
msgstr "Сбой Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Â«$command $sha1» Ð´Ð»Ñ Ð¿Ð¾Ð´Ð¼Ð¾Ð´ÑƒÐ»Ñ Ð¿Ð¾ пути «$prefix$sm_path»"
#: git-submodule.sh:908
diff --git a/progress.c b/progress.c
index 2e31bec60f..353bd37416 100644
--- a/progress.c
+++ b/progress.c
@@ -25,7 +25,7 @@ struct throughput {
unsigned int last_bytes[TP_IDX_MAX];
unsigned int last_misecs[TP_IDX_MAX];
unsigned int idx;
- char display[32];
+ struct strbuf display;
};
struct progress {
@@ -98,7 +98,7 @@ static int display(struct progress *progress, unsigned n, const char *done)
}
progress->last_value = n;
- tp = (progress->throughput) ? progress->throughput->display : "";
+ tp = (progress->throughput) ? progress->throughput->display.buf : "";
eol = done ? done : " \r";
if (progress->total) {
unsigned percent = n * 100 / progress->total;
@@ -129,6 +129,7 @@ static int display(struct progress *progress, unsigned n, const char *done)
static void throughput_string(struct strbuf *buf, off_t total,
unsigned int rate)
{
+ strbuf_reset(buf);
strbuf_addstr(buf, ", ");
strbuf_humanise_bytes(buf, total);
strbuf_addstr(buf, " | ");
@@ -141,7 +142,6 @@ void display_throughput(struct progress *progress, off_t total)
struct throughput *tp;
uint64_t now_ns;
unsigned int misecs, count, rate;
- struct strbuf buf = STRBUF_INIT;
if (!progress)
return;
@@ -154,6 +154,7 @@ void display_throughput(struct progress *progress, off_t total)
if (tp) {
tp->prev_total = tp->curr_total = total;
tp->prev_ns = now_ns;
+ strbuf_init(&tp->display, 0);
}
return;
}
@@ -193,9 +194,7 @@ void display_throughput(struct progress *progress, off_t total)
tp->last_misecs[tp->idx] = misecs;
tp->idx = (tp->idx + 1) % TP_IDX_MAX;
- throughput_string(&buf, total, rate);
- strncpy(tp->display, buf.buf, sizeof(tp->display));
- strbuf_release(&buf);
+ throughput_string(&tp->display, total, rate);
if (progress->last_value != -1 && progress_update)
display(progress, progress->last_value, NULL);
}
@@ -250,20 +249,19 @@ void stop_progress_msg(struct progress **p_progress, const char *msg)
bufp = (len < sizeof(buf)) ? buf : xmalloc(len + 1);
if (tp) {
- struct strbuf strbuf = STRBUF_INIT;
unsigned int rate = !tp->avg_misecs ? 0 :
tp->avg_bytes / tp->avg_misecs;
- throughput_string(&strbuf, tp->curr_total, rate);
- strncpy(tp->display, strbuf.buf, sizeof(tp->display));
- strbuf_release(&strbuf);
+ throughput_string(&tp->display, tp->curr_total, rate);
}
progress_update = 1;
- sprintf(bufp, ", %s.\n", msg);
+ xsnprintf(bufp, len + 1, ", %s.\n", msg);
display(progress, progress->last_value, bufp);
if (buf != bufp)
free(bufp);
}
clear_progress_signal();
+ if (progress->throughput)
+ strbuf_release(&progress->throughput->display);
free(progress->throughput);
free(progress);
}
diff --git a/reachable.c b/reachable.c
index 9cff25b490..43616d49c7 100644
--- a/reachable.c
+++ b/reachable.c
@@ -25,9 +25,15 @@ static void update_progress(struct connectivity_progress *cp)
static int add_one_ref(const char *path, const struct object_id *oid,
int flag, void *cb_data)
{
- struct object *object = parse_object_or_die(oid->hash, path);
struct rev_info *revs = (struct rev_info *)cb_data;
+ struct object *object;
+ if ((flag & REF_ISSYMREF) && (flag & REF_ISBROKEN)) {
+ warning("symbolic ref is dangling: %s", path);
+ return 0;
+ }
+
+ object = parse_object_or_die(oid->hash, path);
add_pending_object(revs, object, "");
return 0;
diff --git a/ref-filter.c b/ref-filter.c
index f38dee4f60..1194f10ed6 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -9,6 +9,10 @@
#include "tag.h"
#include "quote.h"
#include "ref-filter.h"
+#include "revision.h"
+#include "utf8.h"
+#include "git-compat-util.h"
+#include "version.h"
typedef enum { FIELD_STR, FIELD_ULONG, FIELD_TIME } cmp_type;
@@ -43,15 +47,48 @@ static struct {
{ "subject" },
{ "body" },
{ "contents" },
- { "contents:subject" },
- { "contents:body" },
- { "contents:signature" },
{ "upstream" },
{ "push" },
{ "symref" },
{ "flag" },
{ "HEAD" },
{ "color" },
+ { "align" },
+ { "end" },
+};
+
+#define REF_FORMATTING_STATE_INIT { 0, NULL }
+
+struct align {
+ align_type position;
+ unsigned int width;
+};
+
+struct contents {
+ unsigned int lines;
+ struct object_id oid;
+};
+
+struct ref_formatting_stack {
+ struct ref_formatting_stack *prev;
+ struct strbuf output;
+ void (*at_end)(struct ref_formatting_stack *stack);
+ void *at_end_data;
+};
+
+struct ref_formatting_state {
+ int quote_style;
+ struct ref_formatting_stack *stack;
+};
+
+struct atom_value {
+ const char *s;
+ union {
+ struct align align;
+ struct contents contents;
+ } u;
+ void (*handler)(struct atom_value *atomv, struct ref_formatting_state *state);
+ unsigned long ul; /* used for sorting when not FIELD_STR */
};
/*
@@ -123,6 +160,120 @@ int parse_ref_filter_atom(const char *atom, const char *ep)
return at;
}
+static void quote_formatting(struct strbuf *s, const char *str, int quote_style)
+{
+ switch (quote_style) {
+ case QUOTE_NONE:
+ strbuf_addstr(s, str);
+ break;
+ case QUOTE_SHELL:
+ sq_quote_buf(s, str);
+ break;
+ case QUOTE_PERL:
+ perl_quote_buf(s, str);
+ break;
+ case QUOTE_PYTHON:
+ python_quote_buf(s, str);
+ break;
+ case QUOTE_TCL:
+ tcl_quote_buf(s, str);
+ break;
+ }
+}
+
+static void append_atom(struct atom_value *v, struct ref_formatting_state *state)
+{
+ /*
+ * Quote formatting is only done when the stack has a single
+ * element. Otherwise quote formatting is done on the
+ * element's entire output strbuf when the %(end) atom is
+ * encountered.
+ */
+ if (!state->stack->prev)
+ quote_formatting(&state->stack->output, v->s, state->quote_style);
+ else
+ strbuf_addstr(&state->stack->output, v->s);
+}
+
+static void push_stack_element(struct ref_formatting_stack **stack)
+{
+ struct ref_formatting_stack *s = xcalloc(1, sizeof(struct ref_formatting_stack));
+
+ strbuf_init(&s->output, 0);
+ s->prev = *stack;
+ *stack = s;
+}
+
+static void pop_stack_element(struct ref_formatting_stack **stack)
+{
+ struct ref_formatting_stack *current = *stack;
+ struct ref_formatting_stack *prev = current->prev;
+
+ if (prev)
+ strbuf_addbuf(&prev->output, &current->output);
+ strbuf_release(&current->output);
+ free(current);
+ *stack = prev;
+}
+
+static void end_align_handler(struct ref_formatting_stack *stack)
+{
+ struct align *align = (struct align *)stack->at_end_data;
+ struct strbuf s = STRBUF_INIT;
+
+ strbuf_utf8_align(&s, align->position, align->width, stack->output.buf);
+ strbuf_swap(&stack->output, &s);
+ strbuf_release(&s);
+}
+
+static void align_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state)
+{
+ struct ref_formatting_stack *new;
+
+ push_stack_element(&state->stack);
+ new = state->stack;
+ new->at_end = end_align_handler;
+ new->at_end_data = &atomv->u.align;
+}
+
+static void end_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state)
+{
+ struct ref_formatting_stack *current = state->stack;
+ struct strbuf s = STRBUF_INIT;
+
+ if (!current->at_end)
+ die(_("format: %%(end) atom used without corresponding atom"));
+ current->at_end(current);
+
+ /*
+ * Perform quote formatting when the stack element is that of
+ * a supporting atom. If nested then perform quote formatting
+ * only on the topmost supporting atom.
+ */
+ if (!state->stack->prev->prev) {
+ quote_formatting(&s, current->output.buf, state->quote_style);
+ strbuf_swap(&current->output, &s);
+ }
+ strbuf_release(&s);
+ pop_stack_element(&state->stack);
+}
+
+static int match_atom_name(const char *name, const char *atom_name, const char **val)
+{
+ const char *body;
+
+ if (!skip_prefix(name, atom_name, &body))
+ return 0; /* doesn't even begin with "atom_name" */
+ if (!body[0]) {
+ *val = NULL; /* %(atom_name) and no customization */
+ return 1;
+ }
+ if (body[0] != ':')
+ return 0; /* "atom_namefoo" is not "atom_name" or "atom_name:..." */
+ *val = body + 1; /* "atom_name:val" */
+ return 1;
+}
+
/*
* In a format string, find the next occurrence of %(atom).
*/
@@ -192,9 +343,7 @@ static int grab_objectname(const char *name, const unsigned char *sha1,
struct atom_value *v)
{
if (!strcmp(name, "objectname")) {
- char *s = xmalloc(41);
- strcpy(s, sha1_to_hex(sha1));
- v->s = s;
+ v->s = xstrdup(sha1_to_hex(sha1));
return 1;
}
if (!strcmp(name, "objectname:short")) {
@@ -219,10 +368,8 @@ static void grab_common_values(struct atom_value *val, int deref, struct object
if (!strcmp(name, "objecttype"))
v->s = typename(obj->type);
else if (!strcmp(name, "objectsize")) {
- char *s = xmalloc(40);
- sprintf(s, "%lu", sz);
v->ul = sz;
- v->s = s;
+ v->s = xstrfmt("%lu", sz);
}
else if (deref)
grab_objectname(name, obj->sha1, v);
@@ -246,11 +393,8 @@ static void grab_tag_values(struct atom_value *val, int deref, struct object *ob
v->s = tag->tag;
else if (!strcmp(name, "type") && tag->tagged)
v->s = typename(tag->tagged->type);
- else if (!strcmp(name, "object") && tag->tagged) {
- char *s = xmalloc(41);
- strcpy(s, sha1_to_hex(tag->tagged->sha1));
- v->s = s;
- }
+ else if (!strcmp(name, "object") && tag->tagged)
+ v->s = xstrdup(sha1_to_hex(tag->tagged->sha1));
}
}
@@ -268,32 +412,22 @@ static void grab_commit_values(struct atom_value *val, int deref, struct object
if (deref)
name++;
if (!strcmp(name, "tree")) {
- char *s = xmalloc(41);
- strcpy(s, sha1_to_hex(commit->tree->object.sha1));
- v->s = s;
+ v->s = xstrdup(sha1_to_hex(commit->tree->object.sha1));
}
- if (!strcmp(name, "numparent")) {
- char *s = xmalloc(40);
+ else if (!strcmp(name, "numparent")) {
v->ul = commit_list_count(commit->parents);
- sprintf(s, "%lu", v->ul);
- v->s = s;
+ v->s = xstrfmt("%lu", v->ul);
}
else if (!strcmp(name, "parent")) {
- int num = commit_list_count(commit->parents);
- int i;
struct commit_list *parents;
- char *s = xmalloc(41 * num + 1);
- v->s = s;
- for (i = 0, parents = commit->parents;
- parents;
- parents = parents->next, i = i + 41) {
+ struct strbuf s = STRBUF_INIT;
+ for (parents = commit->parents; parents; parents = parents->next) {
struct commit *parent = parents->item;
- strcpy(s+i, sha1_to_hex(parent->object.sha1));
- if (parents->next)
- s[i+40] = ' ';
+ if (parents != commit->parents)
+ strbuf_addch(&s, ' ');
+ strbuf_addstr(&s, sha1_to_hex(parent->object.sha1));
}
- if (!i)
- *s = '\0';
+ v->s = strbuf_detach(&s, NULL);
}
}
}
@@ -497,6 +631,30 @@ static void find_subpos(const char *buf, unsigned long sz,
*nonsiglen = *sig - buf;
}
+/*
+ * If 'lines' is greater than 0, append that many lines from the given
+ * 'buf' of length 'size' to the given strbuf.
+ */
+static void append_lines(struct strbuf *out, const char *buf, unsigned long size, int lines)
+{
+ int i;
+ const char *sp, *eol;
+ size_t len;
+
+ sp = buf;
+
+ for (i = 0; i < lines && sp < buf + size; i++) {
+ if (i)
+ strbuf_addstr(out, "\n ");
+ eol = memchr(sp, '\n', size - (sp - buf));
+ len = eol ? eol - sp : size - (sp - buf);
+ strbuf_add(out, sp, len);
+ if (!eol)
+ break;
+ sp = eol + 1;
+ }
+}
+
/* See grab_values */
static void grab_sub_body_contents(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)
{
@@ -507,6 +665,7 @@ static void grab_sub_body_contents(struct atom_value *val, int deref, struct obj
for (i = 0; i < used_atom_cnt; i++) {
const char *name = used_atom[i];
struct atom_value *v = &val[i];
+ const char *valp = NULL;
if (!!deref != (*name == '*'))
continue;
if (deref)
@@ -516,7 +675,8 @@ static void grab_sub_body_contents(struct atom_value *val, int deref, struct obj
strcmp(name, "contents") &&
strcmp(name, "contents:subject") &&
strcmp(name, "contents:body") &&
- strcmp(name, "contents:signature"))
+ strcmp(name, "contents:signature") &&
+ !starts_with(name, "contents:lines="))
continue;
if (!subpos)
find_subpos(buf, sz,
@@ -536,6 +696,16 @@ static void grab_sub_body_contents(struct atom_value *val, int deref, struct obj
v->s = xmemdupz(sigpos, siglen);
else if (!strcmp(name, "contents"))
v->s = xstrdup(subpos);
+ else if (skip_prefix(name, "contents:lines=", &valp)) {
+ struct strbuf s = STRBUF_INIT;
+ const char *contents_end = bodylen + bodypos - siglen;
+
+ if (strtoul_ui(valp, 10, &v->u.contents.lines))
+ die(_("positive value expected contents:lines=%s"), valp);
+ /* Size is the length of the message after removing the signature */
+ append_lines(&s, subpos, contents_end - subpos, v->u.contents.lines);
+ v->s = strbuf_detach(&s, NULL);
+ }
}
}
@@ -621,8 +791,11 @@ static void populate_value(struct ref_array_item *ref)
int deref = 0;
const char *refname;
const char *formatp;
+ const char *valp;
struct branch *branch = NULL;
+ v->handler = append_atom;
+
if (*name == '*') {
deref = 1;
name++;
@@ -653,10 +826,12 @@ static void populate_value(struct ref_array_item *ref)
refname = branch_get_push(branch, NULL);
if (!refname)
continue;
- } else if (starts_with(name, "color:")) {
+ } else if (match_atom_name(name, "color", &valp)) {
char color[COLOR_MAXLEN] = "";
- if (color_parse(name + 6, color) < 0)
+ if (!valp)
+ die(_("expected format: %%(color:<color>)"));
+ if (color_parse(valp, color) < 0)
die(_("unable to parse format"));
v->s = xstrdup(color);
continue;
@@ -686,6 +861,48 @@ static void populate_value(struct ref_array_item *ref)
else
v->s = " ";
continue;
+ } else if (match_atom_name(name, "align", &valp)) {
+ struct align *align = &v->u.align;
+ struct strbuf **s, **to_free;
+ int width = -1;
+
+ if (!valp)
+ die(_("expected format: %%(align:<width>,<position>)"));
+
+ /*
+ * TODO: Implement a function similar to strbuf_split_str()
+ * which would omit the separator from the end of each value.
+ */
+ s = to_free = strbuf_split_str(valp, ',', 0);
+
+ align->position = ALIGN_LEFT;
+
+ while (*s) {
+ /* Strip trailing comma */
+ if (s[1])
+ strbuf_setlen(s[0], s[0]->len - 1);
+ if (!strtoul_ui(s[0]->buf, 10, (unsigned int *)&width))
+ ;
+ else if (!strcmp(s[0]->buf, "left"))
+ align->position = ALIGN_LEFT;
+ else if (!strcmp(s[0]->buf, "right"))
+ align->position = ALIGN_RIGHT;
+ else if (!strcmp(s[0]->buf, "middle"))
+ align->position = ALIGN_MIDDLE;
+ else
+ die(_("improper format entered align:%s"), s[0]->buf);
+ s++;
+ }
+
+ if (width < 0)
+ die(_("positive width expected with the %%(align) atom"));
+ align->width = width;
+ strbuf_list_free(to_free);
+ v->handler = align_atom_handler;
+ continue;
+ } else if (!strcmp(name, "end")) {
+ v->handler = end_atom_handler;
+ continue;
} else
continue;
@@ -700,7 +917,6 @@ static void populate_value(struct ref_array_item *ref)
else if (!strcmp(formatp, "track") &&
(starts_with(name, "upstream") ||
starts_with(name, "push"))) {
- char buf[40];
if (stat_tracking_info(branch, &num_ours,
&num_theirs, NULL))
@@ -708,17 +924,13 @@ static void populate_value(struct ref_array_item *ref)
if (!num_ours && !num_theirs)
v->s = "";
- else if (!num_ours) {
- sprintf(buf, "[behind %d]", num_theirs);
- v->s = xstrdup(buf);
- } else if (!num_theirs) {
- sprintf(buf, "[ahead %d]", num_ours);
- v->s = xstrdup(buf);
- } else {
- sprintf(buf, "[ahead %d, behind %d]",
- num_ours, num_theirs);
- v->s = xstrdup(buf);
- }
+ else if (!num_ours)
+ v->s = xstrfmt("[behind %d]", num_theirs);
+ else if (!num_theirs)
+ v->s = xstrfmt("[ahead %d]", num_ours);
+ else
+ v->s = xstrfmt("[ahead %d, behind %d]",
+ num_ours, num_theirs);
continue;
} else if (!strcmp(formatp, "trackshort") &&
(starts_with(name, "upstream") ||
@@ -745,12 +957,8 @@ static void populate_value(struct ref_array_item *ref)
if (!deref)
v->s = refname;
- else {
- int len = strlen(refname);
- char *s = xmalloc(len + 4);
- sprintf(s, "%s^{}", refname);
- v->s = s;
- }
+ else
+ v->s = xstrfmt("%s^{}", refname);
}
for (i = 0; i < used_atom_cnt; i++) {
@@ -817,11 +1025,143 @@ static void get_ref_atom_value(struct ref_array_item *ref, int atom, struct atom
*v = &ref->value[atom];
}
+enum contains_result {
+ CONTAINS_UNKNOWN = -1,
+ CONTAINS_NO = 0,
+ CONTAINS_YES = 1
+};
+
+/*
+ * Mimicking the real stack, this stack lives on the heap, avoiding stack
+ * overflows.
+ *
+ * At each recursion step, the stack items points to the commits whose
+ * ancestors are to be inspected.
+ */
+struct contains_stack {
+ int nr, alloc;
+ struct contains_stack_entry {
+ struct commit *commit;
+ struct commit_list *parents;
+ } *contains_stack;
+};
+
+static int in_commit_list(const struct commit_list *want, struct commit *c)
+{
+ for (; want; want = want->next)
+ if (!hashcmp(want->item->object.sha1, c->object.sha1))
+ return 1;
+ return 0;
+}
+
+/*
+ * Test whether the candidate or one of its parents is contained in the list.
+ * Do not recurse to find out, though, but return -1 if inconclusive.
+ */
+static enum contains_result contains_test(struct commit *candidate,
+ const struct commit_list *want)
+{
+ /* was it previously marked as containing a want commit? */
+ if (candidate->object.flags & TMP_MARK)
+ return 1;
+ /* or marked as not possibly containing a want commit? */
+ if (candidate->object.flags & UNINTERESTING)
+ return 0;
+ /* or are we it? */
+ if (in_commit_list(want, candidate)) {
+ candidate->object.flags |= TMP_MARK;
+ return 1;
+ }
+
+ if (parse_commit(candidate) < 0)
+ return 0;
+
+ return -1;
+}
+
+static void push_to_contains_stack(struct commit *candidate, struct contains_stack *contains_stack)
+{
+ ALLOC_GROW(contains_stack->contains_stack, contains_stack->nr + 1, contains_stack->alloc);
+ contains_stack->contains_stack[contains_stack->nr].commit = candidate;
+ contains_stack->contains_stack[contains_stack->nr++].parents = candidate->parents;
+}
+
+static enum contains_result contains_tag_algo(struct commit *candidate,
+ const struct commit_list *want)
+{
+ struct contains_stack contains_stack = { 0, 0, NULL };
+ int result = contains_test(candidate, want);
+
+ if (result != CONTAINS_UNKNOWN)
+ return result;
+
+ push_to_contains_stack(candidate, &contains_stack);
+ while (contains_stack.nr) {
+ struct contains_stack_entry *entry = &contains_stack.contains_stack[contains_stack.nr - 1];
+ struct commit *commit = entry->commit;
+ struct commit_list *parents = entry->parents;
+
+ if (!parents) {
+ commit->object.flags |= UNINTERESTING;
+ contains_stack.nr--;
+ }
+ /*
+ * If we just popped the stack, parents->item has been marked,
+ * therefore contains_test will return a meaningful 0 or 1.
+ */
+ else switch (contains_test(parents->item, want)) {
+ case CONTAINS_YES:
+ commit->object.flags |= TMP_MARK;
+ contains_stack.nr--;
+ break;
+ case CONTAINS_NO:
+ entry->parents = parents->next;
+ break;
+ case CONTAINS_UNKNOWN:
+ push_to_contains_stack(parents->item, &contains_stack);
+ break;
+ }
+ }
+ free(contains_stack.contains_stack);
+ return contains_test(candidate, want);
+}
+
+static int commit_contains(struct ref_filter *filter, struct commit *commit)
+{
+ if (filter->with_commit_tag_algo)
+ return contains_tag_algo(commit, filter->with_commit);
+ return is_descendant_of(commit, filter->with_commit);
+}
+
+/*
+ * Return 1 if the refname matches one of the patterns, otherwise 0.
+ * A pattern can be a literal prefix (e.g. a refname "refs/heads/master"
+ * matches a pattern "refs/heads/mas") or a wildcard (e.g. the same ref
+ * matches "refs/heads/mas*", too).
+ */
+static int match_pattern(const char **patterns, const char *refname)
+{
+ /*
+ * When no '--format' option is given we need to skip the prefix
+ * for matching refs of tags and branches.
+ */
+ (void)(skip_prefix(refname, "refs/tags/", &refname) ||
+ skip_prefix(refname, "refs/heads/", &refname) ||
+ skip_prefix(refname, "refs/remotes/", &refname) ||
+ skip_prefix(refname, "refs/", &refname));
+
+ for (; *patterns; patterns++) {
+ if (!wildmatch(*patterns, refname, 0, NULL))
+ return 1;
+ }
+ return 0;
+}
+
/*
* Return 1 if the refname matches one of the patterns, otherwise 0.
* A pattern can be path prefix (e.g. a refname "refs/heads/master"
- * matches a pattern "refs/heads/") or a wildcard (e.g. the same ref
- * matches "refs/heads/m*",too).
+ * matches a pattern "refs/heads/" but not "refs/heads/m") or a
+ * wildcard (e.g. the same ref matches "refs/heads/m*", too).
*/
static int match_name_as_path(const char **pattern, const char *refname)
{
@@ -842,6 +1182,48 @@ static int match_name_as_path(const char **pattern, const char *refname)
return 0;
}
+/* Return 1 if the refname matches one of the patterns, otherwise 0. */
+static int filter_pattern_match(struct ref_filter *filter, const char *refname)
+{
+ if (!*filter->name_patterns)
+ return 1; /* No pattern always matches */
+ if (filter->match_as_path)
+ return match_name_as_path(filter->name_patterns, refname);
+ return match_pattern(filter->name_patterns, refname);
+}
+
+/*
+ * Given a ref (sha1, refname), check if the ref belongs to the array
+ * of sha1s. If the given ref is a tag, check if the given tag points
+ * at one of the sha1s in the given sha1 array.
+ * the given sha1_array.
+ * NEEDSWORK:
+ * 1. Only a single level of inderection is obtained, we might want to
+ * change this to account for multiple levels (e.g. annotated tags
+ * pointing to annotated tags pointing to a commit.)
+ * 2. As the refs are cached we might know what refname peels to without
+ * the need to parse the object via parse_object(). peel_ref() might be a
+ * more efficient alternative to obtain the pointee.
+ */
+static const unsigned char *match_points_at(struct sha1_array *points_at,
+ const unsigned char *sha1,
+ const char *refname)
+{
+ const unsigned char *tagged_sha1 = NULL;
+ struct object *obj;
+
+ if (sha1_array_lookup(points_at, sha1) >= 0)
+ return sha1;
+ obj = parse_object(sha1);
+ if (!obj)
+ die(_("malformed object at '%s'"), refname);
+ if (obj->type == OBJ_TAG)
+ tagged_sha1 = ((struct tag *)obj)->tagged->sha1;
+ if (tagged_sha1 && sha1_array_lookup(points_at, tagged_sha1) >= 0)
+ return tagged_sha1;
+ return NULL;
+}
+
/* Allocate space for a new ref_array_item and copy the objectname and flag to it */
static struct ref_array_item *new_ref_array_item(const char *refname,
const unsigned char *objectname,
@@ -857,6 +1239,34 @@ static struct ref_array_item *new_ref_array_item(const char *refname,
return ref;
}
+static int filter_ref_kind(struct ref_filter *filter, const char *refname)
+{
+ unsigned int i;
+
+ static struct {
+ const char *prefix;
+ unsigned int kind;
+ } ref_kind[] = {
+ { "refs/heads/" , FILTER_REFS_BRANCHES },
+ { "refs/remotes/" , FILTER_REFS_REMOTES },
+ { "refs/tags/", FILTER_REFS_TAGS}
+ };
+
+ if (filter->kind == FILTER_REFS_BRANCHES ||
+ filter->kind == FILTER_REFS_REMOTES ||
+ filter->kind == FILTER_REFS_TAGS)
+ return filter->kind;
+ else if (!strcmp(refname, "HEAD"))
+ return FILTER_REFS_DETACHED_HEAD;
+
+ for (i = 0; i < ARRAY_SIZE(ref_kind); i++) {
+ if (starts_with(refname, ref_kind[i].prefix))
+ return ref_kind[i].kind;
+ }
+
+ return FILTER_REFS_OTHERS;
+}
+
/*
* A call-back given to for_each_ref(). Filter refs and keep them for
* later object processing.
@@ -866,6 +1276,8 @@ static int ref_filter_handler(const char *refname, const struct object_id *oid,
struct ref_filter_cbdata *ref_cbdata = cb_data;
struct ref_filter *filter = ref_cbdata->filter;
struct ref_array_item *ref;
+ struct commit *commit = NULL;
+ unsigned int kind;
if (flag & REF_BAD_NAME) {
warning("ignoring ref with broken name %s", refname);
@@ -877,18 +1289,43 @@ static int ref_filter_handler(const char *refname, const struct object_id *oid,
return 0;
}
- if (*filter->name_patterns && !match_name_as_path(filter->name_patterns, refname))
+ /* Obtain the current ref kind from filter_ref_kind() and ignore unwanted refs. */
+ kind = filter_ref_kind(filter, refname);
+ if (!(kind & filter->kind))
+ return 0;
+
+ if (!filter_pattern_match(filter, refname))
+ return 0;
+
+ if (filter->points_at.nr && !match_points_at(&filter->points_at, oid->hash, refname))
return 0;
/*
+ * A merge filter is applied on refs pointing to commits. Hence
+ * obtain the commit using the 'oid' available and discard all
+ * non-commits early. The actual filtering is done later.
+ */
+ if (filter->merge_commit || filter->with_commit || filter->verbose) {
+ commit = lookup_commit_reference_gently(oid->hash, 1);
+ if (!commit)
+ return 0;
+ /* We perform the filtering for the '--contains' option */
+ if (filter->with_commit &&
+ !commit_contains(filter, commit))
+ return 0;
+ }
+
+ /*
* We do not open the object yet; sort may only need refname
* to do its job and the resulting list may yet to be pruned
* by maxcount logic.
*/
ref = new_ref_array_item(refname, oid->hash, flag);
+ ref->commit = commit;
REALLOC_ARRAY(ref_cbdata->array->items, ref_cbdata->array->nr + 1);
ref_cbdata->array->items[ref_cbdata->array->nr++] = ref;
+ ref->kind = kind;
return 0;
}
@@ -911,6 +1348,50 @@ void ref_array_clear(struct ref_array *array)
array->nr = array->alloc = 0;
}
+static void do_merge_filter(struct ref_filter_cbdata *ref_cbdata)
+{
+ struct rev_info revs;
+ int i, old_nr;
+ struct ref_filter *filter = ref_cbdata->filter;
+ struct ref_array *array = ref_cbdata->array;
+ struct commit **to_clear = xcalloc(sizeof(struct commit *), array->nr);
+
+ init_revisions(&revs, NULL);
+
+ for (i = 0; i < array->nr; i++) {
+ struct ref_array_item *item = array->items[i];
+ add_pending_object(&revs, &item->commit->object, item->refname);
+ to_clear[i] = item->commit;
+ }
+
+ filter->merge_commit->object.flags |= UNINTERESTING;
+ add_pending_object(&revs, &filter->merge_commit->object, "");
+
+ revs.limited = 1;
+ if (prepare_revision_walk(&revs))
+ die(_("revision walk setup failed"));
+
+ old_nr = array->nr;
+ array->nr = 0;
+
+ for (i = 0; i < old_nr; i++) {
+ struct ref_array_item *item = array->items[i];
+ struct commit *commit = item->commit;
+
+ int is_merged = !!(commit->object.flags & UNINTERESTING);
+
+ if (is_merged == (filter->merge == REF_FILTER_MERGED_INCLUDE))
+ array->items[array->nr++] = array->items[i];
+ else
+ free_array_item(item);
+ }
+
+ for (i = 0; i < old_nr; i++)
+ clear_commit_marks(to_clear[i], ALL_REV_FLAGS);
+ clear_commit_marks(filter->merge_commit, ALL_REV_FLAGS);
+ free(to_clear);
+}
+
/*
* API for filtering a set of refs. Based on the type of refs the user
* has requested, we iterate through those refs and apply filters
@@ -920,17 +1401,44 @@ void ref_array_clear(struct ref_array *array)
int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int type)
{
struct ref_filter_cbdata ref_cbdata;
+ int ret = 0;
+ unsigned int broken = 0;
ref_cbdata.array = array;
ref_cbdata.filter = filter;
- if (type & (FILTER_REFS_ALL | FILTER_REFS_INCLUDE_BROKEN))
- return for_each_rawref(ref_filter_handler, &ref_cbdata);
- else if (type & FILTER_REFS_ALL)
- return for_each_ref(ref_filter_handler, &ref_cbdata);
- else
+ if (type & FILTER_REFS_INCLUDE_BROKEN)
+ broken = 1;
+ filter->kind = type & FILTER_REFS_KIND_MASK;
+
+ /* Simple per-ref filtering */
+ if (!filter->kind)
die("filter_refs: invalid type");
- return 0;
+ else {
+ /*
+ * For common cases where we need only branches or remotes or tags,
+ * we only iterate through those refs. If a mix of refs is needed,
+ * we iterate over all refs and filter out required refs with the help
+ * of filter_ref_kind().
+ */
+ if (filter->kind == FILTER_REFS_BRANCHES)
+ ret = for_each_fullref_in("refs/heads/", ref_filter_handler, &ref_cbdata, broken);
+ else if (filter->kind == FILTER_REFS_REMOTES)
+ ret = for_each_fullref_in("refs/remotes/", ref_filter_handler, &ref_cbdata, broken);
+ else if (filter->kind == FILTER_REFS_TAGS)
+ ret = for_each_fullref_in("refs/tags/", ref_filter_handler, &ref_cbdata, broken);
+ else if (filter->kind & FILTER_REFS_ALL)
+ ret = for_each_fullref_in("", ref_filter_handler, &ref_cbdata, broken);
+ if (!ret && (filter->kind & FILTER_REFS_DETACHED_HEAD))
+ head_ref(ref_filter_handler, &ref_cbdata);
+ }
+
+
+ /* Filters that need revision walking */
+ if (filter->merge_commit)
+ do_merge_filter(&ref_cbdata);
+
+ return ret;
}
static int cmp_ref_sorting(struct ref_sorting *s, struct ref_array_item *a, struct ref_array_item *b)
@@ -941,19 +1449,19 @@ static int cmp_ref_sorting(struct ref_sorting *s, struct ref_array_item *a, stru
get_ref_atom_value(a, s->atom, &va);
get_ref_atom_value(b, s->atom, &vb);
- switch (cmp_type) {
- case FIELD_STR:
+ if (s->version)
+ cmp = versioncmp(va->s, vb->s);
+ else if (cmp_type == FIELD_STR)
cmp = strcmp(va->s, vb->s);
- break;
- default:
+ else {
if (va->ul < vb->ul)
cmp = -1;
else if (va->ul == vb->ul)
cmp = 0;
else
cmp = 1;
- break;
}
+
return (s->reverse) ? -cmp : cmp;
}
@@ -978,32 +1486,6 @@ void ref_array_sort(struct ref_sorting *sorting, struct ref_array *array)
qsort(array->items, array->nr, sizeof(struct ref_array_item *), compare_refs);
}
-static void print_value(struct atom_value *v, int quote_style)
-{
- struct strbuf sb = STRBUF_INIT;
- switch (quote_style) {
- case QUOTE_NONE:
- fputs(v->s, stdout);
- break;
- case QUOTE_SHELL:
- sq_quote_buf(&sb, v->s);
- break;
- case QUOTE_PERL:
- perl_quote_buf(&sb, v->s);
- break;
- case QUOTE_PYTHON:
- python_quote_buf(&sb, v->s);
- break;
- case QUOTE_TCL:
- tcl_quote_buf(&sb, v->s);
- break;
- }
- if (quote_style != QUOTE_NONE) {
- fputs(sb.buf, stdout);
- strbuf_release(&sb);
- }
-}
-
static int hex1(char ch)
{
if ('0' <= ch && ch <= '9')
@@ -1022,8 +1504,10 @@ static int hex2(const char *cp)
return -1;
}
-static void emit(const char *cp, const char *ep)
+static void append_literal(const char *cp, const char *ep, struct ref_formatting_state *state)
{
+ struct strbuf *s = &state->stack->output;
+
while (*cp && (!ep || cp < ep)) {
if (*cp == '%') {
if (cp[1] == '%')
@@ -1031,13 +1515,13 @@ static void emit(const char *cp, const char *ep)
else {
int ch = hex2(cp + 1);
if (0 <= ch) {
- putchar(ch);
+ strbuf_addch(s, ch);
cp += 3;
continue;
}
}
}
- putchar(*cp);
+ strbuf_addch(s, *cp);
cp++;
}
}
@@ -1045,19 +1529,24 @@ static void emit(const char *cp, const char *ep)
void show_ref_array_item(struct ref_array_item *info, const char *format, int quote_style)
{
const char *cp, *sp, *ep;
+ struct strbuf *final_buf;
+ struct ref_formatting_state state = REF_FORMATTING_STATE_INIT;
+
+ state.quote_style = quote_style;
+ push_stack_element(&state.stack);
for (cp = format; *cp && (sp = find_next(cp)); cp = ep + 1) {
struct atom_value *atomv;
ep = strchr(sp, ')');
if (cp < sp)
- emit(cp, sp);
+ append_literal(cp, sp, &state);
get_ref_atom_value(info, parse_ref_filter_atom(sp + 2, ep), &atomv);
- print_value(atomv, quote_style);
+ atomv->handler(atomv, &state);
}
if (*cp) {
sp = cp + strlen(cp);
- emit(cp, sp);
+ append_literal(cp, sp, &state);
}
if (need_color_reset_at_eol) {
struct atom_value resetv;
@@ -1066,8 +1555,13 @@ void show_ref_array_item(struct ref_array_item *info, const char *format, int qu
if (color_parse("reset", color) < 0)
die("BUG: couldn't parse 'reset' as a color");
resetv.s = color;
- print_value(&resetv, quote_style);
+ append_atom(&resetv, &state);
}
+ if (state.stack->prev)
+ die(_("format: %%(end) atom missing"));
+ final_buf = &state.stack->output;
+ fwrite(final_buf->buf, 1, final_buf->len, stdout);
+ pop_stack_element(&state.stack);
putchar('\n');
}
@@ -1100,7 +1594,29 @@ int parse_opt_ref_sorting(const struct option *opt, const char *arg, int unset)
s->reverse = 1;
arg++;
}
+ if (skip_prefix(arg, "version:", &arg) ||
+ skip_prefix(arg, "v:", &arg))
+ s->version = 1;
len = strlen(arg);
s->atom = parse_ref_filter_atom(arg, arg+len);
return 0;
}
+
+int parse_opt_merge_filter(const struct option *opt, const char *arg, int unset)
+{
+ struct ref_filter *rf = opt->value;
+ unsigned char sha1[20];
+
+ rf->merge = starts_with(opt->long_name, "no")
+ ? REF_FILTER_MERGED_OMIT
+ : REF_FILTER_MERGED_INCLUDE;
+
+ if (get_sha1(arg, sha1))
+ die(_("malformed object name %s"), arg);
+
+ rf->merge_commit = lookup_commit_reference_gently(sha1, 0);
+ if (!rf->merge_commit)
+ return opterror(opt, "must point to a commit", 0);
+
+ return 0;
+}
diff --git a/ref-filter.h b/ref-filter.h
index 699798400b..14d435e2cc 100644
--- a/ref-filter.h
+++ b/ref-filter.h
@@ -13,24 +13,31 @@
#define QUOTE_PYTHON 4
#define QUOTE_TCL 8
-#define FILTER_REFS_INCLUDE_BROKEN 0x1
-#define FILTER_REFS_ALL 0x2
+#define FILTER_REFS_INCLUDE_BROKEN 0x0001
+#define FILTER_REFS_TAGS 0x0002
+#define FILTER_REFS_BRANCHES 0x0004
+#define FILTER_REFS_REMOTES 0x0008
+#define FILTER_REFS_OTHERS 0x0010
+#define FILTER_REFS_ALL (FILTER_REFS_TAGS | FILTER_REFS_BRANCHES | \
+ FILTER_REFS_REMOTES | FILTER_REFS_OTHERS)
+#define FILTER_REFS_DETACHED_HEAD 0x0020
+#define FILTER_REFS_KIND_MASK (FILTER_REFS_ALL | FILTER_REFS_DETACHED_HEAD)
-struct atom_value {
- const char *s;
- unsigned long ul; /* used for sorting when not FIELD_STR */
-};
+struct atom_value;
struct ref_sorting {
struct ref_sorting *next;
int atom; /* index into used_atom array (internal) */
- unsigned reverse : 1;
+ unsigned reverse : 1,
+ version : 1;
};
struct ref_array_item {
unsigned char objectname[20];
int flag;
+ unsigned int kind;
const char *symref;
+ struct commit *commit;
struct atom_value *value;
char refname[FLEX_ARRAY];
};
@@ -38,10 +45,28 @@ struct ref_array_item {
struct ref_array {
int nr, alloc;
struct ref_array_item **items;
+ struct rev_info *revs;
};
struct ref_filter {
const char **name_patterns;
+ struct sha1_array points_at;
+ struct commit_list *with_commit;
+
+ enum {
+ REF_FILTER_MERGED_NONE = 0,
+ REF_FILTER_MERGED_INCLUDE,
+ REF_FILTER_MERGED_OMIT
+ } merge;
+ struct commit *merge_commit;
+
+ unsigned int with_commit_tag_algo : 1,
+ match_as_path : 1,
+ detached : 1;
+ unsigned int kind,
+ lines;
+ int abbrev,
+ verbose;
};
struct ref_filter_cbdata {
@@ -49,6 +74,15 @@ struct ref_filter_cbdata {
struct ref_filter *filter;
};
+/* Macros for checking --merged and --no-merged options */
+#define _OPT_MERGED_NO_MERGED(option, filter, h) \
+ { OPTION_CALLBACK, 0, option, (filter), N_("commit"), (h), \
+ PARSE_OPT_LASTARG_DEFAULT | PARSE_OPT_NONEG, \
+ parse_opt_merge_filter, (intptr_t) "HEAD" \
+ }
+#define OPT_MERGED(f, h) _OPT_MERGED_NO_MERGED("merged", f, h)
+#define OPT_NO_MERGED(f, h) _OPT_MERGED_NO_MERGED("no-merged", f, h)
+
/*
* API for filtering a set of refs. Based on the type of refs the user
* has requested, we iterate through those refs and apply filters
@@ -70,5 +104,7 @@ void show_ref_array_item(struct ref_array_item *info, const char *format, int qu
int parse_opt_ref_sorting(const struct option *opt, const char *arg, int unset);
/* Default sort option based on refname */
struct ref_sorting *ref_default_sorting(void);
+/* Function to parse --merged and --no-merged options */
+int parse_opt_merge_filter(const struct option *opt, const char *arg, int unset);
#endif /* REF_FILTER_H */
diff --git a/reflog-walk.c b/reflog-walk.c
index f8e743a23b..85b8a54241 100644
--- a/reflog-walk.c
+++ b/reflog-walk.c
@@ -56,12 +56,11 @@ static struct complete_reflogs *read_complete_reflog(const char *ref)
}
}
if (reflogs->nr == 0) {
- int len = strlen(ref);
- char *refname = xmalloc(len + 12);
- sprintf(refname, "refs/%s", ref);
+ char *refname = xstrfmt("refs/%s", ref);
for_each_reflog_ent(refname, read_one_reflog, reflogs);
if (reflogs->nr == 0) {
- sprintf(refname, "refs/heads/%s", ref);
+ free(refname);
+ refname = xstrfmt("refs/heads/%s", ref);
for_each_reflog_ent(refname, read_one_reflog, reflogs);
}
free(refname);
diff --git a/refs.c b/refs.c
index 4e15f60d98..132eff52ca 100644
--- a/refs.c
+++ b/refs.c
@@ -304,6 +304,11 @@ struct ref_entry {
};
static void read_loose_refs(const char *dirname, struct ref_dir *dir);
+static int search_ref_dir(struct ref_dir *dir, const char *refname, size_t len);
+static struct ref_entry *create_dir_entry(struct ref_cache *ref_cache,
+ const char *dirname, size_t len,
+ int incomplete);
+static void add_entry_to_dir(struct ref_dir *dir, struct ref_entry *entry);
static struct ref_dir *get_ref_dir(struct ref_entry *entry)
{
@@ -312,6 +317,24 @@ static struct ref_dir *get_ref_dir(struct ref_entry *entry)
dir = &entry->u.subdir;
if (entry->flag & REF_INCOMPLETE) {
read_loose_refs(entry->name, dir);
+
+ /*
+ * Manually add refs/bisect, which, being
+ * per-worktree, might not appear in the directory
+ * listing for refs/ in the main repo.
+ */
+ if (!strcmp(entry->name, "refs/")) {
+ int pos = search_ref_dir(dir, "refs/bisect/", 12);
+ if (pos < 0) {
+ struct ref_entry *child_entry;
+ child_entry = create_dir_entry(dir->ref_cache,
+ "refs/bisect/",
+ 12, 1);
+ add_entry_to_dir(dir, child_entry);
+ read_loose_refs("refs/bisect",
+ &child_entry->u.subdir);
+ }
+ }
entry->flag &= ~REF_INCOMPLETE;
}
return dir;
@@ -1579,16 +1602,15 @@ static int resolve_missing_loose_ref(const char *refname,
}
/* This function needs to return a meaningful errno on failure */
-static const char *resolve_ref_unsafe_1(const char *refname,
- int resolve_flags,
- unsigned char *sha1,
- int *flags,
- struct strbuf *sb_path)
+static const char *resolve_ref_1(const char *refname,
+ int resolve_flags,
+ unsigned char *sha1,
+ int *flags,
+ struct strbuf *sb_refname,
+ struct strbuf *sb_path,
+ struct strbuf *sb_contents)
{
int depth = MAXDEPTH;
- ssize_t len;
- char buffer[256];
- static char refname_buffer[256];
int bad_name = 0;
if (flags)
@@ -1654,19 +1676,18 @@ static const char *resolve_ref_unsafe_1(const char *refname,
/* Follow "normalized" - ie "refs/.." symlinks by hand */
if (S_ISLNK(st.st_mode)) {
- len = readlink(path, buffer, sizeof(buffer)-1);
- if (len < 0) {
+ strbuf_reset(sb_contents);
+ if (strbuf_readlink(sb_contents, path, 0) < 0) {
if (errno == ENOENT || errno == EINVAL)
/* inconsistent with lstat; retry */
goto stat_ref;
else
return NULL;
}
- buffer[len] = 0;
- if (starts_with(buffer, "refs/") &&
- !check_refname_format(buffer, 0)) {
- strcpy(refname_buffer, buffer);
- refname = refname_buffer;
+ if (starts_with(sb_contents->buf, "refs/") &&
+ !check_refname_format(sb_contents->buf, 0)) {
+ strbuf_swap(sb_refname, sb_contents);
+ refname = sb_refname->buf;
if (flags)
*flags |= REF_ISSYMREF;
if (resolve_flags & RESOLVE_REF_NO_RECURSE) {
@@ -1695,28 +1716,26 @@ static const char *resolve_ref_unsafe_1(const char *refname,
else
return NULL;
}
- len = read_in_full(fd, buffer, sizeof(buffer)-1);
- if (len < 0) {
+ strbuf_reset(sb_contents);
+ if (strbuf_read(sb_contents, fd, 256) < 0) {
int save_errno = errno;
close(fd);
errno = save_errno;
return NULL;
}
close(fd);
- while (len && isspace(buffer[len-1]))
- len--;
- buffer[len] = '\0';
+ strbuf_rtrim(sb_contents);
/*
* Is it a symbolic ref?
*/
- if (!starts_with(buffer, "ref:")) {
+ if (!starts_with(sb_contents->buf, "ref:")) {
/*
* Please note that FETCH_HEAD has a second
* line containing other data.
*/
- if (get_sha1_hex(buffer, sha1) ||
- (buffer[40] != '\0' && !isspace(buffer[40]))) {
+ if (get_sha1_hex(sb_contents->buf, sha1) ||
+ (sb_contents->buf[40] != '\0' && !isspace(sb_contents->buf[40]))) {
if (flags)
*flags |= REF_ISBROKEN;
errno = EINVAL;
@@ -1731,10 +1750,12 @@ static const char *resolve_ref_unsafe_1(const char *refname,
}
if (flags)
*flags |= REF_ISSYMREF;
- buf = buffer + 4;
+ buf = sb_contents->buf + 4;
while (isspace(*buf))
buf++;
- refname = strcpy(refname_buffer, buf);
+ strbuf_reset(sb_refname);
+ strbuf_addstr(sb_refname, buf);
+ refname = sb_refname->buf;
if (resolve_flags & RESOLVE_REF_NO_RECURSE) {
hashclr(sha1);
return refname;
@@ -1756,10 +1777,15 @@ static const char *resolve_ref_unsafe_1(const char *refname,
const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
unsigned char *sha1, int *flags)
{
+ static struct strbuf sb_refname = STRBUF_INIT;
+ struct strbuf sb_contents = STRBUF_INIT;
struct strbuf sb_path = STRBUF_INIT;
- const char *ret = resolve_ref_unsafe_1(refname, resolve_flags,
- sha1, flags, &sb_path);
+ const char *ret;
+
+ ret = resolve_ref_1(refname, resolve_flags, sha1, flags,
+ &sb_refname, &sb_path, &sb_contents);
strbuf_release(&sb_path);
+ strbuf_release(&sb_contents);
return ret;
}
@@ -2108,6 +2134,15 @@ int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data)
return do_for_each_ref(&ref_cache, prefix, fn, strlen(prefix), 0, cb_data);
}
+int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data, unsigned int broken)
+{
+ unsigned int flag = 0;
+
+ if (broken)
+ flag = DO_FOR_EACH_INCLUDE_BROKEN;
+ return do_for_each_ref(&ref_cache, prefix, fn, 0, flag, cb_data);
+}
+
int for_each_ref_in_submodule(const char *submodule, const char *prefix,
each_ref_fn fn, void *cb_data)
{
@@ -2190,8 +2225,7 @@ int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,
if (!has_glob_specials(pattern)) {
/* Append implied '/' '*' if not present. */
- if (real_pattern.buf[real_pattern.len - 1] != '/')
- strbuf_addch(&real_pattern, '/');
+ strbuf_complete(&real_pattern, '/');
/* No need to check for '*', there is none. */
strbuf_addch(&real_pattern, '*');
}
@@ -2649,6 +2683,8 @@ struct pack_refs_cb_data {
struct ref_to_prune *ref_to_prune;
};
+static int is_per_worktree_ref(const char *refname);
+
/*
* An each_ref_entry_fn that is run over loose references only. If
* the loose reference can be packed, add an entry in the packed ref
@@ -2662,6 +2698,10 @@ static int pack_if_possible_fn(struct ref_entry *entry, void *cb_data)
struct ref_entry *packed_entry;
int is_tag_ref = starts_with(entry->name, "refs/tags/");
+ /* Do not pack per-worktree refs: */
+ if (is_per_worktree_ref(entry->name))
+ return 0;
+
/* ALWAYS pack tags */
if (!(cb->flags & PACK_REFS_ALL) && !is_tag_ref)
return 0;
@@ -2692,7 +2732,7 @@ static int pack_if_possible_fn(struct ref_entry *entry, void *cb_data)
int namelen = strlen(entry->name) + 1;
struct ref_to_prune *n = xcalloc(1, sizeof(*n) + namelen);
hashcpy(n->sha1, entry->u.value.oid.hash);
- strcpy(n->name, entry->name);
+ memcpy(n->name, entry->name, namelen); /* includes NUL */
n->next = cb->ref_to_prune;
cb->ref_to_prune = n;
}
@@ -2856,7 +2896,8 @@ static int delete_ref_loose(struct ref_lock *lock, int flag, struct strbuf *err)
static int is_per_worktree_ref(const char *refname)
{
- return !strcmp(refname, "HEAD");
+ return !strcmp(refname, "HEAD") ||
+ starts_with(refname, "refs/bisect/");
}
static int is_pseudoref_syntax(const char *refname)
@@ -3326,10 +3367,10 @@ static int log_ref_write_fd(int fd, const unsigned char *old_sha1,
msglen = msg ? strlen(msg) : 0;
maxlen = strlen(committer) + msglen + 100;
logrec = xmalloc(maxlen);
- len = sprintf(logrec, "%s %s %s\n",
- sha1_to_hex(old_sha1),
- sha1_to_hex(new_sha1),
- committer);
+ len = xsnprintf(logrec, maxlen, "%s %s %s\n",
+ sha1_to_hex(old_sha1),
+ sha1_to_hex(new_sha1),
+ committer);
if (msglen)
len += copy_msg(logrec + len - 1, msg) - 1;
@@ -3981,10 +4022,10 @@ void ref_transaction_free(struct ref_transaction *transaction)
static struct ref_update *add_update(struct ref_transaction *transaction,
const char *refname)
{
- size_t len = strlen(refname);
- struct ref_update *update = xcalloc(1, sizeof(*update) + len + 1);
+ size_t len = strlen(refname) + 1;
+ struct ref_update *update = xcalloc(1, sizeof(*update) + len);
- strcpy((char *)update->refname, refname);
+ memcpy((char *)update->refname, refname, len); /* includes NUL */
ALLOC_GROW(transaction->updates, transaction->nr + 1, transaction->alloc);
transaction->updates[transaction->nr++] = update;
return update;
diff --git a/refs.h b/refs.h
index e9a5f3230a..6d30c980d1 100644
--- a/refs.h
+++ b/refs.h
@@ -173,6 +173,7 @@ typedef int each_ref_fn(const char *refname,
extern int head_ref(each_ref_fn fn, void *cb_data);
extern int for_each_ref(each_ref_fn fn, void *cb_data);
extern int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data);
+extern int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data, unsigned int broken);
extern int for_each_tag_ref(each_ref_fn fn, void *cb_data);
extern int for_each_branch_ref(each_ref_fn fn, void *cb_data);
extern int for_each_remote_ref(each_ref_fn fn, void *cb_data);
diff --git a/remote-curl.c b/remote-curl.c
index 71fbbb694f..cc7a8a66fa 100644
--- a/remote-curl.c
+++ b/remote-curl.c
@@ -168,10 +168,7 @@ static struct ref *parse_info_refs(struct discovery *heads)
url.buf);
data[i] = 0;
ref_name = mid + 1;
- ref = xmalloc(sizeof(struct ref) +
- strlen(ref_name) + 1);
- memset(ref, 0, sizeof(struct ref));
- strcpy(ref->name, ref_name);
+ ref = alloc_ref(ref_name);
get_sha1_hex(start, ref->old_sha1);
if (!refs)
refs = ref;
diff --git a/remote.c b/remote.c
index 26504b7447..1101f82eaf 100644
--- a/remote.c
+++ b/remote.c
@@ -8,6 +8,7 @@
#include "tag.h"
#include "string-list.h"
#include "mergesort.h"
+#include "argv-array.h"
enum map_direction { FROM_SRC, FROM_DST };
@@ -54,9 +55,6 @@ static const char *pushremote_name;
static struct rewrites rewrites;
static struct rewrites rewrites_push;
-#define BUF_SIZE (2048)
-static char buffer[BUF_SIZE];
-
static int valid_remote(const struct remote *remote)
{
return (!!remote->url) || (!!remote->foreign_vcs);
@@ -65,7 +63,6 @@ static int valid_remote(const struct remote *remote)
static const char *alias_url(const char *url, struct rewrites *r)
{
int i, j;
- char *ret;
struct counted_string *longest;
int longest_i;
@@ -86,11 +83,7 @@ static const char *alias_url(const char *url, struct rewrites *r)
if (!longest)
return url;
- ret = xmalloc(r->rewrite[longest_i]->baselen +
- (strlen(url) - longest->len) + 1);
- strcpy(ret, r->rewrite[longest_i]->base);
- strcpy(ret + r->rewrite[longest_i]->baselen, url + longest->len);
- return ret;
+ return xstrfmt("%s%s", r->rewrite[longest_i]->base, url + longest->len);
}
static void add_push_refspec(struct remote *remote, const char *ref)
@@ -248,106 +241,76 @@ static void add_instead_of(struct rewrite *rewrite, const char *instead_of)
rewrite->instead_of_nr++;
}
+static const char *skip_spaces(const char *s)
+{
+ while (isspace(*s))
+ s++;
+ return s;
+}
+
static void read_remotes_file(struct remote *remote)
{
+ struct strbuf buf = STRBUF_INIT;
FILE *f = fopen(git_path("remotes/%s", remote->name), "r");
if (!f)
return;
remote->origin = REMOTE_REMOTES;
- while (fgets(buffer, BUF_SIZE, f)) {
- int value_list;
- char *s, *p;
-
- if (starts_with(buffer, "URL:")) {
- value_list = 0;
- s = buffer + 4;
- } else if (starts_with(buffer, "Push:")) {
- value_list = 1;
- s = buffer + 5;
- } else if (starts_with(buffer, "Pull:")) {
- value_list = 2;
- s = buffer + 5;
- } else
- continue;
-
- while (isspace(*s))
- s++;
- if (!*s)
- continue;
+ while (strbuf_getline(&buf, f, '\n') != EOF) {
+ const char *v;
- p = s + strlen(s);
- while (isspace(p[-1]))
- *--p = 0;
+ strbuf_rtrim(&buf);
- switch (value_list) {
- case 0:
- add_url_alias(remote, xstrdup(s));
- break;
- case 1:
- add_push_refspec(remote, xstrdup(s));
- break;
- case 2:
- add_fetch_refspec(remote, xstrdup(s));
- break;
- }
+ if (skip_prefix(buf.buf, "URL:", &v))
+ add_url_alias(remote, xstrdup(skip_spaces(v)));
+ else if (skip_prefix(buf.buf, "Push:", &v))
+ add_push_refspec(remote, xstrdup(skip_spaces(v)));
+ else if (skip_prefix(buf.buf, "Pull:", &v))
+ add_fetch_refspec(remote, xstrdup(skip_spaces(v)));
}
+ strbuf_release(&buf);
fclose(f);
}
static void read_branches_file(struct remote *remote)
{
char *frag;
- struct strbuf branch = STRBUF_INIT;
- int n = 1000;
- FILE *f = fopen(git_path("branches/%.*s", n, remote->name), "r");
- char *s, *p;
- int len;
+ struct strbuf buf = STRBUF_INIT;
+ FILE *f = fopen(git_path("branches/%s", remote->name), "r");
if (!f)
return;
- s = fgets(buffer, BUF_SIZE, f);
- fclose(f);
- if (!s)
- return;
- while (isspace(*s))
- s++;
- if (!*s)
+
+ strbuf_getline(&buf, f, '\n');
+ strbuf_trim(&buf);
+ if (!buf.len) {
+ strbuf_release(&buf);
return;
+ }
+
remote->origin = REMOTE_BRANCHES;
- p = s + strlen(s);
- while (isspace(p[-1]))
- *--p = 0;
- len = p - s;
- p = xmalloc(len + 1);
- strcpy(p, s);
/*
* The branches file would have URL and optionally
* #branch specified. The "master" (or specified) branch is
- * fetched and stored in the local branch of the same name.
+ * fetched and stored in the local branch matching the
+ * remote name.
*/
- frag = strchr(p, '#');
- if (frag) {
+ frag = strchr(buf.buf, '#');
+ if (frag)
*(frag++) = '\0';
- strbuf_addf(&branch, "refs/heads/%s", frag);
- } else
- strbuf_addstr(&branch, "refs/heads/master");
+ else
+ frag = "master";
+
+ add_url_alias(remote, strbuf_detach(&buf, NULL));
+ add_fetch_refspec(remote, xstrfmt("refs/heads/%s:refs/heads/%s",
+ frag, remote->name));
- strbuf_addf(&branch, ":refs/heads/%s", remote->name);
- add_url_alias(remote, p);
- add_fetch_refspec(remote, strbuf_detach(&branch, NULL));
/*
* Cogito compatible push: push current HEAD to remote #branch
* (master if missing)
*/
- strbuf_init(&branch, 0);
- strbuf_addstr(&branch, "HEAD");
- if (frag)
- strbuf_addf(&branch, ":refs/heads/%s", frag);
- else
- strbuf_addstr(&branch, ":refs/heads/master");
- add_push_refspec(remote, strbuf_detach(&branch, NULL));
+ add_push_refspec(remote, xstrfmt("HEAD:refs/heads/%s", frag));
remote->fetch_tags = 1; /* always auto-follow */
}
@@ -2035,10 +1998,9 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,
{
unsigned char sha1[20];
struct commit *ours, *theirs;
- char symmetric[84];
struct rev_info revs;
- const char *rev_argv[10], *base;
- int rev_argc;
+ const char *base;
+ struct argv_array argv = ARGV_ARRAY_INIT;
/* Cannot stat unless we are marked to build on top of somebody else. */
base = branch_get_upstream(branch, NULL);
@@ -2067,19 +2029,15 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,
}
/* Run "rev-list --left-right ours...theirs" internally... */
- rev_argc = 0;
- rev_argv[rev_argc++] = NULL;
- rev_argv[rev_argc++] = "--left-right";
- rev_argv[rev_argc++] = symmetric;
- rev_argv[rev_argc++] = "--";
- rev_argv[rev_argc] = NULL;
-
- strcpy(symmetric, sha1_to_hex(ours->object.sha1));
- strcpy(symmetric + 40, "...");
- strcpy(symmetric + 43, sha1_to_hex(theirs->object.sha1));
+ argv_array_push(&argv, ""); /* ignored */
+ argv_array_push(&argv, "--left-right");
+ argv_array_pushf(&argv, "%s...%s",
+ sha1_to_hex(ours->object.sha1),
+ sha1_to_hex(theirs->object.sha1));
+ argv_array_push(&argv, "--");
init_revisions(&revs, NULL);
- setup_revisions(rev_argc, rev_argv, &revs, NULL);
+ setup_revisions(argv.argc, argv.argv, &revs, NULL);
if (prepare_revision_walk(&revs))
die("revision walk setup failed");
@@ -2099,6 +2057,8 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,
/* clear object flags smudged by the above traversal */
clear_commit_marks(ours, ALL_REV_FLAGS);
clear_commit_marks(theirs, ALL_REV_FLAGS);
+
+ argv_array_clear(&argv);
return 0;
}
diff --git a/rerere.c b/rerere.c
index d90057b769..403c700c32 100644
--- a/rerere.c
+++ b/rerere.c
@@ -20,45 +20,74 @@ static int rerere_enabled = -1;
/* automatically update cleanly resolved paths to the index */
static int rerere_autoupdate;
-const char *rerere_path(const char *hex, const char *file)
+static void free_rerere_id(struct string_list_item *item)
{
- return git_path("rr-cache/%s/%s", hex, file);
+ free(item->util);
}
-static int has_rerere_resolution(const char *hex)
+static const char *rerere_id_hex(const struct rerere_id *id)
+{
+ return id->hex;
+}
+
+const char *rerere_path(const struct rerere_id *id, const char *file)
+{
+ if (!file)
+ return git_path("rr-cache/%s", rerere_id_hex(id));
+
+ return git_path("rr-cache/%s/%s", rerere_id_hex(id), file);
+}
+
+static int has_rerere_resolution(const struct rerere_id *id)
{
struct stat st;
- return !stat(rerere_path(hex, "postimage"), &st);
+
+ return !stat(rerere_path(id, "postimage"), &st);
+}
+
+static struct rerere_id *new_rerere_id_hex(char *hex)
+{
+ struct rerere_id *id = xmalloc(sizeof(*id));
+ strcpy(id->hex, hex);
+ return id;
+}
+
+static struct rerere_id *new_rerere_id(unsigned char *sha1)
+{
+ return new_rerere_id_hex(sha1_to_hex(sha1));
}
+/*
+ * $GIT_DIR/MERGE_RR file is a collection of records, each of which is
+ * "conflict ID", a HT and pathname, terminated with a NUL, and is
+ * used to keep track of the set of paths that "rerere" may need to
+ * work on (i.e. what is left by the previous invocation of "git
+ * rerere" during the current conflict resolution session).
+ */
static void read_rr(struct string_list *rr)
{
- unsigned char sha1[20];
- char buf[PATH_MAX];
+ struct strbuf buf = STRBUF_INIT;
FILE *in = fopen(git_path_merge_rr(), "r");
+
if (!in)
return;
- while (fread(buf, 40, 1, in) == 1) {
- int i;
- char *name;
- if (get_sha1_hex(buf, sha1))
+ while (!strbuf_getwholeline(&buf, in, '\0')) {
+ char *path;
+ unsigned char sha1[20];
+ struct rerere_id *id;
+
+ /* There has to be the hash, tab, path and then NUL */
+ if (buf.len < 42 || get_sha1_hex(buf.buf, sha1))
die("corrupt MERGE_RR");
- buf[40] = '\0';
- name = xstrdup(buf);
- if (fgetc(in) != '\t')
+
+ if (buf.buf[40] != '\t')
die("corrupt MERGE_RR");
- for (i = 0; i < sizeof(buf); i++) {
- int c = fgetc(in);
- if (c < 0)
- die("corrupt MERGE_RR");
- buf[i] = c;
- if (c == 0)
- break;
- }
- if (i == sizeof(buf))
- die("filename too long");
- string_list_insert(rr, buf)->util = name;
+ buf.buf[40] = '\0';
+ path = buf.buf + 41;
+ id = new_rerere_id_hex(buf.buf);
+ string_list_insert(rr, path)->util = id;
}
+ strbuf_release(&buf);
fclose(in);
}
@@ -68,22 +97,42 @@ static int write_rr(struct string_list *rr, int out_fd)
{
int i;
for (i = 0; i < rr->nr; i++) {
- const char *path;
- int length;
- if (!rr->items[i].util)
+ struct strbuf buf = STRBUF_INIT;
+ struct rerere_id *id;
+
+ assert(rr->items[i].util != RERERE_RESOLVED);
+
+ id = rr->items[i].util;
+ if (!id)
continue;
- path = rr->items[i].string;
- length = strlen(path) + 1;
- if (write_in_full(out_fd, rr->items[i].util, 40) != 40 ||
- write_str_in_full(out_fd, "\t") != 1 ||
- write_in_full(out_fd, path, length) != length)
+ strbuf_addf(&buf, "%s\t%s%c",
+ rerere_id_hex(id),
+ rr->items[i].string, 0);
+ if (write_in_full(out_fd, buf.buf, buf.len) != buf.len)
die("unable to write rerere record");
+
+ strbuf_release(&buf);
}
if (commit_lock_file(&write_lock) != 0)
die("unable to write rerere record");
return 0;
}
+/*
+ * "rerere" interacts with conflicted file contents using this I/O
+ * abstraction. It reads a conflicted contents from one place via
+ * "getline()" method, and optionally can write it out after
+ * normalizing the conflicted hunks to the "output". Subclasses of
+ * rerere_io embed this structure at the beginning of their own
+ * rerere_io object.
+ */
+struct rerere_io {
+ int (*getline)(struct strbuf *, struct rerere_io *);
+ FILE *output;
+ int wrerror;
+ /* some more stuff */
+};
+
static void ferr_write(const void *p, size_t count, FILE *fp, int *err)
{
if (!count || *err)
@@ -97,31 +146,34 @@ static inline void ferr_puts(const char *s, FILE *fp, int *err)
ferr_write(s, strlen(s), fp, err);
}
-struct rerere_io {
- int (*getline)(struct strbuf *, struct rerere_io *);
- FILE *output;
- int wrerror;
- /* some more stuff */
-};
-
static void rerere_io_putstr(const char *str, struct rerere_io *io)
{
if (io->output)
ferr_puts(str, io->output, &io->wrerror);
}
+/*
+ * Write a conflict marker to io->output (if defined).
+ */
static void rerere_io_putconflict(int ch, int size, struct rerere_io *io)
{
char buf[64];
while (size) {
- if (size < sizeof(buf) - 2) {
+ if (size <= sizeof(buf) - 2) {
memset(buf, ch, size);
buf[size] = '\n';
buf[size + 1] = '\0';
size = 0;
} else {
int sz = sizeof(buf) - 1;
+
+ /*
+ * Make sure we will not write everything out
+ * in this round by leaving at least 1 byte
+ * for the next round, giving the next round
+ * a chance to add the terminating LF. Yuck.
+ */
if (size <= sz)
sz -= (sz - size) + 1;
memset(buf, ch, sz);
@@ -138,19 +190,42 @@ static void rerere_io_putmem(const char *mem, size_t sz, struct rerere_io *io)
ferr_write(mem, sz, io->output, &io->wrerror);
}
+/*
+ * Subclass of rerere_io that reads from an on-disk file
+ */
struct rerere_io_file {
struct rerere_io io;
FILE *input;
};
+/*
+ * ... and its getline() method implementation
+ */
static int rerere_file_getline(struct strbuf *sb, struct rerere_io *io_)
{
struct rerere_io_file *io = (struct rerere_io_file *)io_;
return strbuf_getwholeline(sb, io->input, '\n');
}
-static int is_cmarker(char *buf, int marker_char, int marker_size, int want_sp)
+/*
+ * Require the exact number of conflict marker letters, no more, no
+ * less, followed by SP or any whitespace
+ * (including LF).
+ */
+static int is_cmarker(char *buf, int marker_char, int marker_size)
{
+ int want_sp;
+
+ /*
+ * The beginning of our version and the end of their version
+ * always are labeled like "<<<<< ours" or ">>>>> theirs",
+ * hence we set want_sp for them. Note that the version from
+ * the common ancestor in diff3-style output is not always
+ * labelled (e.g. "||||| common" is often seen but "|||||"
+ * alone is also valid), so we do not set want_sp.
+ */
+ want_sp = (marker_char == '<') || (marker_char == '>');
+
while (marker_size--)
if (*buf++ != marker_char)
return 0;
@@ -159,6 +234,21 @@ static int is_cmarker(char *buf, int marker_char, int marker_size, int want_sp)
return isspace(*buf);
}
+/*
+ * Read contents a file with conflicts, normalize the conflicts
+ * by (1) discarding the common ancestor version in diff3-style,
+ * (2) reordering our side and their side so that whichever sorts
+ * alphabetically earlier comes before the other one, while
+ * computing the "conflict ID", which is just an SHA-1 hash of
+ * one side of the conflict, NUL, the other side of the conflict,
+ * and NUL concatenated together.
+ *
+ * Return the number of conflict hunks found.
+ *
+ * NEEDSWORK: the logic and theory of operation behind this conflict
+ * normalization may deserve to be documented somewhere, perhaps in
+ * Documentation/technical/rerere.txt.
+ */
static int handle_path(unsigned char *sha1, struct rerere_io *io, int marker_size)
{
git_SHA_CTX ctx;
@@ -173,19 +263,19 @@ static int handle_path(unsigned char *sha1, struct rerere_io *io, int marker_siz
git_SHA1_Init(&ctx);
while (!io->getline(&buf, io)) {
- if (is_cmarker(buf.buf, '<', marker_size, 1)) {
+ if (is_cmarker(buf.buf, '<', marker_size)) {
if (hunk != RR_CONTEXT)
goto bad;
hunk = RR_SIDE_1;
- } else if (is_cmarker(buf.buf, '|', marker_size, 0)) {
+ } else if (is_cmarker(buf.buf, '|', marker_size)) {
if (hunk != RR_SIDE_1)
goto bad;
hunk = RR_ORIGINAL;
- } else if (is_cmarker(buf.buf, '=', marker_size, 0)) {
+ } else if (is_cmarker(buf.buf, '=', marker_size)) {
if (hunk != RR_SIDE_1 && hunk != RR_ORIGINAL)
goto bad;
hunk = RR_SIDE_2;
- } else if (is_cmarker(buf.buf, '>', marker_size, 1)) {
+ } else if (is_cmarker(buf.buf, '>', marker_size)) {
if (hunk != RR_SIDE_2)
goto bad;
if (strbuf_cmp(&one, &two) > 0)
@@ -229,6 +319,10 @@ static int handle_path(unsigned char *sha1, struct rerere_io *io, int marker_siz
return hunk_no;
}
+/*
+ * Scan the path for conflicts, do the "handle_path()" thing above, and
+ * return the number of conflict hunks found.
+ */
static int handle_file(const char *path, unsigned char *sha1, const char *output)
{
int hunk_no = 0;
@@ -270,11 +364,18 @@ static int handle_file(const char *path, unsigned char *sha1, const char *output
return hunk_no;
}
+/*
+ * Subclass of rerere_io that reads from an in-core buffer that is a
+ * strbuf
+ */
struct rerere_io_mem {
struct rerere_io io;
struct strbuf input;
};
+/*
+ * ... and its getline() method implementation
+ */
static int rerere_mem_getline(struct strbuf *sb, struct rerere_io *io_)
{
struct rerere_io_mem *io = (struct rerere_io_mem *)io_;
@@ -313,24 +414,23 @@ static int handle_cache(const char *path, unsigned char *sha1, const char *outpu
return -1;
pos = -pos - 1;
- for (i = 0; i < 3; i++) {
+ while (pos < active_nr) {
enum object_type type;
unsigned long size;
- int j;
- if (active_nr <= pos)
- break;
ce = active_cache[pos++];
if (ce_namelen(ce) != len || memcmp(ce->name, path, len))
- continue;
- j = ce_stage(ce) - 1;
- mmfile[j].ptr = read_sha1_file(ce->sha1, &type, &size);
- mmfile[j].size = size;
+ break;
+ i = ce_stage(ce) - 1;
+ if (!mmfile[i].ptr) {
+ mmfile[i].ptr = read_sha1_file(ce->sha1, &type, &size);
+ mmfile[i].size = size;
+ }
}
- for (i = 0; i < 3; i++) {
+ for (i = 0; i < 3; i++)
if (!mmfile[i].ptr && !mmfile[i].size)
mmfile[i].ptr = xstrdup("");
- }
+
/*
* NEEDSWORK: handle conflicts from merges with
* merge.renormalize set, too
@@ -350,6 +450,10 @@ static int handle_cache(const char *path, unsigned char *sha1, const char *outpu
strbuf_init(&io.input, 0);
strbuf_attach(&io.input, result.ptr, result.size, result.size);
+ /*
+ * Grab the conflict ID and optionally write the original
+ * contents with conflict markers out.
+ */
hunk_no = handle_path(sha1, (struct rerere_io *)&io, marker_size);
strbuf_release(&io.input);
if (io.io.output)
@@ -357,6 +461,14 @@ static int handle_cache(const char *path, unsigned char *sha1, const char *outpu
return hunk_no;
}
+/*
+ * Look at a cache entry at "i" and see if it is not conflicting,
+ * conflicting and we are willing to handle, or conflicting and
+ * we are unable to handle, and return the determination in *type.
+ * Return the cache index to be looked at next, by skipping the
+ * stages we have already looked at in this invocation of this
+ * function.
+ */
static int check_one_conflict(int i, int *type)
{
const struct cache_entry *e = active_cache[i];
@@ -367,10 +479,8 @@ static int check_one_conflict(int i, int *type)
}
*type = PUNTED;
- if (ce_stage(e) == 1) {
- if (active_nr <= ++i)
- return i + 1;
- }
+ while (ce_stage(active_cache[i]) == 1)
+ i++;
/* Only handle regular files with both stages #2 and #3 */
if (i + 1 < active_nr) {
@@ -390,6 +500,17 @@ static int check_one_conflict(int i, int *type)
return i;
}
+/*
+ * Scan the index and find paths that have conflicts that rerere can
+ * handle, i.e. the ones that has both stages #2 and #3.
+ *
+ * NEEDSWORK: we do not record or replay a previous "resolve by
+ * deletion" for a delete-modify conflict, as that is inherently risky
+ * without knowing what modification is being discarded. The only
+ * safe case, i.e. both side doing the deletion and modification that
+ * are identical to the previous round, might want to be handled,
+ * though.
+ */
static int find_conflict(struct string_list *conflict)
{
int i;
@@ -406,6 +527,21 @@ static int find_conflict(struct string_list *conflict)
return 0;
}
+/*
+ * The merge_rr list is meant to hold outstanding conflicted paths
+ * that rerere could handle. Abuse the list by adding other types of
+ * entries to allow the caller to show "rerere remaining".
+ *
+ * - Conflicted paths that rerere does not handle are added
+ * - Conflicted paths that have been resolved are marked as such
+ * by storing RERERE_RESOLVED to .util field (where conflict ID
+ * is expected to be stored).
+ *
+ * Do *not* write MERGE_RR file out after calling this function.
+ *
+ * NEEDSWORK: we may want to fix the caller that implements "rerere
+ * remaining" to do this without abusing merge_rr.
+ */
int rerere_remaining(struct string_list *merge_rr)
{
int i;
@@ -424,7 +560,7 @@ int rerere_remaining(struct string_list *merge_rr)
struct string_list_item *it;
it = string_list_lookup(merge_rr, (const char *)e->name);
if (it != NULL) {
- free(it->util);
+ free_rerere_id(it);
it->util = RERERE_RESOLVED;
}
}
@@ -432,39 +568,66 @@ int rerere_remaining(struct string_list *merge_rr)
return 0;
}
-static int merge(const char *name, const char *path)
+/*
+ * Find the conflict identified by "id"; the change between its
+ * "preimage" (i.e. a previous contents with conflict markers) and its
+ * "postimage" (i.e. the corresponding contents with conflicts
+ * resolved) may apply cleanly to the contents stored in "path", i.e.
+ * the conflict this time around.
+ *
+ * Returns 0 for successful replay of recorded resolution, or non-zero
+ * for failure.
+ */
+static int merge(const struct rerere_id *id, const char *path)
{
+ FILE *f;
int ret;
mmfile_t cur = {NULL, 0}, base = {NULL, 0}, other = {NULL, 0};
mmbuffer_t result = {NULL, 0};
- if (handle_file(path, NULL, rerere_path(name, "thisimage")) < 0)
- return 1;
+ /*
+ * Normalize the conflicts in path and write it out to
+ * "thisimage" temporary file.
+ */
+ if (handle_file(path, NULL, rerere_path(id, "thisimage")) < 0) {
+ ret = 1;
+ goto out;
+ }
- if (read_mmfile(&cur, rerere_path(name, "thisimage")) ||
- read_mmfile(&base, rerere_path(name, "preimage")) ||
- read_mmfile(&other, rerere_path(name, "postimage"))) {
+ if (read_mmfile(&cur, rerere_path(id, "thisimage")) ||
+ read_mmfile(&base, rerere_path(id, "preimage")) ||
+ read_mmfile(&other, rerere_path(id, "postimage"))) {
ret = 1;
goto out;
}
+
+ /*
+ * A three-way merge. Note that this honors user-customizable
+ * low-level merge driver settings.
+ */
ret = ll_merge(&result, path, &base, NULL, &cur, "", &other, "", NULL);
- if (!ret) {
- FILE *f;
-
- if (utime(rerere_path(name, "postimage"), NULL) < 0)
- warning("failed utime() on %s: %s",
- rerere_path(name, "postimage"),
- strerror(errno));
- f = fopen(path, "w");
- if (!f)
- return error("Could not open %s: %s", path,
- strerror(errno));
- if (fwrite(result.ptr, result.size, 1, f) != 1)
- error("Could not write %s: %s", path, strerror(errno));
- if (fclose(f))
- return error("Writing %s failed: %s", path,
- strerror(errno));
- }
+ if (ret)
+ goto out;
+
+ /*
+ * A successful replay of recorded resolution.
+ * Mark that "postimage" was used to help gc.
+ */
+ if (utime(rerere_path(id, "postimage"), NULL) < 0)
+ warning("failed utime() on %s: %s",
+ rerere_path(id, "postimage"),
+ strerror(errno));
+
+ /* Update "path" with the resolution */
+ f = fopen(path, "w");
+ if (!f)
+ return error("Could not open %s: %s", path,
+ strerror(errno));
+ if (fwrite(result.ptr, result.size, 1, f) != 1)
+ error("Could not write %s: %s", path, strerror(errno));
+ if (fclose(f))
+ return error("Writing %s failed: %s", path,
+ strerror(errno));
out:
free(cur.ptr);
@@ -487,6 +650,8 @@ static void update_paths(struct string_list *update)
struct string_list_item *item = &update->items[i];
if (add_file_to_cache(item->string, 0))
exit(128);
+ fprintf(stderr, "Staged '%s' using previous resolution.\n",
+ item->string);
}
if (active_cache_changed) {
@@ -496,6 +661,41 @@ static void update_paths(struct string_list *update)
rollback_lock_file(&index_lock);
}
+/*
+ * The path indicated by rr_item may still have conflict for which we
+ * have a recorded resolution, in which case replay it and optionally
+ * update it. Or it may have been resolved by the user and we may
+ * only have the preimage for that conflict, in which case the result
+ * needs to be recorded as a resolution in a postimage file.
+ */
+static void do_rerere_one_path(struct string_list_item *rr_item,
+ struct string_list *update)
+{
+ const char *path = rr_item->string;
+ const struct rerere_id *id = rr_item->util;
+
+ /* Is there a recorded resolution we could attempt to apply? */
+ if (has_rerere_resolution(id)) {
+ if (merge(id, path))
+ return; /* failed to replay */
+
+ if (rerere_autoupdate)
+ string_list_insert(update, path);
+ else
+ fprintf(stderr,
+ "Resolved '%s' using previous resolution.\n",
+ path);
+ } else if (!handle_file(path, NULL, NULL)) {
+ /* The user has resolved it. */
+ copy_file(rerere_path(id, "postimage"), path, 0666);
+ fprintf(stderr, "Recorded resolution for '%s'.\n", path);
+ } else {
+ return;
+ }
+ free_rerere_id(rr_item);
+ rr_item->util = NULL;
+}
+
static int do_plain_rerere(struct string_list *rr, int fd)
{
struct string_list conflict = STRING_LIST_INIT_DUP;
@@ -505,65 +705,55 @@ static int do_plain_rerere(struct string_list *rr, int fd)
find_conflict(&conflict);
/*
- * MERGE_RR records paths with conflicts immediately after merge
- * failed. Some of the conflicted paths might have been hand resolved
- * in the working tree since then, but the initial run would catch all
- * and register their preimages.
+ * MERGE_RR records paths with conflicts immediately after
+ * merge failed. Some of the conflicted paths might have been
+ * hand resolved in the working tree since then, but the
+ * initial run would catch all and register their preimages.
*/
-
for (i = 0; i < conflict.nr; i++) {
+ struct rerere_id *id;
+ unsigned char sha1[20];
const char *path = conflict.items[i].string;
- if (!string_list_has_string(rr, path)) {
- unsigned char sha1[20];
- char *hex;
- int ret;
- ret = handle_file(path, sha1, NULL);
- if (ret < 1)
- continue;
- hex = xstrdup(sha1_to_hex(sha1));
- string_list_insert(rr, path)->util = hex;
- if (mkdir_in_gitdir(git_path("rr-cache/%s", hex)))
- continue;
- handle_file(path, NULL, rerere_path(hex, "preimage"));
- fprintf(stderr, "Recorded preimage for '%s'\n", path);
- }
- }
+ int ret;
- /*
- * Now some of the paths that had conflicts earlier might have been
- * hand resolved. Others may be similar to a conflict already that
- * was resolved before.
- */
+ if (string_list_has_string(rr, path))
+ continue;
- for (i = 0; i < rr->nr; i++) {
- int ret;
- const char *path = rr->items[i].string;
- const char *name = (const char *)rr->items[i].util;
-
- if (has_rerere_resolution(name)) {
- if (!merge(name, path)) {
- const char *msg;
- if (rerere_autoupdate) {
- string_list_insert(&update, path);
- msg = "Staged '%s' using previous resolution.\n";
- } else
- msg = "Resolved '%s' using previous resolution.\n";
- fprintf(stderr, msg, path);
- goto mark_resolved;
- }
- }
+ /*
+ * Ask handle_file() to scan and assign a
+ * conflict ID. No need to write anything out
+ * yet.
+ */
+ ret = handle_file(path, sha1, NULL);
+ if (ret < 1)
+ continue;
- /* Let's see if we have resolved it. */
- ret = handle_file(path, NULL, NULL);
- if (ret)
+ id = new_rerere_id(sha1);
+ string_list_insert(rr, path)->util = id;
+
+ /*
+ * If the directory does not exist, create
+ * it. mkdir_in_gitdir() will fail with
+ * EEXIST if there already is one.
+ *
+ * NEEDSWORK: make sure "gc" does not remove
+ * preimage without removing the directory.
+ */
+ if (mkdir_in_gitdir(rerere_path(id, NULL)))
continue;
- fprintf(stderr, "Recorded resolution for '%s'.\n", path);
- copy_file(rerere_path(name, "postimage"), path, 0666);
- mark_resolved:
- rr->items[i].util = NULL;
+ /*
+ * We are the first to encounter this
+ * conflict. Ask handle_file() to write the
+ * normalized contents to the "preimage" file.
+ */
+ handle_file(path, NULL, rerere_path(id, "preimage"));
+ fprintf(stderr, "Recorded preimage for '%s'\n", path);
}
+ for (i = 0; i < rr->nr; i++)
+ do_rerere_one_path(&rr->items[i], &update);
+
if (update.nr)
update_paths(&update);
@@ -614,6 +804,11 @@ int setup_rerere(struct string_list *merge_rr, int flags)
return fd;
}
+/*
+ * The main entry point that is called internally from codepaths that
+ * perform mergy operations, possibly leaving conflicted index entries
+ * and working tree files.
+ */
int rerere(int flags)
{
struct string_list merge_rr = STRING_LIST_INIT_DUP;
@@ -628,25 +823,42 @@ int rerere(int flags)
static int rerere_forget_one_path(const char *path, struct string_list *rr)
{
const char *filename;
- char *hex;
+ struct rerere_id *id;
unsigned char sha1[20];
int ret;
+ struct string_list_item *item;
+ /*
+ * Recreate the original conflict from the stages in the
+ * index and compute the conflict ID
+ */
ret = handle_cache(path, sha1, NULL);
if (ret < 1)
return error("Could not parse conflict hunks in '%s'", path);
- hex = xstrdup(sha1_to_hex(sha1));
- filename = rerere_path(hex, "postimage");
+
+ /* Nuke the recorded resolution for the conflict */
+ id = new_rerere_id(sha1);
+ filename = rerere_path(id, "postimage");
if (unlink(filename))
return (errno == ENOENT
? error("no remembered resolution for %s", path)
: error("cannot unlink %s: %s", filename, strerror(errno)));
- handle_cache(path, sha1, rerere_path(hex, "preimage"));
+ /*
+ * Update the preimage so that the user can resolve the
+ * conflict in the working tree, run us again to record
+ * the postimage.
+ */
+ handle_cache(path, sha1, rerere_path(id, "preimage"));
fprintf(stderr, "Updated preimage for '%s'\n", path);
-
- string_list_insert(rr, path)->util = hex;
+ /*
+ * And remember that we can record resolution for this
+ * conflict when the user is done.
+ */
+ item = string_list_insert(rr, path);
+ free_rerere_id(item);
+ item->util = id;
fprintf(stderr, "Forgot resolution for %s\n", path);
return 0;
}
@@ -664,6 +876,11 @@ int rerere_forget(struct pathspec *pathspec)
if (fd < 0)
return 0;
+ /*
+ * The paths may have been resolved (incorrectly);
+ * recover the original conflicted state and then
+ * find the conflicted paths.
+ */
unmerge_cache(pathspec);
find_conflict(&conflict);
for (i = 0; i < conflict.nr; i++) {
@@ -676,24 +893,51 @@ int rerere_forget(struct pathspec *pathspec)
return write_rr(&merge_rr, fd);
}
-static time_t rerere_created_at(const char *name)
+/*
+ * Garbage collection support
+ */
+
+/*
+ * Note that this is not reentrant but is used only one-at-a-time
+ * so it does not matter right now.
+ */
+static struct rerere_id *dirname_to_id(const char *name)
+{
+ static struct rerere_id id;
+ strcpy(id.hex, name);
+ return &id;
+}
+
+static time_t rerere_created_at(const char *dir_name)
{
struct stat st;
- return stat(rerere_path(name, "preimage"), &st) ? (time_t) 0 : st.st_mtime;
+ struct rerere_id *id = dirname_to_id(dir_name);
+
+ return stat(rerere_path(id, "preimage"), &st) ? (time_t) 0 : st.st_mtime;
}
-static time_t rerere_last_used_at(const char *name)
+static time_t rerere_last_used_at(const char *dir_name)
{
struct stat st;
- return stat(rerere_path(name, "postimage"), &st) ? (time_t) 0 : st.st_mtime;
+ struct rerere_id *id = dirname_to_id(dir_name);
+
+ return stat(rerere_path(id, "postimage"), &st) ? (time_t) 0 : st.st_mtime;
}
-static void unlink_rr_item(const char *name)
+/*
+ * Remove the recorded resolution for a given conflict ID
+ */
+static void unlink_rr_item(struct rerere_id *id)
{
- unlink(rerere_path(name, "thisimage"));
- unlink(rerere_path(name, "preimage"));
- unlink(rerere_path(name, "postimage"));
- rmdir(git_path("rr-cache/%s", name));
+ unlink(rerere_path(id, "thisimage"));
+ unlink(rerere_path(id, "preimage"));
+ unlink(rerere_path(id, "postimage"));
+ /*
+ * NEEDSWORK: what if this rmdir() fails? Wouldn't we then
+ * assume that we already have preimage recorded in
+ * do_plain_rerere()?
+ */
+ rmdir(rerere_path(id, NULL));
}
void rerere_gc(struct string_list *rr)
@@ -715,6 +959,7 @@ void rerere_gc(struct string_list *rr)
dir = opendir(git_path("rr-cache"));
if (!dir)
die_errno("unable to open rr-cache directory");
+ /* Collect stale conflict IDs ... */
while ((e = readdir(dir))) {
if (is_dot_or_dotdot(e->d_name))
continue;
@@ -732,12 +977,20 @@ void rerere_gc(struct string_list *rr)
string_list_append(&to_remove, e->d_name);
}
closedir(dir);
+ /* ... and then remove them one-by-one */
for (i = 0; i < to_remove.nr; i++)
- unlink_rr_item(to_remove.items[i].string);
+ unlink_rr_item(dirname_to_id(to_remove.items[i].string));
string_list_clear(&to_remove, 0);
rollback_lock_file(&write_lock);
}
+/*
+ * During a conflict resolution, after "rerere" recorded the
+ * preimages, abandon them if the user did not resolve them or
+ * record their resolutions. And drop $GIT_DIR/MERGE_RR.
+ *
+ * NEEDSWORK: shouldn't we be calling this from "reset --hard"?
+ */
void rerere_clear(struct string_list *merge_rr)
{
int i;
@@ -746,9 +999,9 @@ void rerere_clear(struct string_list *merge_rr)
return;
for (i = 0; i < merge_rr->nr; i++) {
- const char *name = (const char *)merge_rr->items[i].util;
- if (!has_rerere_resolution(name))
- unlink_rr_item(name);
+ struct rerere_id *id = merge_rr->items[i].util;
+ if (!has_rerere_resolution(id))
+ unlink_rr_item(id);
}
unlink_or_warn(git_path_merge_rr());
rollback_lock_file(&write_lock);
diff --git a/rerere.h b/rerere.h
index 407d59996d..1222e91921 100644
--- a/rerere.h
+++ b/rerere.h
@@ -16,9 +16,19 @@ struct pathspec;
*/
extern void *RERERE_RESOLVED;
+struct rerere_id {
+ char hex[41];
+};
+
extern int setup_rerere(struct string_list *, int);
extern int rerere(int);
-extern const char *rerere_path(const char *hex, const char *file);
+/*
+ * Given the conflict ID and the name of a "file" used for replaying
+ * the recorded resolution (e.g. "preimage", "postimage"), return the
+ * path to that filesystem entity. With "file" specified with NULL,
+ * return the path to the directory that houses these files.
+ */
+extern const char *rerere_path(const struct rerere_id *, const char *file);
extern int rerere_forget(struct pathspec *);
extern int rerere_remaining(struct string_list *);
extern void rerere_clear(struct string_list *);
diff --git a/revision.c b/revision.c
index af2a18ed74..22364636d1 100644
--- a/revision.c
+++ b/revision.c
@@ -38,7 +38,7 @@ char *path_name(const struct name_path *path, const char *name)
}
n = xmalloc(len);
m = n + len - (nlen + 1);
- strcpy(m, name);
+ memcpy(m, name, nlen + 1);
for (p = path; p; p = p->up) {
if (p->elem_len) {
m -= p->elem_len + 1;
diff --git a/run-command.c b/run-command.c
index e09275bd9e..e17e456cda 100644
--- a/run-command.c
+++ b/run-command.c
@@ -604,7 +604,7 @@ static NORETURN void die_async(const char *err, va_list params)
{
vreportf("fatal: ", err, params);
- if (!pthread_equal(main_thread, pthread_self())) {
+ if (in_async()) {
struct async *async = pthread_getspecific(async_key);
if (async->proc_in >= 0)
close(async->proc_in);
@@ -623,6 +623,13 @@ static int async_die_is_recursing(void)
return ret != NULL;
}
+int in_async(void)
+{
+ if (!main_thread_set)
+ return 0; /* no asyncs started yet */
+ return !pthread_equal(main_thread, pthread_self());
+}
+
#else
static struct {
@@ -662,6 +669,12 @@ int git_atexit(void (*handler)(void))
}
#define atexit git_atexit
+static int process_is_async;
+int in_async(void)
+{
+ return process_is_async;
+}
+
#endif
int start_async(struct async *async)
@@ -721,6 +734,7 @@ int start_async(struct async *async)
if (need_out)
close(fdout[0]);
git_atexit_clear();
+ process_is_async = 1;
exit(!!async->proc(proc_in, proc_out, async->data));
}
diff --git a/run-command.h b/run-command.h
index 275d35c442..5428b048e2 100644
--- a/run-command.h
+++ b/run-command.h
@@ -119,5 +119,6 @@ struct async {
int start_async(struct async *async);
int finish_async(struct async *async);
+int in_async(void);
#endif
diff --git a/setup.c b/setup.c
index a17c51e61d..b264471691 100644
--- a/setup.c
+++ b/setup.c
@@ -99,10 +99,7 @@ char *prefix_path_gently(const char *prefix, int len,
return NULL;
}
} else {
- sanitized = xmalloc(len + strlen(path) + 1);
- if (len)
- memcpy(sanitized, prefix, len);
- strcpy(sanitized + len, path);
+ sanitized = xstrfmt("%.*s%s", len, prefix, path);
if (remaining_prefix)
*remaining_prefix = len;
if (normalize_path_copy_len(sanitized, sanitized, remaining_prefix)) {
@@ -229,14 +226,21 @@ void verify_non_filename(const char *prefix, const char *arg)
int get_common_dir(struct strbuf *sb, const char *gitdir)
{
+ const char *git_env_common_dir = getenv(GIT_COMMON_DIR_ENVIRONMENT);
+ if (git_env_common_dir) {
+ strbuf_addstr(sb, git_env_common_dir);
+ return 1;
+ } else {
+ return get_common_dir_noenv(sb, gitdir);
+ }
+}
+
+int get_common_dir_noenv(struct strbuf *sb, const char *gitdir)
+{
struct strbuf data = STRBUF_INIT;
struct strbuf path = STRBUF_INIT;
- const char *git_common_dir = getenv(GIT_COMMON_DIR_ENVIRONMENT);
int ret = 0;
- if (git_common_dir) {
- strbuf_addstr(sb, git_common_dir);
- return 1;
- }
+
strbuf_addf(&path, "%s/commondir", gitdir);
if (file_exists(path.buf)) {
if (strbuf_read_file(&data, path.buf, 0) <= 0)
@@ -468,11 +472,8 @@ const char *read_gitfile_gently(const char *path, int *return_error_code)
if (!is_absolute_path(dir) && (slash = strrchr(path, '/'))) {
size_t pathlen = slash+1 - path;
- size_t dirlen = pathlen + len - 8;
- dir = xmalloc(dirlen + 1);
- strncpy(dir, path, pathlen);
- strncpy(dir + pathlen, buf + 8, len - 8);
- dir[dirlen] = '\0';
+ dir = xstrfmt("%.*s%.*s", (int)pathlen, path,
+ (int)(len - 8), buf + 8);
free(buf);
buf = dir;
}
diff --git a/sha1_file.c b/sha1_file.c
index d295a3225a..50896ff1eb 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -208,44 +208,25 @@ const char *sha1_file_name(const unsigned char *sha1)
* provided by the caller. which should be "pack" or "idx".
*/
static char *sha1_get_pack_name(const unsigned char *sha1,
- char **name, char **base, const char *which)
+ struct strbuf *buf,
+ const char *which)
{
- static const char hex[] = "0123456789abcdef";
- char *buf;
- int i;
-
- if (!*base) {
- const char *sha1_file_directory = get_object_directory();
- int len = strlen(sha1_file_directory);
- *base = xmalloc(len + 60);
- sprintf(*base, "%s/pack/pack-1234567890123456789012345678901234567890.%s",
- sha1_file_directory, which);
- *name = *base + len + 11;
- }
-
- buf = *name;
-
- for (i = 0; i < 20; i++) {
- unsigned int val = *sha1++;
- *buf++ = hex[val >> 4];
- *buf++ = hex[val & 0xf];
- }
-
- return *base;
+ strbuf_reset(buf);
+ strbuf_addf(buf, "%s/pack/pack-%s.%s", get_object_directory(),
+ sha1_to_hex(sha1), which);
+ return buf->buf;
}
char *sha1_pack_name(const unsigned char *sha1)
{
- static char *name, *base;
-
- return sha1_get_pack_name(sha1, &name, &base, "pack");
+ static struct strbuf buf = STRBUF_INIT;
+ return sha1_get_pack_name(sha1, &buf, "pack");
}
char *sha1_pack_index_name(const unsigned char *sha1)
{
- static char *name, *base;
-
- return sha1_get_pack_name(sha1, &name, &base, "idx");
+ static struct strbuf buf = STRBUF_INIT;
+ return sha1_get_pack_name(sha1, &buf, "idx");
}
struct alternate_object_database *alt_odb_list;
@@ -671,13 +652,15 @@ static int check_packed_git_idx(const char *path, struct packed_git *p)
int open_pack_index(struct packed_git *p)
{
char *idx_name;
+ size_t len;
int ret;
if (p->index_data)
return 0;
- idx_name = xstrdup(p->pack_name);
- strcpy(idx_name + strlen(idx_name) - strlen(".pack"), ".idx");
+ if (!strip_suffix(p->pack_name, ".pack", &len))
+ die("BUG: pack_name does not end in .pack");
+ idx_name = xstrfmt("%.*s.idx", (int)len, p->pack_name);
ret = check_packed_git_idx(idx_name, p);
free(idx_name);
return ret;
@@ -786,6 +769,37 @@ void close_pack_windows(struct packed_git *p)
}
}
+static int close_pack_fd(struct packed_git *p)
+{
+ if (p->pack_fd < 0)
+ return 0;
+
+ close(p->pack_fd);
+ pack_open_fds--;
+ p->pack_fd = -1;
+
+ return 1;
+}
+
+static void close_pack(struct packed_git *p)
+{
+ close_pack_windows(p);
+ close_pack_fd(p);
+ close_pack_index(p);
+}
+
+void close_all_packs(void)
+{
+ struct packed_git *p;
+
+ for (p = packed_git; p; p = p->next)
+ if (p->do_not_close)
+ die("BUG! Want to close pack marked 'do-not-close'");
+ else
+ close_pack(p);
+}
+
+
/*
* The LRU pack is the one with the oldest MRU window, preferring packs
* with no used windows, or the oldest mtime if it has no windows allocated.
@@ -853,12 +867,8 @@ static int close_one_pack(void)
find_lru_pack(p, &lru_p, &mru_w, &accept_windows_inuse);
}
- if (lru_p) {
- close(lru_p->pack_fd);
- pack_open_fds--;
- lru_p->pack_fd = -1;
- return 1;
- }
+ if (lru_p)
+ return close_pack_fd(lru_p);
return 0;
}
@@ -898,12 +908,7 @@ void free_pack_by_name(const char *pack_name)
p = *pp;
if (strcmp(pack_name, p->pack_name) == 0) {
clear_delta_base_cache();
- close_pack_windows(p);
- if (p->pack_fd != -1) {
- close(p->pack_fd);
- pack_open_fds--;
- }
- close_pack_index(p);
+ close_pack(p);
free(p->bad_object_sha1);
*pp = p->next;
if (last_found_pack == p)
@@ -1037,11 +1042,7 @@ static int open_packed_git(struct packed_git *p)
{
if (!open_packed_git_1(p))
return 0;
- if (p->pack_fd != -1) {
- close(p->pack_fd);
- pack_open_fds--;
- p->pack_fd = -1;
- }
+ close_pack_fd(p);
return -1;
}
@@ -1107,11 +1108,8 @@ unsigned char *use_pack(struct packed_git *p,
p->pack_name,
strerror(errno));
if (!win->offset && win->len == p->pack_size
- && !p->do_not_close) {
- close(p->pack_fd);
- pack_open_fds--;
- p->pack_fd = -1;
- }
+ && !p->do_not_close)
+ close_pack_fd(p);
pack_mmap_calls++;
pack_open_windows++;
if (pack_mapped > peak_pack_mapped)
@@ -1146,11 +1144,12 @@ static void try_to_free_pack_memory(size_t size)
release_pack_memory(size);
}
-struct packed_git *add_packed_git(const char *path, int path_len, int local)
+struct packed_git *add_packed_git(const char *path, size_t path_len, int local)
{
static int have_set_try_to_free_routine;
struct stat st;
- struct packed_git *p = alloc_packed_git(path_len + 2);
+ size_t alloc;
+ struct packed_git *p;
if (!have_set_try_to_free_routine) {
have_set_try_to_free_routine = 1;
@@ -1161,18 +1160,22 @@ struct packed_git *add_packed_git(const char *path, int path_len, int local)
* Make sure a corresponding .pack file exists and that
* the index looks sane.
*/
- path_len -= strlen(".idx");
- if (path_len < 1) {
- free(p);
+ if (!strip_suffix_mem(path, &path_len, ".idx"))
return NULL;
- }
+
+ /*
+ * ".pack" is long enough to hold any suffix we're adding (and
+ * the use xsnprintf double-checks that)
+ */
+ alloc = path_len + strlen(".pack") + 1;
+ p = alloc_packed_git(alloc);
memcpy(p->pack_name, path, path_len);
- strcpy(p->pack_name + path_len, ".keep");
+ xsnprintf(p->pack_name + path_len, alloc - path_len, ".keep");
if (!access(p->pack_name, F_OK))
p->pack_keep = 1;
- strcpy(p->pack_name + path_len, ".pack");
+ xsnprintf(p->pack_name + path_len, alloc - path_len, ".pack");
if (stat(p->pack_name, &st) || !S_ISREG(st.st_mode)) {
free(p);
return NULL;
@@ -1192,9 +1195,10 @@ struct packed_git *add_packed_git(const char *path, int path_len, int local)
struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path)
{
const char *path = sha1_pack_name(sha1);
- struct packed_git *p = alloc_packed_git(strlen(path) + 1);
+ int alloc = strlen(path) + 1;
+ struct packed_git *p = alloc_packed_git(alloc);
- strcpy(p->pack_name, path);
+ memcpy(p->pack_name, path, alloc); /* includes NUL */
hashcpy(p->sha1, sha1);
if (check_packed_git_idx(idx_path, p)) {
free(p);
@@ -1464,7 +1468,7 @@ int check_sha1_signature(const unsigned char *sha1, void *map,
return -1;
/* Generate the header */
- hdrlen = sprintf(hdr, "%s %lu", typename(obj_type), size) + 1;
+ hdrlen = xsnprintf(hdr, sizeof(hdr), "%s %lu", typename(obj_type), size) + 1;
/* Sha1.. */
git_SHA1_Init(&c);
@@ -2930,7 +2934,7 @@ static void write_sha1_file_prepare(const void *buf, unsigned long len,
git_SHA_CTX c;
/* Generate the header */
- *hdrlen = sprintf(hdr, "%s %lu", type, len)+1;
+ *hdrlen = xsnprintf(hdr, *hdrlen, "%s %lu", type, len)+1;
/* Sha1.. */
git_SHA1_Init(&c);
@@ -2993,7 +2997,7 @@ int hash_sha1_file(const void *buf, unsigned long len, const char *type,
unsigned char *sha1)
{
char hdr[32];
- int hdrlen;
+ int hdrlen = sizeof(hdr);
write_sha1_file_prepare(buf, len, type, sha1, hdr, &hdrlen);
return 0;
}
@@ -3023,29 +3027,31 @@ static inline int directory_size(const char *filename)
* We want to avoid cross-directory filename renames, because those
* can have problems on various filesystems (FAT, NFS, Coda).
*/
-static int create_tmpfile(char *buffer, size_t bufsiz, const char *filename)
+static int create_tmpfile(struct strbuf *tmp, const char *filename)
{
int fd, dirlen = directory_size(filename);
- if (dirlen + 20 > bufsiz) {
- errno = ENAMETOOLONG;
- return -1;
- }
- memcpy(buffer, filename, dirlen);
- strcpy(buffer + dirlen, "tmp_obj_XXXXXX");
- fd = git_mkstemp_mode(buffer, 0444);
+ strbuf_reset(tmp);
+ strbuf_add(tmp, filename, dirlen);
+ strbuf_addstr(tmp, "tmp_obj_XXXXXX");
+ fd = git_mkstemp_mode(tmp->buf, 0444);
if (fd < 0 && dirlen && errno == ENOENT) {
- /* Make sure the directory exists */
- memcpy(buffer, filename, dirlen);
- buffer[dirlen-1] = 0;
- if (mkdir(buffer, 0777) && errno != EEXIST)
+ /*
+ * Make sure the directory exists; note that the contents
+ * of the buffer are undefined after mkstemp returns an
+ * error, so we have to rewrite the whole buffer from
+ * scratch.
+ */
+ strbuf_reset(tmp);
+ strbuf_add(tmp, filename, dirlen - 1);
+ if (mkdir(tmp->buf, 0777) && errno != EEXIST)
return -1;
- if (adjust_shared_perm(buffer))
+ if (adjust_shared_perm(tmp->buf))
return -1;
/* Try again */
- strcpy(buffer + dirlen - 1, "/tmp_obj_XXXXXX");
- fd = git_mkstemp_mode(buffer, 0444);
+ strbuf_addstr(tmp, "/tmp_obj_XXXXXX");
+ fd = git_mkstemp_mode(tmp->buf, 0444);
}
return fd;
}
@@ -3058,10 +3064,10 @@ static int write_loose_object(const unsigned char *sha1, char *hdr, int hdrlen,
git_zstream stream;
git_SHA_CTX c;
unsigned char parano_sha1[20];
- static char tmp_file[PATH_MAX];
+ static struct strbuf tmp_file = STRBUF_INIT;
const char *filename = sha1_file_name(sha1);
- fd = create_tmpfile(tmp_file, sizeof(tmp_file), filename);
+ fd = create_tmpfile(&tmp_file, filename);
if (fd < 0) {
if (errno == EACCES)
return error("insufficient permission for adding an object to repository database %s", get_object_directory());
@@ -3110,12 +3116,12 @@ static int write_loose_object(const unsigned char *sha1, char *hdr, int hdrlen,
struct utimbuf utb;
utb.actime = mtime;
utb.modtime = mtime;
- if (utime(tmp_file, &utb) < 0)
+ if (utime(tmp_file.buf, &utb) < 0)
warning("failed utime() on %s: %s",
- tmp_file, strerror(errno));
+ tmp_file.buf, strerror(errno));
}
- return finalize_object_file(tmp_file, filename);
+ return finalize_object_file(tmp_file.buf, filename);
}
static int freshen_loose_object(const unsigned char *sha1)
@@ -3139,7 +3145,7 @@ static int freshen_packed_object(const unsigned char *sha1)
int write_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *sha1)
{
char hdr[32];
- int hdrlen;
+ int hdrlen = sizeof(hdr);
/* Normally if we have it in the pack then we do not bother writing
* it out into .git/objects/??/?{38} file.
@@ -3157,7 +3163,8 @@ int hash_sha1_file_literally(const void *buf, unsigned long len, const char *typ
int hdrlen, status = 0;
/* type string, SP, %lu of the length plus NUL must fit this */
- header = xmalloc(strlen(type) + 32);
+ hdrlen = strlen(type) + 32;
+ header = xmalloc(hdrlen);
write_sha1_file_prepare(buf, len, type, sha1, header, &hdrlen);
if (!(flags & HASH_WRITE_OBJECT))
@@ -3185,7 +3192,7 @@ int force_object_loose(const unsigned char *sha1, time_t mtime)
buf = read_packed_sha1(sha1, &type, &len);
if (!buf)
return error("cannot read sha1_file for %s", sha1_to_hex(sha1));
- hdrlen = sprintf(hdr, "%s %lu", typename(type), len) + 1;
+ hdrlen = xsnprintf(hdr, sizeof(hdr), "%s %lu", typename(type), len) + 1;
ret = write_loose_object(sha1, hdr, hdrlen, buf, len, mtime);
free(buf);
diff --git a/sha1_name.c b/sha1_name.c
index da6874c15e..3242c5ea46 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -96,11 +96,15 @@ static void find_short_object_filename(int len, const char *hex_pfx, struct disa
}
fakeent->next = alt_odb_list;
- sprintf(hex, "%.2s", hex_pfx);
+ xsnprintf(hex, sizeof(hex), "%.2s", hex_pfx);
for (alt = fakeent; alt && !ds->ambiguous; alt = alt->next) {
struct dirent *de;
DIR *dir;
- sprintf(alt->name, "%.2s/", hex_pfx);
+ /*
+ * every alt_odb struct has 42 extra bytes after the base
+ * for exactly this purpose
+ */
+ xsnprintf(alt->name, 42, "%.2s/", hex_pfx);
dir = opendir(alt->base);
if (!dir)
continue;
@@ -368,14 +372,13 @@ int for_each_abbrev(const char *prefix, each_abbrev_fn fn, void *cb_data)
return ds.ambiguous;
}
-const char *find_unique_abbrev(const unsigned char *sha1, int len)
+int find_unique_abbrev_r(char *hex, const unsigned char *sha1, int len)
{
int status, exists;
- static char hex[41];
- memcpy(hex, sha1_to_hex(sha1), 40);
+ sha1_to_hex_r(hex, sha1);
if (len == 40 || !len)
- return hex;
+ return 40;
exists = has_sha1_file(sha1);
while (len < 40) {
unsigned char sha1_ret[20];
@@ -384,10 +387,17 @@ const char *find_unique_abbrev(const unsigned char *sha1, int len)
? !status
: status == SHORT_NAME_NOT_FOUND) {
hex[len] = 0;
- return hex;
+ return len;
}
len++;
}
+ return len;
+}
+
+const char *find_unique_abbrev(const unsigned char *sha1, int len)
+{
+ static char hex[GIT_SHA1_HEXSZ + 1];
+ find_unique_abbrev_r(hex, sha1, len);
return hex;
}
@@ -1283,8 +1293,7 @@ static void diagnose_invalid_index_path(int stage,
const struct cache_entry *ce;
int pos;
unsigned namelen = strlen(filename);
- unsigned fullnamelen;
- char *fullname;
+ struct strbuf fullname = STRBUF_INIT;
if (!prefix)
prefix = "";
@@ -1304,21 +1313,19 @@ static void diagnose_invalid_index_path(int stage,
}
/* Confusion between relative and absolute filenames? */
- fullnamelen = namelen + strlen(prefix);
- fullname = xmalloc(fullnamelen + 1);
- strcpy(fullname, prefix);
- strcat(fullname, filename);
- pos = cache_name_pos(fullname, fullnamelen);
+ strbuf_addstr(&fullname, prefix);
+ strbuf_addstr(&fullname, filename);
+ pos = cache_name_pos(fullname.buf, fullname.len);
if (pos < 0)
pos = -pos - 1;
if (pos < active_nr) {
ce = active_cache[pos];
- if (ce_namelen(ce) == fullnamelen &&
- !memcmp(ce->name, fullname, fullnamelen))
+ if (ce_namelen(ce) == fullname.len &&
+ !memcmp(ce->name, fullname.buf, fullname.len))
die("Path '%s' is in the index, but not '%s'.\n"
"Did you mean ':%d:%s' aka ':%d:./%s'?",
- fullname, filename,
- ce_stage(ce), fullname,
+ fullname.buf, filename,
+ ce_stage(ce), fullname.buf,
ce_stage(ce), filename);
}
@@ -1328,7 +1335,7 @@ static void diagnose_invalid_index_path(int stage,
die("Path '%s' does not exist (neither on disk nor in the index).",
filename);
- free(fullname);
+ strbuf_release(&fullname);
}
diff --git a/sideband.c b/sideband.c
index 7f9dc229fb..fde8adc000 100644
--- a/sideband.c
+++ b/sideband.c
@@ -137,11 +137,11 @@ ssize_t send_sideband(int fd, int band, const char *data, ssize_t sz, int packet
if (packet_max - 5 < n)
n = packet_max - 5;
if (0 <= band) {
- sprintf(hdr, "%04x", n + 5);
+ xsnprintf(hdr, sizeof(hdr), "%04x", n + 5);
hdr[4] = band;
write_or_die(fd, hdr, 5);
} else {
- sprintf(hdr, "%04x", n + 4);
+ xsnprintf(hdr, sizeof(hdr), "%04x", n + 4);
write_or_die(fd, hdr, 4);
}
write_or_die(fd, p, n);
diff --git a/strbuf.c b/strbuf.c
index 29df55b1b0..107c45d291 100644
--- a/strbuf.c
+++ b/strbuf.c
@@ -245,8 +245,8 @@ void strbuf_add_commented_lines(struct strbuf *out, const char *buf, size_t size
static char prefix2[2];
if (prefix1[0] != comment_line_char) {
- sprintf(prefix1, "%c ", comment_line_char);
- sprintf(prefix2, "%c", comment_line_char);
+ xsnprintf(prefix1, sizeof(prefix1), "%c ", comment_line_char);
+ xsnprintf(prefix2, sizeof(prefix2), "%c", comment_line_char);
}
add_lines(out, prefix1, prefix2, buf, size);
}
@@ -743,3 +743,12 @@ void strbuf_addftime(struct strbuf *sb, const char *fmt, const struct tm *tm)
}
strbuf_setlen(sb, sb->len + len);
}
+
+void strbuf_add_unique_abbrev(struct strbuf *sb, const unsigned char *sha1,
+ int abbrev_len)
+{
+ int r;
+ strbuf_grow(sb, GIT_SHA1_HEXSZ + 1);
+ r = find_unique_abbrev_r(sb->buf + sb->len, sha1, abbrev_len);
+ strbuf_setlen(sb, sb->len + r);
+}
diff --git a/strbuf.h b/strbuf.h
index aef2794651..0f9c8a72ba 100644
--- a/strbuf.h
+++ b/strbuf.h
@@ -475,6 +475,14 @@ static inline struct strbuf **strbuf_split(const struct strbuf *sb,
extern void strbuf_list_free(struct strbuf **);
/**
+ * Add the abbreviation, as generated by find_unique_abbrev, of `sha1` to
+ * the strbuf `sb`.
+ */
+extern void strbuf_add_unique_abbrev(struct strbuf *sb,
+ const unsigned char *sha1,
+ int abbrev_len);
+
+/**
* Launch the user preferred editor to edit a file and fill the buffer
* with the file's contents upon the user completing their editing. The
* third argument can be used to set the environment which the editor is
@@ -491,10 +499,21 @@ extern void strbuf_add_lines(struct strbuf *sb, const char *prefix, const char *
*/
extern void strbuf_addstr_xml_quoted(struct strbuf *sb, const char *s);
+/**
+ * "Complete" the contents of `sb` by ensuring that either it ends with the
+ * character `term`, or it is empty. This can be used, for example,
+ * to ensure that text ends with a newline, but without creating an empty
+ * blank line if there is no content in the first place.
+ */
+static inline void strbuf_complete(struct strbuf *sb, char term)
+{
+ if (sb->len && sb->buf[sb->len - 1] != term)
+ strbuf_addch(sb, term);
+}
+
static inline void strbuf_complete_line(struct strbuf *sb)
{
- if (sb->len && sb->buf[sb->len - 1] != '\n')
- strbuf_addch(sb, '\n');
+ strbuf_complete(sb, '\n');
}
extern int strbuf_branchname(struct strbuf *sb, const char *name);
diff --git a/submodule-config.c b/submodule-config.c
index 393de5357e..afe0ea8156 100644
--- a/submodule-config.c
+++ b/submodule-config.c
@@ -257,78 +257,62 @@ static int parse_config(const char *var, const char *value, void *data)
if (!name_and_item_from_var(var, &name, &item))
return 0;
- submodule = lookup_or_create_by_name(me->cache, me->gitmodules_sha1,
- name.buf);
+ submodule = lookup_or_create_by_name(me->cache,
+ me->gitmodules_sha1,
+ name.buf);
if (!strcmp(item.buf, "path")) {
- struct strbuf path = STRBUF_INIT;
- if (!value) {
+ if (!value)
ret = config_error_nonbool(var);
- goto release_return;
- }
- if (!me->overwrite && submodule->path != NULL) {
+ else if (!me->overwrite && submodule->path != NULL)
warn_multiple_config(me->commit_sha1, submodule->name,
"path");
- goto release_return;
+ else {
+ if (submodule->path)
+ cache_remove_path(me->cache, submodule);
+ free((void *) submodule->path);
+ submodule->path = xstrdup(value);
+ cache_put_path(me->cache, submodule);
}
-
- if (submodule->path)
- cache_remove_path(me->cache, submodule);
- free((void *) submodule->path);
- strbuf_addstr(&path, value);
- submodule->path = strbuf_detach(&path, NULL);
- cache_put_path(me->cache, submodule);
} else if (!strcmp(item.buf, "fetchrecursesubmodules")) {
/* when parsing worktree configurations we can die early */
int die_on_error = is_null_sha1(me->gitmodules_sha1);
if (!me->overwrite &&
- submodule->fetch_recurse != RECURSE_SUBMODULES_NONE) {
+ submodule->fetch_recurse != RECURSE_SUBMODULES_NONE)
warn_multiple_config(me->commit_sha1, submodule->name,
"fetchrecursesubmodules");
- goto release_return;
- }
-
- submodule->fetch_recurse = parse_fetch_recurse(var, value,
+ else
+ submodule->fetch_recurse = parse_fetch_recurse(
+ var, value,
die_on_error);
} else if (!strcmp(item.buf, "ignore")) {
- struct strbuf ignore = STRBUF_INIT;
- if (!me->overwrite && submodule->ignore != NULL) {
+ if (!value)
+ ret = config_error_nonbool(var);
+ else if (!me->overwrite && submodule->ignore != NULL)
warn_multiple_config(me->commit_sha1, submodule->name,
"ignore");
- goto release_return;
- }
- if (!value) {
- ret = config_error_nonbool(var);
- goto release_return;
- }
- if (strcmp(value, "untracked") && strcmp(value, "dirty") &&
- strcmp(value, "all") && strcmp(value, "none")) {
+ else if (strcmp(value, "untracked") &&
+ strcmp(value, "dirty") &&
+ strcmp(value, "all") &&
+ strcmp(value, "none"))
warning("Invalid parameter '%s' for config option "
"'submodule.%s.ignore'", value, var);
- goto release_return;
+ else {
+ free((void *) submodule->ignore);
+ submodule->ignore = xstrdup(value);
}
-
- free((void *) submodule->ignore);
- strbuf_addstr(&ignore, value);
- submodule->ignore = strbuf_detach(&ignore, NULL);
} else if (!strcmp(item.buf, "url")) {
- struct strbuf url = STRBUF_INIT;
if (!value) {
ret = config_error_nonbool(var);
- goto release_return;
- }
- if (!me->overwrite && submodule->url != NULL) {
+ } else if (!me->overwrite && submodule->url != NULL) {
warn_multiple_config(me->commit_sha1, submodule->name,
"url");
- goto release_return;
+ } else {
+ free((void *) submodule->url);
+ submodule->url = xstrdup(value);
}
-
- free((void *) submodule->url);
- strbuf_addstr(&url, value);
- submodule->url = strbuf_detach(&url, NULL);
}
-release_return:
strbuf_release(&name);
strbuf_release(&item);
diff --git a/submodule.c b/submodule.c
index 245ed4dfbb..5879cfb158 100644
--- a/submodule.c
+++ b/submodule.c
@@ -122,15 +122,9 @@ static int add_submodule_odb(const char *path)
struct strbuf objects_directory = STRBUF_INIT;
struct alternate_object_database *alt_odb;
int ret = 0;
- const char *git_dir;
+ int alloc;
- strbuf_addf(&objects_directory, "%s/.git", path);
- git_dir = read_gitfile(objects_directory.buf);
- if (git_dir) {
- strbuf_reset(&objects_directory);
- strbuf_addstr(&objects_directory, git_dir);
- }
- strbuf_addstr(&objects_directory, "/objects/");
+ strbuf_git_path_submodule(&objects_directory, path, "objects/");
if (!is_directory(objects_directory.buf)) {
ret = -1;
goto done;
@@ -142,9 +136,10 @@ static int add_submodule_odb(const char *path)
objects_directory.len))
goto done;
- alt_odb = xmalloc(objects_directory.len + 42 + sizeof(*alt_odb));
+ alloc = objects_directory.len + 42; /* for "12/345..." sha1 */
+ alt_odb = xmalloc(sizeof(*alt_odb) + alloc);
alt_odb->next = alt_odb_list;
- strcpy(alt_odb->base, objects_directory.buf);
+ xsnprintf(alt_odb->base, alloc, "%s", objects_directory.buf);
alt_odb->name = alt_odb->base + objects_directory.len;
alt_odb->name[2] = '/';
alt_odb->name[40] = '\0';
diff --git a/t/t0001-init.sh b/t/t0001-init.sh
index 7de8d85ee8..f91bbcfc85 100755
--- a/t/t0001-init.sh
+++ b/t/t0001-init.sh
@@ -202,8 +202,8 @@ test_expect_success 'init honors global core.sharedRepository' '
x$(git config -f shared-honor-global/.git/config core.sharedRepository)
'
-test_expect_success 'init rejects insanely long --template' '
- test_must_fail git init --template=$(printf "x%09999dx" 1) test
+test_expect_success 'init allows insanely long --template' '
+ git init --template=$(printf "x%09999dx" 1) test
'
test_expect_success 'init creates a new directory' '
diff --git a/t/t0002-gitfile.sh b/t/t0002-gitfile.sh
index 9393322c3e..9670e8cbe6 100755
--- a/t/t0002-gitfile.sh
+++ b/t/t0002-gitfile.sh
@@ -116,4 +116,46 @@ test_expect_success 'setup_git_dir twice in subdir' '
)
'
+test_expect_success 'enter_repo non-strict mode' '
+ test_create_repo enter_repo &&
+ (
+ cd enter_repo &&
+ test_tick &&
+ test_commit foo &&
+ mv .git .realgit &&
+ echo "gitdir: .realgit" >.git
+ ) &&
+ git ls-remote enter_repo >actual &&
+ cat >expected <<-\EOF &&
+ 946e985ab20de757ca5b872b16d64e92ff3803a9 HEAD
+ 946e985ab20de757ca5b872b16d64e92ff3803a9 refs/heads/master
+ 946e985ab20de757ca5b872b16d64e92ff3803a9 refs/tags/foo
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'enter_repo linked checkout' '
+ (
+ cd enter_repo &&
+ git worktree add ../foo refs/tags/foo
+ ) &&
+ git ls-remote foo >actual &&
+ cat >expected <<-\EOF &&
+ 946e985ab20de757ca5b872b16d64e92ff3803a9 HEAD
+ 946e985ab20de757ca5b872b16d64e92ff3803a9 refs/heads/master
+ 946e985ab20de757ca5b872b16d64e92ff3803a9 refs/tags/foo
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'enter_repo strict mode' '
+ git ls-remote --upload-pack="git upload-pack --strict" foo/.git >actual &&
+ cat >expected <<-\EOF &&
+ 946e985ab20de757ca5b872b16d64e92ff3803a9 HEAD
+ 946e985ab20de757ca5b872b16d64e92ff3803a9 refs/heads/master
+ 946e985ab20de757ca5b872b16d64e92ff3803a9 refs/tags/foo
+ EOF
+ test_cmp expected actual
+'
+
test_done
diff --git a/t/t0027-auto-crlf.sh b/t/t0027-auto-crlf.sh
index 1a56e5e82e..b343651504 100755
--- a/t/t0027-auto-crlf.sh
+++ b/t/t0027-auto-crlf.sh
@@ -55,6 +55,26 @@ create_gitattributes () {
esac
}
+create_NNO_files () {
+ lfname=$1
+ crlfname=$2
+ lfmixcrlf=$3
+ lfmixcr=$4
+ crlfnul=$5
+ for crlf in false true input
+ do
+ for attr in "" auto text -text lf crlf
+ do
+ pfx=NNO_${crlf}_attr_${attr} &&
+ cp $lfname ${pfx}_LF.txt &&
+ cp $crlfname ${pfx}_CRLF.txt &&
+ cp $lfmixcrlf ${pfx}_CRLF_mix_LF.txt &&
+ cp $lfmixcr ${pfx}_LF_mix_CR.txt &&
+ cp $crlfnul ${pfx}_CRLF_nul.txt
+ done
+ done
+}
+
check_warning () {
case "$1" in
LF_CRLF) echo "warning: LF will be replaced by CRLF" >"$2".expect ;;
@@ -62,7 +82,7 @@ check_warning () {
'') >"$2".expect ;;
*) echo >&2 "Illegal 1": "$1" ; return false ;;
esac
- grep "will be replaced by" "$2" | sed -e "s/\(.*\) in [^ ]*$/\1/" >"$2".actual
+ grep "will be replaced by" "$2" | sed -e "s/\(.*\) in [^ ]*$/\1/" | uniq >"$2".actual
test_cmp "$2".expect "$2".actual
}
@@ -71,19 +91,10 @@ commit_check_warn () {
attr=$2
lfname=$3
crlfname=$4
- repoMIX=$5
- lfmixcrlf=$6
- lfmixcr=$7
- crlfnul=$8
+ lfmixcrlf=$5
+ lfmixcr=$6
+ crlfnul=$7
pfx=crlf_${crlf}_attr_${attr}
- # Special handling for repoMIX: It should already be in the repo
- # with CRLF
- f=repoMIX
- fname=${pfx}_$f.txt
- echo >.gitattributes &&
- cp $f $fname &&
- git -c core.autocrlf=false add $fname 2>"${pfx}_$f.err" &&
- git commit -m "repoMIX" &&
create_gitattributes "$attr" &&
for f in LF CRLF repoMIX LF_mix_CR CRLF_mix_LF LF_nul CRLF_nul
do
@@ -99,6 +110,45 @@ commit_check_warn () {
check_warning "$crlfnul" ${pfx}_CRLF_nul.err
}
+commit_chk_wrnNNO () {
+ crlf=$1
+ attr=$2
+ lfwarn=$3
+ crlfwarn=$4
+ lfmixcrlf=$5
+ lfmixcr=$6
+ crlfnul=$7
+ pfx=NNO_${crlf}_attr_${attr}
+ #Commit files on top of existing file
+ create_gitattributes "$attr" &&
+ for f in LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+ do
+ fname=${pfx}_$f.txt &&
+ cp $f $fname &&
+ git -c core.autocrlf=$crlf add $fname 2>/dev/null &&
+ git -c core.autocrlf=$crlf commit -m "commit_$fname" $fname >"${pfx}_$f.err" 2>&1
+ done
+
+ test_expect_success "commit NNO files crlf=$crlf attr=$attr LF" '
+ check_warning "$lfwarn" ${pfx}_LF.err
+ '
+ test_expect_success "commit NNO files crlf=$crlf attr=$attr CRLF" '
+ check_warning "$crlfwarn" ${pfx}_CRLF.err
+ '
+
+ test_expect_success "commit NNO files crlf=$crlf attr=$attr CRLF_mix_LF" '
+ check_warning "$lfmixcrlf" ${pfx}_CRLF_mix_LF.err
+ '
+
+ test_expect_success "commit NNO files crlf=$crlf attr=$attr LF_mix_cr" '
+ check_warning "$lfmixcr" ${pfx}_LF_mix_CR.err
+ '
+
+ test_expect_success "commit NNO files crlf=$crlf attr=$attr CRLF_nul" '
+ check_warning "$crlfnul" ${pfx}_CRLF_nul.err
+ '
+}
+
check_files_in_repo () {
crlf=$1
attr=$2
@@ -115,6 +165,31 @@ check_files_in_repo () {
compare_files $crlfnul ${pfx}CRLF_nul.txt
}
+check_in_repo_NNO () {
+ crlf=$1
+ attr=$2
+ lfname=$3
+ crlfname=$4
+ lfmixcrlf=$5
+ lfmixcr=$6
+ crlfnul=$7
+ pfx=NNO_${crlf}_attr_${attr}_
+ test_expect_success "compare_files $lfname ${pfx}LF.txt" '
+ compare_files $lfname ${pfx}LF.txt
+ '
+ test_expect_success "compare_files $crlfname ${pfx}CRLF.txt" '
+ compare_files $crlfname ${pfx}CRLF.txt
+ '
+ test_expect_success "compare_files $lfmixcrlf ${pfx}CRLF_mix_LF.txt" '
+ compare_files $lfmixcrlf ${pfx}CRLF_mix_LF.txt
+ '
+ test_expect_success "compare_files $lfmixcr ${pfx}LF_mix_CR.txt" '
+ compare_files $lfmixcr ${pfx}LF_mix_CR.txt
+ '
+ test_expect_success "compare_files $crlfnul ${pfx}CRLF_nul.txt" '
+ compare_files $crlfnul ${pfx}CRLF_nul.txt
+ '
+}
checkout_files () {
eol=$1
@@ -169,7 +244,11 @@ test_expect_success 'setup master' '
printf "line1\nline2\rline3" >LF_mix_CR &&
printf "line1\r\nline2\rline3" >CRLF_mix_CR &&
printf "line1Q\r\nline2\r\nline3" | q_to_nul >CRLF_nul &&
- printf "line1Q\nline2\nline3" | q_to_nul >LF_nul
+ printf "line1Q\nline2\nline3" | q_to_nul >LF_nul &&
+ create_NNO_files CRLF_mix_LF CRLF_mix_LF CRLF_mix_LF CRLF_mix_LF CRLF_mix_LF &&
+ git -c core.autocrlf=false add NNO_*.txt &&
+ git commit -m "mixed line endings" &&
+ test_tick
'
@@ -191,46 +270,72 @@ else
WAMIX=CRLF_LF
fi
-# attr LF CRLF repoMIX CRLFmixLF LFmixCR CRLFNUL
+# attr LF CRLF CRLFmixLF LFmixCR CRLFNUL
test_expect_success 'commit files empty attr' '
- commit_check_warn false "" "" "" "" "" "" "" &&
- commit_check_warn true "" "LF_CRLF" "" "LF_CRLF" "LF_CRLF" "" "" &&
- commit_check_warn input "" "" "CRLF_LF" "CRLF_LF" "CRLF_LF" "" ""
+ commit_check_warn false "" "" "" "" "" "" &&
+ commit_check_warn true "" "LF_CRLF" "" "LF_CRLF" "" "" &&
+ commit_check_warn input "" "" "CRLF_LF" "CRLF_LF" "" ""
'
test_expect_success 'commit files attr=auto' '
- commit_check_warn false "auto" "$WILC" "$WICL" "$WAMIX" "$WAMIX" "" "" &&
- commit_check_warn true "auto" "LF_CRLF" "" "LF_CRLF" "LF_CRLF" "" "" &&
- commit_check_warn input "auto" "" "CRLF_LF" "CRLF_LF" "CRLF_LF" "" ""
+ commit_check_warn false "auto" "$WILC" "$WICL" "$WAMIX" "" "" &&
+ commit_check_warn true "auto" "LF_CRLF" "" "LF_CRLF" "" "" &&
+ commit_check_warn input "auto" "" "CRLF_LF" "CRLF_LF" "" ""
'
test_expect_success 'commit files attr=text' '
- commit_check_warn false "text" "$WILC" "$WICL" "$WAMIX" "$WAMIX" "$WILC" "$WICL" &&
- commit_check_warn true "text" "LF_CRLF" "" "LF_CRLF" "LF_CRLF" "LF_CRLF" "" &&
- commit_check_warn input "text" "" "CRLF_LF" "CRLF_LF" "CRLF_LF" "" "CRLF_LF"
+ commit_check_warn false "text" "$WILC" "$WICL" "$WAMIX" "$WILC" "$WICL" &&
+ commit_check_warn true "text" "LF_CRLF" "" "LF_CRLF" "LF_CRLF" "" &&
+ commit_check_warn input "text" "" "CRLF_LF" "CRLF_LF" "" "CRLF_LF"
'
test_expect_success 'commit files attr=-text' '
- commit_check_warn false "-text" "" "" "" "" "" "" &&
- commit_check_warn true "-text" "" "" "" "" "" "" &&
- commit_check_warn input "-text" "" "" "" "" "" ""
+ commit_check_warn false "-text" "" "" "" "" "" &&
+ commit_check_warn true "-text" "" "" "" "" "" &&
+ commit_check_warn input "-text" "" "" "" "" ""
'
test_expect_success 'commit files attr=lf' '
- commit_check_warn false "lf" "" "CRLF_LF" "CRLF_LF" "CRLF_LF" "" "CRLF_LF" &&
- commit_check_warn true "lf" "" "CRLF_LF" "CRLF_LF" "CRLF_LF" "" "CRLF_LF" &&
- commit_check_warn input "lf" "" "CRLF_LF" "CRLF_LF" "CRLF_LF" "" "CRLF_LF"
+ commit_check_warn false "lf" "" "CRLF_LF" "CRLF_LF" "" "CRLF_LF" &&
+ commit_check_warn true "lf" "" "CRLF_LF" "CRLF_LF" "" "CRLF_LF" &&
+ commit_check_warn input "lf" "" "CRLF_LF" "CRLF_LF" "" "CRLF_LF"
'
test_expect_success 'commit files attr=crlf' '
- commit_check_warn false "crlf" "LF_CRLF" "" "LF_CRLF" "LF_CRLF" "LF_CRLF" "" &&
- commit_check_warn true "crlf" "LF_CRLF" "" "LF_CRLF" "LF_CRLF" "LF_CRLF" "" &&
- commit_check_warn input "crlf" "LF_CRLF" "" "LF_CRLF" "LF_CRLF" "LF_CRLF" ""
+ commit_check_warn false "crlf" "LF_CRLF" "" "LF_CRLF" "LF_CRLF" "" &&
+ commit_check_warn true "crlf" "LF_CRLF" "" "LF_CRLF" "LF_CRLF" "" &&
+ commit_check_warn input "crlf" "LF_CRLF" "" "LF_CRLF" "LF_CRLF" ""
'
+# attr LF CRLF CRLFmixLF LF_mix_CR CRLFNUL
+commit_chk_wrnNNO false "" "" "" "" "" ""
+commit_chk_wrnNNO true "" "LF_CRLF" "" "" "" ""
+commit_chk_wrnNNO input "" "" "" "" "" ""
+
+
+commit_chk_wrnNNO false "auto" "$WILC" "$WICL" "$WAMIX" "" ""
+commit_chk_wrnNNO true "auto" "LF_CRLF" "" "LF_CRLF" "" ""
+commit_chk_wrnNNO input "auto" "" "CRLF_LF" "CRLF_LF" "" ""
+
+commit_chk_wrnNNO false "text" "$WILC" "$WICL" "$WAMIX" "$WILC" "$WICL"
+commit_chk_wrnNNO true "text" "LF_CRLF" "" "LF_CRLF" "LF_CRLF" ""
+commit_chk_wrnNNO input "text" "" "CRLF_LF" "CRLF_LF" "" "CRLF_LF"
+
+commit_chk_wrnNNO false "-text" "" "" "" "" ""
+commit_chk_wrnNNO true "-text" "" "" "" "" ""
+commit_chk_wrnNNO input "-text" "" "" "" "" ""
+
+commit_chk_wrnNNO false "lf" "" "CRLF_LF" "CRLF_LF" "" "CRLF_LF"
+commit_chk_wrnNNO true "lf" "" "CRLF_LF" "CRLF_LF" "" "CRLF_LF"
+commit_chk_wrnNNO input "lf" "" "CRLF_LF" "CRLF_LF" "" "CRLF_LF"
+
+commit_chk_wrnNNO false "crlf" "LF_CRLF" "" "LF_CRLF" "LF_CRLF" ""
+commit_chk_wrnNNO true "crlf" "LF_CRLF" "" "LF_CRLF" "LF_CRLF" ""
+commit_chk_wrnNNO input "crlf" "LF_CRLF" "" "LF_CRLF" "LF_CRLF" ""
+
test_expect_success 'create files cleanup' '
rm -f *.txt &&
- git reset --hard
+ git -c core.autocrlf=false reset --hard
'
test_expect_success 'commit empty gitattribues' '
@@ -257,6 +362,24 @@ test_expect_success 'commit -text' '
check_files_in_repo input "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
'
+# attr LF CRLF CRLF_mix_LF LF_mix_CR CRLFNUL
+check_in_repo_NNO false "" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_in_repo_NNO true "" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_in_repo_NNO input "" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+
+check_in_repo_NNO false "auto" LF LF LF LF_mix_CR CRLF_nul
+check_in_repo_NNO true "auto" LF LF LF LF_mix_CR CRLF_nul
+check_in_repo_NNO input "auto" LF LF LF LF_mix_CR CRLF_nul
+
+check_in_repo_NNO false "text" LF LF LF LF_mix_CR LF_nul
+check_in_repo_NNO true "text" LF LF LF LF_mix_CR LF_nul
+check_in_repo_NNO input "text" LF LF LF LF_mix_CR LF_nul
+
+check_in_repo_NNO false "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_in_repo_NNO true "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_in_repo_NNO input "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+
+
################################################################################
# Check how files in the repo are changed when they are checked out
# How to read the table below:
diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh
index 93605f42f2..627ef854d5 100755
--- a/t/t0060-path-utils.sh
+++ b/t/t0060-path-utils.sh
@@ -266,15 +266,21 @@ test_expect_success 'setup common repository' 'git --git-dir=bar init'
test_git_path GIT_COMMON_DIR=bar index .git/index
test_git_path GIT_COMMON_DIR=bar HEAD .git/HEAD
test_git_path GIT_COMMON_DIR=bar logs/HEAD .git/logs/HEAD
+test_git_path GIT_COMMON_DIR=bar logs/refs/bisect/foo .git/logs/refs/bisect/foo
+test_git_path GIT_COMMON_DIR=bar logs/refs/bisec/foo bar/logs/refs/bisec/foo
+test_git_path GIT_COMMON_DIR=bar logs/refs/bisec bar/logs/refs/bisec
+test_git_path GIT_COMMON_DIR=bar logs/refs/bisectfoo bar/logs/refs/bisectfoo
test_git_path GIT_COMMON_DIR=bar objects bar/objects
test_git_path GIT_COMMON_DIR=bar objects/bar bar/objects/bar
test_git_path GIT_COMMON_DIR=bar info/exclude bar/info/exclude
test_git_path GIT_COMMON_DIR=bar info/grafts bar/info/grafts
test_git_path GIT_COMMON_DIR=bar info/sparse-checkout .git/info/sparse-checkout
+test_git_path GIT_COMMON_DIR=bar info//sparse-checkout .git/info//sparse-checkout
test_git_path GIT_COMMON_DIR=bar remotes/bar bar/remotes/bar
test_git_path GIT_COMMON_DIR=bar branches/bar bar/branches/bar
test_git_path GIT_COMMON_DIR=bar logs/refs/heads/master bar/logs/refs/heads/master
test_git_path GIT_COMMON_DIR=bar refs/heads/master bar/refs/heads/master
+test_git_path GIT_COMMON_DIR=bar refs/bisect/foo .git/refs/bisect/foo
test_git_path GIT_COMMON_DIR=bar hooks/me bar/hooks/me
test_git_path GIT_COMMON_DIR=bar config bar/config
test_git_path GIT_COMMON_DIR=bar packed-refs bar/packed-refs
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index 97406fa4b1..af1b20dd5c 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -1130,4 +1130,23 @@ test_expect_success ULIMIT_FILE_DESCRIPTORS 'large transaction deleting branches
)
'
+test_expect_success 'handle per-worktree refs in refs/bisect' '
+ git commit --allow-empty -m "initial commit" &&
+ git worktree add -b branch worktree &&
+ (
+ cd worktree &&
+ git commit --allow-empty -m "test commit" &&
+ git for-each-ref >for-each-ref.out &&
+ ! grep refs/bisect for-each-ref.out &&
+ git update-ref refs/bisect/something HEAD &&
+ git rev-parse refs/bisect/something >../worktree-head &&
+ git for-each-ref | grep refs/bisect/something
+ ) &&
+ test_path_is_missing .git/refs/bisect &&
+ test_must_fail git rev-parse refs/bisect/something &&
+ git update-ref refs/bisect/something HEAD &&
+ git rev-parse refs/bisect/something >main-head &&
+ ! test_cmp main-head worktree-head
+'
+
test_done
diff --git a/t/t1401-symbolic-ref.sh b/t/t1401-symbolic-ref.sh
index 36378b0e3f..20b022ae33 100755
--- a/t/t1401-symbolic-ref.sh
+++ b/t/t1401-symbolic-ref.sh
@@ -63,4 +63,33 @@ test_expect_success 'symbolic-ref fails to delete real ref' '
'
reset_to_sane
+test_expect_success 'create large ref name' '
+ # make 256+ character ref; some systems may not handle that,
+ # so be gentle
+ long=0123456789abcdef &&
+ long=$long/$long/$long/$long &&
+ long=$long/$long/$long/$long &&
+ long_ref=refs/heads/$long &&
+ tree=$(git write-tree) &&
+ commit=$(echo foo | git commit-tree $tree) &&
+ if git update-ref $long_ref $commit; then
+ test_set_prereq LONG_REF
+ else
+ echo >&2 "long refs not supported"
+ fi
+'
+
+test_expect_success LONG_REF 'symbolic-ref can point to large ref name' '
+ git symbolic-ref HEAD $long_ref &&
+ echo $long_ref >expect &&
+ git symbolic-ref HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success LONG_REF 'we can parse long symbolic ref' '
+ echo $commit >expect &&
+ git rev-parse --verify HEAD >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t1430-bad-ref-name.sh b/t/t1430-bad-ref-name.sh
index 16d0b8bd1a..c465abe8e3 100755
--- a/t/t1430-bad-ref-name.sh
+++ b/t/t1430-bad-ref-name.sh
@@ -38,18 +38,20 @@ test_expect_success 'fast-import: fail on invalid branch name "bad[branch]name"'
test_must_fail git fast-import <input
'
-test_expect_success 'git branch shows badly named ref' '
+test_expect_success 'git branch shows badly named ref as warning' '
cp .git/refs/heads/master .git/refs/heads/broken...ref &&
test_when_finished "rm -f .git/refs/heads/broken...ref" &&
- git branch >output &&
- grep -e "broken\.\.\.ref" output
+ git branch >output 2>error &&
+ grep -e "broken\.\.\.ref" error &&
+ ! grep -e "broken\.\.\.ref" output
'
test_expect_success 'branch -d can delete badly named ref' '
cp .git/refs/heads/master .git/refs/heads/broken...ref &&
test_when_finished "rm -f .git/refs/heads/broken...ref" &&
git branch -d broken...ref &&
- git branch >output &&
+ git branch >output 2>error &&
+ ! grep -e "broken\.\.\.ref" error &&
! grep -e "broken\.\.\.ref" output
'
@@ -57,7 +59,8 @@ test_expect_success 'branch -D can delete badly named ref' '
cp .git/refs/heads/master .git/refs/heads/broken...ref &&
test_when_finished "rm -f .git/refs/heads/broken...ref" &&
git branch -D broken...ref &&
- git branch >output &&
+ git branch >output 2>error &&
+ ! grep -e "broken\.\.\.ref" error &&
! grep -e "broken\.\.\.ref" output
'
@@ -85,7 +88,8 @@ test_expect_success 'branch -D cannot delete absolute path' '
test_expect_success 'git branch cannot create a badly named ref' '
test_when_finished "rm -f .git/refs/heads/broken...ref" &&
test_must_fail git branch broken...ref &&
- git branch >output &&
+ git branch >output 2>error &&
+ ! grep -e "broken\.\.\.ref" error &&
! grep -e "broken\.\.\.ref" output
'
@@ -95,7 +99,8 @@ test_expect_success 'branch -m cannot rename to a bad ref name' '
git branch goodref &&
test_must_fail git branch -m goodref broken...ref &&
test_cmp_rev master goodref &&
- git branch >output &&
+ git branch >output 2>error &&
+ ! grep -e "broken\.\.\.ref" error &&
! grep -e "broken\.\.\.ref" output
'
@@ -104,14 +109,16 @@ test_expect_failure 'branch -m can rename from a bad ref name' '
test_when_finished "rm -f .git/refs/heads/broken...ref" &&
git branch -m broken...ref renamed &&
test_cmp_rev master renamed &&
- git branch >output &&
+ git branch >output 2>error &&
+ ! grep -e "broken\.\.\.ref" error &&
! grep -e "broken\.\.\.ref" output
'
test_expect_success 'push cannot create a badly named ref' '
test_when_finished "rm -f .git/refs/heads/broken...ref" &&
test_must_fail git push "file://$(pwd)" HEAD:refs/heads/broken...ref &&
- git branch >output &&
+ git branch >output 2>error &&
+ ! grep -e "broken\.\.\.ref" error &&
! grep -e "broken\.\.\.ref" output
'
@@ -131,7 +138,8 @@ test_expect_failure 'push --mirror can delete badly named ref' '
cp .git/refs/heads/master .git/refs/heads/broken...ref
) &&
git -C src push --mirror "file://$top/dest" &&
- git -C dest branch >output &&
+ git -C dest branch >output 2>error &&
+ ! grep -e "broken\.\.\.ref" error &&
! grep -e "broken\.\.\.ref" output
'
@@ -159,7 +167,8 @@ test_expect_success 'update-ref -d can delete broken name' '
cp .git/refs/heads/master .git/refs/heads/broken...ref &&
test_when_finished "rm -f .git/refs/heads/broken...ref" &&
git update-ref -d refs/heads/broken...ref &&
- git branch >output &&
+ git branch >output 2>error &&
+ ! grep -e "broken\.\.\.ref" error &&
! grep -e "broken\.\.\.ref" output
'
diff --git a/t/t2025-worktree-add.sh b/t/t2025-worktree-add.sh
index 8267411a0e..3694174989 100755
--- a/t/t2025-worktree-add.sh
+++ b/t/t2025-worktree-add.sh
@@ -193,4 +193,9 @@ test_expect_success '"add" -B/--detach mutually exclusive' '
test_must_fail git worktree add -B poodle --detach bamboo master
'
+test_expect_success 'local clone from linked checkout' '
+ git clone --local here here-clone &&
+ ( cd here-clone && git fsck )
+'
+
test_done
diff --git a/t/t2027-worktree-list.sh b/t/t2027-worktree-list.sh
new file mode 100755
index 0000000000..75ebb1b4a0
--- /dev/null
+++ b/t/t2027-worktree-list.sh
@@ -0,0 +1,89 @@
+#!/bin/sh
+
+test_description='test git worktree list'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ test_commit init
+'
+
+test_expect_success '"list" all worktrees from main' '
+ echo "$(git rev-parse --show-toplevel) $(git rev-parse --short HEAD) [$(git symbolic-ref --short HEAD)]" >expect &&
+ test_when_finished "rm -rf here && git worktree prune" &&
+ git worktree add --detach here master &&
+ echo "$(git -C here rev-parse --show-toplevel) $(git rev-parse --short HEAD) (detached HEAD)" >>expect &&
+ git worktree list | sed "s/ */ /g" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '"list" all worktrees from linked' '
+ echo "$(git rev-parse --show-toplevel) $(git rev-parse --short HEAD) [$(git symbolic-ref --short HEAD)]" >expect &&
+ test_when_finished "rm -rf here && git worktree prune" &&
+ git worktree add --detach here master &&
+ echo "$(git -C here rev-parse --show-toplevel) $(git rev-parse --short HEAD) (detached HEAD)" >>expect &&
+ git -C here worktree list | sed "s/ */ /g" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '"list" all worktrees --porcelain' '
+ echo "worktree $(git rev-parse --show-toplevel)" >expect &&
+ echo "HEAD $(git rev-parse HEAD)" >>expect &&
+ echo "branch $(git symbolic-ref HEAD)" >>expect &&
+ echo >>expect &&
+ test_when_finished "rm -rf here && git worktree prune" &&
+ git worktree add --detach here master &&
+ echo "worktree $(git -C here rev-parse --show-toplevel)" >>expect &&
+ echo "HEAD $(git rev-parse HEAD)" >>expect &&
+ echo "detached" >>expect &&
+ echo >>expect &&
+ git worktree list --porcelain >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'bare repo setup' '
+ git init --bare bare1 &&
+ echo "data" >file1 &&
+ git add file1 &&
+ git commit -m"File1: add data" &&
+ git push bare1 master &&
+ git reset --hard HEAD^
+'
+
+test_expect_success '"list" all worktrees from bare main' '
+ test_when_finished "rm -rf there && git -C bare1 worktree prune" &&
+ git -C bare1 worktree add --detach ../there master &&
+ echo "$(pwd)/bare1 (bare)" >expect &&
+ echo "$(git -C there rev-parse --show-toplevel) $(git -C there rev-parse --short HEAD) (detached HEAD)" >>expect &&
+ git -C bare1 worktree list | sed "s/ */ /g" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '"list" all worktrees --porcelain from bare main' '
+ test_when_finished "rm -rf there && git -C bare1 worktree prune" &&
+ git -C bare1 worktree add --detach ../there master &&
+ echo "worktree $(pwd)/bare1" >expect &&
+ echo "bare" >>expect &&
+ echo >>expect &&
+ echo "worktree $(git -C there rev-parse --show-toplevel)" >>expect &&
+ echo "HEAD $(git -C there rev-parse HEAD)" >>expect &&
+ echo "detached" >>expect &&
+ echo >>expect &&
+ git -C bare1 worktree list --porcelain >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '"list" all worktrees from linked with a bare main' '
+ test_when_finished "rm -rf there && git -C bare1 worktree prune" &&
+ git -C bare1 worktree add --detach ../there master &&
+ echo "$(pwd)/bare1 (bare)" >expect &&
+ echo "$(git -C there rev-parse --show-toplevel) $(git -C there rev-parse --short HEAD) (detached HEAD)" >>expect &&
+ git -C there worktree list | sed "s/ */ /g" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'bare repo cleanup' '
+ rm -rf bare1
+'
+
+test_done
diff --git a/t/t3001-ls-files-others-exclude.sh b/t/t3001-ls-files-others-exclude.sh
index 3fc484e8c3..da257c020f 100755
--- a/t/t3001-ls-files-others-exclude.sh
+++ b/t/t3001-ls-files-others-exclude.sh
@@ -305,4 +305,29 @@ test_expect_success 'ls-files with "**" patterns and no slashes' '
test_cmp expect actual
'
+test_expect_success 'negative patterns' '
+ git init reinclude &&
+ (
+ cd reinclude &&
+ cat >.gitignore <<-\EOF &&
+ /fooo
+ /foo
+ !foo/bar/bar
+ EOF
+ mkdir fooo &&
+ cat >fooo/.gitignore <<-\EOF &&
+ !/*
+ EOF
+ mkdir -p foo/bar &&
+ touch abc foo/def foo/bar/ghi foo/bar/bar &&
+ git ls-files -o --exclude-standard >../actual &&
+ cat >../expected <<-\EOF &&
+ .gitignore
+ abc
+ foo/bar/bar
+ EOF
+ test_cmp ../expected ../actual
+ )
+'
+
test_done
diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh
index 16efe7af03..9454423ca0 100755
--- a/t/t3203-branch-output.sh
+++ b/t/t3203-branch-output.sh
@@ -156,4 +156,24 @@ EOF
test_i18ncmp expect actual
'
+test_expect_success 'git branch `--sort` option' '
+ cat >expect <<-\EOF &&
+ branch-two
+ * (HEAD detached from fromtag)
+ branch-one
+ master
+ EOF
+ git branch --sort=objectsize >actual &&
+ test_i18ncmp expect actual
+'
+
+test_expect_success 'git branch --points-at option' '
+ cat >expect <<-\EOF &&
+ branch-one
+ master
+ EOF
+ git branch --points-at=branch-one >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t3210-pack-refs.sh b/t/t3210-pack-refs.sh
index 7b5b6d452e..db244d2f88 100755
--- a/t/t3210-pack-refs.sh
+++ b/t/t3210-pack-refs.sh
@@ -160,6 +160,13 @@ test_expect_success 'pack ref directly below refs/' '
test_path_is_missing .git/refs/top
'
+test_expect_success 'do not pack ref in refs/bisect' '
+ git update-ref refs/bisect/local HEAD &&
+ git pack-refs --all --prune &&
+ ! grep refs/bisect/local .git/packed-refs >/dev/null &&
+ test_path_is_file .git/refs/bisect/local
+'
+
test_expect_success 'disable reflogs' '
git config core.logallrefupdates false &&
rm -rf .git/logs
diff --git a/t/t3420-rebase-autostash.sh b/t/t3420-rebase-autostash.sh
index d783f03d3f..944154b2e0 100755
--- a/t/t3420-rebase-autostash.sh
+++ b/t/t3420-rebase-autostash.sh
@@ -37,6 +37,16 @@ testrebase() {
type=$1
dotest=$2
+ test_expect_success "rebase$type: dirty worktree, --no-autostash" '
+ test_config rebase.autostash true &&
+ git reset --hard &&
+ git checkout -b rebased-feature-branch feature-branch &&
+ test_when_finished git branch -D rebased-feature-branch &&
+ test_when_finished git checkout feature-branch &&
+ echo dirty >>file3 &&
+ test_must_fail git rebase$type --no-autostash unrelated-onto-branch
+ '
+
test_expect_success "rebase$type: dirty worktree, non-conflicting rebase" '
test_config rebase.autostash true &&
git reset --hard &&
diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh
index 7a8499ce66..dfaf9d9f68 100755
--- a/t/t5505-remote.sh
+++ b/t/t5505-remote.sh
@@ -919,6 +919,19 @@ test_expect_success 'new remote' '
cmp expect actual
'
+get_url_test () {
+ cat >expect &&
+ git remote get-url "$@" >actual &&
+ test_cmp expect actual
+}
+
+test_expect_success 'get-url on new remote' '
+ echo foo | get_url_test someremote &&
+ echo foo | get_url_test --all someremote &&
+ echo foo | get_url_test --push someremote &&
+ echo foo | get_url_test --push --all someremote
+'
+
test_expect_success 'remote set-url bar' '
git remote set-url someremote bar &&
echo bar >expect &&
@@ -961,6 +974,13 @@ test_expect_success 'remote set-url --push zot' '
cmp expect actual
'
+test_expect_success 'get-url with different urls' '
+ echo baz | get_url_test someremote &&
+ echo baz | get_url_test --all someremote &&
+ echo zot | get_url_test --push someremote &&
+ echo zot | get_url_test --push --all someremote
+'
+
test_expect_success 'remote set-url --push qux zot' '
git remote set-url --push someremote qux zot &&
echo qux >expect &&
@@ -995,6 +1015,14 @@ test_expect_success 'remote set-url --push --add aaa' '
cmp expect actual
'
+test_expect_success 'get-url on multi push remote' '
+ echo foo | get_url_test --push someremote &&
+ get_url_test --push --all someremote <<-\EOF
+ foo
+ aaa
+ EOF
+'
+
test_expect_success 'remote set-url --push bar aaa' '
git remote set-url --push someremote bar aaa &&
echo foo >expect &&
@@ -1039,6 +1067,14 @@ test_expect_success 'remote set-url --add bbb' '
cmp expect actual
'
+test_expect_success 'get-url on multi fetch remote' '
+ echo baz | get_url_test someremote &&
+ get_url_test --all someremote <<-\EOF
+ baz
+ bbb
+ EOF
+'
+
test_expect_success 'remote set-url --delete .*' '
test_must_fail git remote set-url --delete someremote .\* &&
echo "YYY" >expect &&
@@ -1108,6 +1144,7 @@ test_extra_arg rename origin newname
test_extra_arg remove origin
test_extra_arg set-head origin master
# set-branches takes any number of args
+test_extra_arg get-url origin newurl
test_extra_arg set-url origin newurl oldurl
# show takes any number of args
# prune takes any number of args
diff --git a/t/t5700-clone-reference.sh b/t/t5700-clone-reference.sh
index ef1779f5ca..2250ef4fe2 100755
--- a/t/t5700-clone-reference.sh
+++ b/t/t5700-clone-reference.sh
@@ -188,5 +188,26 @@ test_expect_success 'clone and dissociate from reference' '
test_must_fail git -C R fsck &&
git -C S fsck
'
+test_expect_success 'clone, dissociate from partial reference and repack' '
+ rm -fr P Q R &&
+ git init P &&
+ (
+ cd P &&
+ test_commit one &&
+ git repack &&
+ test_commit two &&
+ git repack
+ ) &&
+ git clone --bare P Q &&
+ (
+ cd P &&
+ git checkout -b second &&
+ test_commit three &&
+ git repack
+ ) &&
+ git clone --bare --dissociate --reference=P Q R &&
+ ls R/objects/pack/*.pack >packs.txt &&
+ test_line_count = 1 packs.txt
+'
test_done
diff --git a/t/t5801-remote-helpers.sh b/t/t5801-remote-helpers.sh
index c9d3ed14c3..362b1581e0 100755
--- a/t/t5801-remote-helpers.sh
+++ b/t/t5801-remote-helpers.sh
@@ -242,13 +242,6 @@ clean_mark () {
sort >$(basename "$1")
}
-cmp_marks () {
- test_when_finished "rm -rf git.marks testgit.marks" &&
- clean_mark ".git/testgit/$1/git.marks" &&
- clean_mark ".git/testgit/$1/testgit.marks" &&
- test_cmp git.marks testgit.marks
-}
-
test_expect_success 'proper failure checks for fetching' '
(cd local &&
test_must_fail env GIT_REMOTE_TESTGIT_FAILURE=1 git fetch 2>error &&
@@ -258,12 +251,15 @@ test_expect_success 'proper failure checks for fetching' '
'
test_expect_success 'proper failure checks for pushing' '
+ test_when_finished "rm -rf local/git.marks local/testgit.marks" &&
(cd local &&
git checkout -b crash master &&
echo crash >>file &&
git commit -a -m crash &&
test_must_fail env GIT_REMOTE_TESTGIT_FAILURE=1 git push --all &&
- cmp_marks origin
+ clean_mark ".git/testgit/origin/git.marks" &&
+ clean_mark ".git/testgit/origin/testgit.marks" &&
+ test_cmp git.marks testgit.marks
)
'
diff --git a/t/t5802-connect-helper.sh b/t/t5802-connect-helper.sh
index 878faf2b63..b7a7f9d588 100755
--- a/t/t5802-connect-helper.sh
+++ b/t/t5802-connect-helper.sh
@@ -69,4 +69,32 @@ test_expect_success 'update backfilled tag without primary transfer' '
test_cmp expect actual
'
+
+test_expect_success 'set up fake git-daemon' '
+ mkdir remote &&
+ git init --bare remote/one.git &&
+ mkdir remote/host &&
+ git init --bare remote/host/two.git &&
+ write_script fake-daemon <<-\EOF &&
+ git daemon --inetd \
+ --informative-errors \
+ --export-all \
+ --base-path="$TRASH_DIRECTORY/remote" \
+ --interpolated-path="$TRASH_DIRECTORY/remote/%H%D" \
+ "$TRASH_DIRECTORY/remote"
+ EOF
+ export TRASH_DIRECTORY &&
+ PATH=$TRASH_DIRECTORY:$PATH
+'
+
+test_expect_success 'ext command can connect to git daemon (no vhost)' '
+ rm -rf dst &&
+ git clone "ext::fake-daemon %G/one.git" dst
+'
+
+test_expect_success 'ext command can connect to git daemon (vhost)' '
+ rm -rf dst &&
+ git clone "ext::fake-daemon %G/two.git %Vhost" dst
+'
+
test_done
diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh
index 9e2c203747..e74662ba5c 100755
--- a/t/t6030-bisect-porcelain.sh
+++ b/t/t6030-bisect-porcelain.sh
@@ -759,4 +759,139 @@ test_expect_success '"git bisect bad HEAD" behaves as "git bisect bad"' '
git bisect reset
'
+test_expect_success 'bisect starts with only one new' '
+ git bisect reset &&
+ git bisect start &&
+ git bisect new $HASH4 &&
+ git bisect next
+'
+
+test_expect_success 'bisect does not start with only one old' '
+ git bisect reset &&
+ git bisect start &&
+ git bisect old $HASH1 &&
+ test_must_fail git bisect next
+'
+
+test_expect_success 'bisect start with one new and old' '
+ git bisect reset &&
+ git bisect start &&
+ git bisect old $HASH1 &&
+ git bisect new $HASH4 &&
+ git bisect new &&
+ git bisect new >bisect_result &&
+ grep "$HASH2 is the first new commit" bisect_result &&
+ git bisect log >log_to_replay.txt &&
+ git bisect reset
+'
+
+test_expect_success 'bisect replay with old and new' '
+ git bisect replay log_to_replay.txt >bisect_result &&
+ grep "$HASH2 is the first new commit" bisect_result &&
+ git bisect reset
+'
+
+test_expect_success 'bisect cannot mix old/new and good/bad' '
+ git bisect start &&
+ git bisect bad $HASH4 &&
+ test_must_fail git bisect old $HASH1
+'
+
+test_expect_success 'bisect terms needs 0 or 1 argument' '
+ git bisect reset &&
+ test_must_fail git bisect terms only-one &&
+ test_must_fail git bisect terms 1 2 &&
+ test_must_fail git bisect terms 2>actual &&
+ echo "no terms defined" >expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'bisect terms shows good/bad after start' '
+ git bisect reset &&
+ git bisect start HEAD $HASH1 &&
+ git bisect terms --term-good >actual &&
+ echo good >expected &&
+ test_cmp expected actual &&
+ git bisect terms --term-bad >actual &&
+ echo bad >expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'bisect start with one term1 and term2' '
+ git bisect reset &&
+ git bisect start --term-old term2 --term-new term1 &&
+ git bisect term2 $HASH1 &&
+ git bisect term1 $HASH4 &&
+ git bisect term1 &&
+ git bisect term1 >bisect_result &&
+ grep "$HASH2 is the first term1 commit" bisect_result &&
+ git bisect log >log_to_replay.txt &&
+ git bisect reset
+'
+
+test_expect_success 'bisect replay with term1 and term2' '
+ git bisect replay log_to_replay.txt >bisect_result &&
+ grep "$HASH2 is the first term1 commit" bisect_result &&
+ git bisect reset
+'
+
+test_expect_success 'bisect start term1 term2' '
+ git bisect reset &&
+ git bisect start --term-new term1 --term-old term2 $HASH4 $HASH1 &&
+ git bisect term1 &&
+ git bisect term1 >bisect_result &&
+ grep "$HASH2 is the first term1 commit" bisect_result &&
+ git bisect log >log_to_replay.txt &&
+ git bisect reset
+'
+
+test_expect_success 'bisect cannot mix terms' '
+ git bisect reset &&
+ git bisect start --term-good term1 --term-bad term2 $HASH4 $HASH1 &&
+ test_must_fail git bisect a &&
+ test_must_fail git bisect b &&
+ test_must_fail git bisect bad &&
+ test_must_fail git bisect good &&
+ test_must_fail git bisect new &&
+ test_must_fail git bisect old
+'
+
+test_expect_success 'bisect terms rejects invalid terms' '
+ git bisect reset &&
+ test_must_fail git bisect start --term-good invalid..term &&
+ test_must_fail git bisect terms --term-bad invalid..term &&
+ test_must_fail git bisect terms --term-good bad &&
+ test_must_fail git bisect terms --term-good old &&
+ test_must_fail git bisect terms --term-good skip &&
+ test_must_fail git bisect terms --term-good reset &&
+ test_path_is_missing .git/BISECT_TERMS
+'
+
+test_expect_success 'bisect start --term-* does store terms' '
+ git bisect reset &&
+ git bisect start --term-bad=one --term-good=two &&
+ git bisect terms >actual &&
+ cat <<-EOF >expected &&
+ Your current terms are two for the old state
+ and one for the new state.
+ EOF
+ test_cmp expected actual &&
+ git bisect terms --term-bad >actual &&
+ echo one >expected &&
+ test_cmp expected actual &&
+ git bisect terms --term-good >actual &&
+ echo two >expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'bisect start takes options and revs in any order' '
+ git bisect reset &&
+ git bisect start --term-good one $HASH4 \
+ --term-good two --term-bad bad-term \
+ $HASH1 --term-good three -- &&
+ (git bisect terms --term-bad && git bisect terms --term-good) >actual &&
+ printf "%s\n%s\n" bad-term three >expected &&
+ test_cmp expected actual
+'
+
test_done
diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh
index 7c9bec7630..03873b09d1 100755
--- a/t/t6300-for-each-ref.sh
+++ b/t/t6300-for-each-ref.sh
@@ -8,8 +8,8 @@ test_description='for-each-ref test'
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-gpg.sh
-# Mon Jul 3 15:18:43 2006 +0000
-datestamp=1151939923
+# Mon Jul 3 23:18:43 2006 +0000
+datestamp=1151968723
setdate_and_increment () {
GIT_COMMITTER_DATE="$datestamp +0200"
datestamp=$(expr "$datestamp" + 1)
@@ -61,21 +61,21 @@ test_atom head object ''
test_atom head type ''
test_atom head '*objectname' ''
test_atom head '*objecttype' ''
-test_atom head author 'A U Thor <author@example.com> 1151939924 +0200'
+test_atom head author 'A U Thor <author@example.com> 1151968724 +0200'
test_atom head authorname 'A U Thor'
test_atom head authoremail '<author@example.com>'
-test_atom head authordate 'Mon Jul 3 17:18:44 2006 +0200'
-test_atom head committer 'C O Mitter <committer@example.com> 1151939923 +0200'
+test_atom head authordate 'Tue Jul 4 01:18:44 2006 +0200'
+test_atom head committer 'C O Mitter <committer@example.com> 1151968723 +0200'
test_atom head committername 'C O Mitter'
test_atom head committeremail '<committer@example.com>'
-test_atom head committerdate 'Mon Jul 3 17:18:43 2006 +0200'
+test_atom head committerdate 'Tue Jul 4 01:18:43 2006 +0200'
test_atom head tag ''
test_atom head tagger ''
test_atom head taggername ''
test_atom head taggeremail ''
test_atom head taggerdate ''
-test_atom head creator 'C O Mitter <committer@example.com> 1151939923 +0200'
-test_atom head creatordate 'Mon Jul 3 17:18:43 2006 +0200'
+test_atom head creator 'C O Mitter <committer@example.com> 1151968723 +0200'
+test_atom head creatordate 'Tue Jul 4 01:18:43 2006 +0200'
test_atom head subject 'Initial'
test_atom head contents:subject 'Initial'
test_atom head body ''
@@ -96,7 +96,7 @@ test_atom tag parent ''
test_atom tag numparent ''
test_atom tag object $(git rev-parse refs/tags/testtag^0)
test_atom tag type 'commit'
-test_atom tag '*objectname' '67a36f10722846e891fbada1ba48ed035de75581'
+test_atom tag '*objectname' 'ea122842f48be4afb2d1fc6a4b96c05885ab7463'
test_atom tag '*objecttype' 'commit'
test_atom tag author ''
test_atom tag authorname ''
@@ -107,18 +107,18 @@ test_atom tag committername ''
test_atom tag committeremail ''
test_atom tag committerdate ''
test_atom tag tag 'testtag'
-test_atom tag tagger 'C O Mitter <committer@example.com> 1151939925 +0200'
+test_atom tag tagger 'C O Mitter <committer@example.com> 1151968725 +0200'
test_atom tag taggername 'C O Mitter'
test_atom tag taggeremail '<committer@example.com>'
-test_atom tag taggerdate 'Mon Jul 3 17:18:45 2006 +0200'
-test_atom tag creator 'C O Mitter <committer@example.com> 1151939925 +0200'
-test_atom tag creatordate 'Mon Jul 3 17:18:45 2006 +0200'
-test_atom tag subject 'Tagging at 1151939927'
-test_atom tag contents:subject 'Tagging at 1151939927'
+test_atom tag taggerdate 'Tue Jul 4 01:18:45 2006 +0200'
+test_atom tag creator 'C O Mitter <committer@example.com> 1151968725 +0200'
+test_atom tag creatordate 'Tue Jul 4 01:18:45 2006 +0200'
+test_atom tag subject 'Tagging at 1151968727'
+test_atom tag contents:subject 'Tagging at 1151968727'
test_atom tag body ''
test_atom tag contents:body ''
test_atom tag contents:signature ''
-test_atom tag contents 'Tagging at 1151939927
+test_atom tag contents 'Tagging at 1151968727
'
test_atom tag HEAD ' '
@@ -146,95 +146,123 @@ test_expect_success 'Check invalid format specifiers are errors' '
test_must_fail git for-each-ref --format="%(authordate:INVALID)" refs/heads
'
-cat >expected <<\EOF
-'refs/heads/master' 'Mon Jul 3 17:18:43 2006 +0200' 'Mon Jul 3 17:18:44 2006 +0200'
-'refs/tags/testtag' 'Mon Jul 3 17:18:45 2006 +0200'
-EOF
+test_date () {
+ f=$1 &&
+ committer_date=$2 &&
+ author_date=$3 &&
+ tagger_date=$4 &&
+ cat >expected <<-EOF &&
+ 'refs/heads/master' '$committer_date' '$author_date'
+ 'refs/tags/testtag' '$tagger_date'
+ EOF
+ (
+ git for-each-ref --shell \
+ --format="%(refname) %(committerdate${f:+:$f}) %(authordate${f:+:$f})" \
+ refs/heads &&
+ git for-each-ref --shell \
+ --format="%(refname) %(taggerdate${f:+:$f})" \
+ refs/tags
+ ) >actual &&
+ test_cmp expected actual
+}
test_expect_success 'Check unformatted date fields output' '
- (git for-each-ref --shell --format="%(refname) %(committerdate) %(authordate)" refs/heads &&
- git for-each-ref --shell --format="%(refname) %(taggerdate)" refs/tags) >actual &&
- test_cmp expected actual
+ test_date "" \
+ "Tue Jul 4 01:18:43 2006 +0200" \
+ "Tue Jul 4 01:18:44 2006 +0200" \
+ "Tue Jul 4 01:18:45 2006 +0200"
'
test_expect_success 'Check format "default" formatted date fields output' '
- f=default &&
- (git for-each-ref --shell --format="%(refname) %(committerdate:$f) %(authordate:$f)" refs/heads &&
- git for-each-ref --shell --format="%(refname) %(taggerdate:$f)" refs/tags) >actual &&
- test_cmp expected actual
+ test_date default \
+ "Tue Jul 4 01:18:43 2006 +0200" \
+ "Tue Jul 4 01:18:44 2006 +0200" \
+ "Tue Jul 4 01:18:45 2006 +0200"
+'
+
+test_expect_success 'Check format "default-local" date fields output' '
+ test_date default-local "Mon Jul 3 23:18:43 2006" "Mon Jul 3 23:18:44 2006" "Mon Jul 3 23:18:45 2006"
'
# Don't know how to do relative check because I can't know when this script
# is going to be run and can't fake the current time to git, and hence can't
# provide expected output. Instead, I'll just make sure that "relative"
# doesn't exit in error
-#
-#cat >expected <<\EOF
-#
-#EOF
-#
test_expect_success 'Check format "relative" date fields output' '
f=relative &&
(git for-each-ref --shell --format="%(refname) %(committerdate:$f) %(authordate:$f)" refs/heads &&
git for-each-ref --shell --format="%(refname) %(taggerdate:$f)" refs/tags) >actual
'
-cat >expected <<\EOF
-'refs/heads/master' '2006-07-03' '2006-07-03'
-'refs/tags/testtag' '2006-07-03'
-EOF
+# We just check that this is the same as "relative" for now.
+test_expect_success 'Check format "relative-local" date fields output' '
+ test_date relative-local \
+ "$(git for-each-ref --format="%(committerdate:relative)" refs/heads)" \
+ "$(git for-each-ref --format="%(authordate:relative)" refs/heads)" \
+ "$(git for-each-ref --format="%(taggerdate:relative)" refs/tags)"
+'
test_expect_success 'Check format "short" date fields output' '
- f=short &&
- (git for-each-ref --shell --format="%(refname) %(committerdate:$f) %(authordate:$f)" refs/heads &&
- git for-each-ref --shell --format="%(refname) %(taggerdate:$f)" refs/tags) >actual &&
- test_cmp expected actual
+ test_date short 2006-07-04 2006-07-04 2006-07-04
'
-cat >expected <<\EOF
-'refs/heads/master' 'Mon Jul 3 15:18:43 2006' 'Mon Jul 3 15:18:44 2006'
-'refs/tags/testtag' 'Mon Jul 3 15:18:45 2006'
-EOF
+test_expect_success 'Check format "short-local" date fields output' '
+ test_date short-local 2006-07-03 2006-07-03 2006-07-03
+'
test_expect_success 'Check format "local" date fields output' '
- f=local &&
- (git for-each-ref --shell --format="%(refname) %(committerdate:$f) %(authordate:$f)" refs/heads &&
- git for-each-ref --shell --format="%(refname) %(taggerdate:$f)" refs/tags) >actual &&
- test_cmp expected actual
+ test_date local \
+ "Mon Jul 3 23:18:43 2006" \
+ "Mon Jul 3 23:18:44 2006" \
+ "Mon Jul 3 23:18:45 2006"
'
-cat >expected <<\EOF
-'refs/heads/master' '2006-07-03 17:18:43 +0200' '2006-07-03 17:18:44 +0200'
-'refs/tags/testtag' '2006-07-03 17:18:45 +0200'
-EOF
-
test_expect_success 'Check format "iso8601" date fields output' '
- f=iso8601 &&
- (git for-each-ref --shell --format="%(refname) %(committerdate:$f) %(authordate:$f)" refs/heads &&
- git for-each-ref --shell --format="%(refname) %(taggerdate:$f)" refs/tags) >actual &&
- test_cmp expected actual
+ test_date iso8601 \
+ "2006-07-04 01:18:43 +0200" \
+ "2006-07-04 01:18:44 +0200" \
+ "2006-07-04 01:18:45 +0200"
'
-cat >expected <<\EOF
-'refs/heads/master' 'Mon, 3 Jul 2006 17:18:43 +0200' 'Mon, 3 Jul 2006 17:18:44 +0200'
-'refs/tags/testtag' 'Mon, 3 Jul 2006 17:18:45 +0200'
-EOF
+test_expect_success 'Check format "iso8601-local" date fields output' '
+ test_date iso8601-local "2006-07-03 23:18:43 +0000" "2006-07-03 23:18:44 +0000" "2006-07-03 23:18:45 +0000"
+'
test_expect_success 'Check format "rfc2822" date fields output' '
- f=rfc2822 &&
- (git for-each-ref --shell --format="%(refname) %(committerdate:$f) %(authordate:$f)" refs/heads &&
- git for-each-ref --shell --format="%(refname) %(taggerdate:$f)" refs/tags) >actual &&
- test_cmp expected actual
+ test_date rfc2822 \
+ "Tue, 4 Jul 2006 01:18:43 +0200" \
+ "Tue, 4 Jul 2006 01:18:44 +0200" \
+ "Tue, 4 Jul 2006 01:18:45 +0200"
+'
+
+test_expect_success 'Check format "rfc2822-local" date fields output' '
+ test_date rfc2822-local "Mon, 3 Jul 2006 23:18:43 +0000" "Mon, 3 Jul 2006 23:18:44 +0000" "Mon, 3 Jul 2006 23:18:45 +0000"
+'
+
+test_expect_success 'Check format "raw" date fields output' '
+ test_date raw "1151968723 +0200" "1151968724 +0200" "1151968725 +0200"
+'
+
+test_expect_success 'Check format "raw-local" date fields output' '
+ test_date raw-local "1151968723 +0000" "1151968724 +0000" "1151968725 +0000"
'
test_expect_success 'Check format of strftime date fields' '
- echo "my date is 2006-07-03" >expected &&
+ echo "my date is 2006-07-04" >expected &&
git for-each-ref \
--format="%(authordate:format:my date is %Y-%m-%d)" \
refs/heads >actual &&
test_cmp expected actual
'
+test_expect_success 'Check format of strftime-local date fields' '
+ echo "my date is 2006-07-03" >expected &&
+ git for-each-ref \
+ --format="%(authordate:format-local:my date is %Y-%m-%d)" \
+ refs/heads >actual &&
+ test_cmp expected actual
+'
+
test_expect_success 'exercise strftime with odd fields' '
echo >expected &&
git for-each-ref --format="%(authordate:format:)" refs/heads >actual &&
@@ -546,8 +574,8 @@ body contents
$sig"
cat >expected <<EOF
-$(git rev-parse refs/tags/master) <committer@example.com> refs/tags/master
$(git rev-parse refs/tags/bogo) <committer@example.com> refs/tags/bogo
+$(git rev-parse refs/tags/master) <committer@example.com> refs/tags/master
EOF
test_expect_success 'Verify sort with multiple keys' '
diff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh
new file mode 100755
index 0000000000..fe4796cc9c
--- /dev/null
+++ b/t/t6302-for-each-ref-filter.sh
@@ -0,0 +1,258 @@
+#!/bin/sh
+
+test_description='test for-each-refs usage of ref-filter APIs'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-gpg.sh
+
+if ! test_have_prereq GPG
+then
+ skip_all="skipping for-each-ref tests, GPG not available"
+ test_done
+fi
+
+test_expect_success 'setup some history and refs' '
+ test_commit one &&
+ test_commit two &&
+ test_commit three &&
+ git checkout -b side &&
+ test_commit four &&
+ git tag -s -m "A signed tag message" signed-tag &&
+ git tag -s -m "Annonated doubly" double-tag signed-tag &&
+ git checkout master &&
+ git update-ref refs/odd/spot master
+'
+
+test_expect_success 'filtering with --points-at' '
+ cat >expect <<-\EOF &&
+ refs/heads/master
+ refs/odd/spot
+ refs/tags/three
+ EOF
+ git for-each-ref --format="%(refname)" --points-at=master >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'check signed tags with --points-at' '
+ sed -e "s/Z$//" >expect <<-\EOF &&
+ refs/heads/side Z
+ refs/tags/four Z
+ refs/tags/signed-tag four
+ EOF
+ git for-each-ref --format="%(refname) %(*subject)" --points-at=side >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'filtering with --merged' '
+ cat >expect <<-\EOF &&
+ refs/heads/master
+ refs/odd/spot
+ refs/tags/one
+ refs/tags/three
+ refs/tags/two
+ EOF
+ git for-each-ref --format="%(refname)" --merged=master >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'filtering with --no-merged' '
+ cat >expect <<-\EOF &&
+ refs/heads/side
+ refs/tags/double-tag
+ refs/tags/four
+ refs/tags/signed-tag
+ EOF
+ git for-each-ref --format="%(refname)" --no-merged=master >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'filtering with --contains' '
+ cat >expect <<-\EOF &&
+ refs/heads/master
+ refs/heads/side
+ refs/odd/spot
+ refs/tags/double-tag
+ refs/tags/four
+ refs/tags/signed-tag
+ refs/tags/three
+ refs/tags/two
+ EOF
+ git for-each-ref --format="%(refname)" --contains=two >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '%(color) must fail' '
+ test_must_fail git for-each-ref --format="%(color)%(refname)"
+'
+
+test_expect_success 'left alignment is default' '
+ cat >expect <<-\EOF &&
+ refname is refs/heads/master |refs/heads/master
+ refname is refs/heads/side |refs/heads/side
+ refname is refs/odd/spot |refs/odd/spot
+ refname is refs/tags/double-tag|refs/tags/double-tag
+ refname is refs/tags/four |refs/tags/four
+ refname is refs/tags/one |refs/tags/one
+ refname is refs/tags/signed-tag|refs/tags/signed-tag
+ refname is refs/tags/three |refs/tags/three
+ refname is refs/tags/two |refs/tags/two
+ EOF
+ git for-each-ref --format="%(align:30)refname is %(refname)%(end)|%(refname)" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'middle alignment' '
+ cat >expect <<-\EOF &&
+ | refname is refs/heads/master |refs/heads/master
+ | refname is refs/heads/side |refs/heads/side
+ | refname is refs/odd/spot |refs/odd/spot
+ |refname is refs/tags/double-tag|refs/tags/double-tag
+ | refname is refs/tags/four |refs/tags/four
+ | refname is refs/tags/one |refs/tags/one
+ |refname is refs/tags/signed-tag|refs/tags/signed-tag
+ | refname is refs/tags/three |refs/tags/three
+ | refname is refs/tags/two |refs/tags/two
+ EOF
+ git for-each-ref --format="|%(align:middle,30)refname is %(refname)%(end)|%(refname)" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'right alignment' '
+ cat >expect <<-\EOF &&
+ | refname is refs/heads/master|refs/heads/master
+ | refname is refs/heads/side|refs/heads/side
+ | refname is refs/odd/spot|refs/odd/spot
+ |refname is refs/tags/double-tag|refs/tags/double-tag
+ | refname is refs/tags/four|refs/tags/four
+ | refname is refs/tags/one|refs/tags/one
+ |refname is refs/tags/signed-tag|refs/tags/signed-tag
+ | refname is refs/tags/three|refs/tags/three
+ | refname is refs/tags/two|refs/tags/two
+ EOF
+ git for-each-ref --format="|%(align:30,right)refname is %(refname)%(end)|%(refname)" >actual &&
+ test_cmp expect actual
+'
+
+# Individual atoms inside %(align:...) and %(end) must not be quoted.
+
+test_expect_success 'alignment with format quote' "
+ cat >expect <<-\EOF &&
+ |' '\''master| A U Thor'\'' '|
+ |' '\''side| A U Thor'\'' '|
+ |' '\''odd/spot| A U Thor'\'' '|
+ |' '\''double-tag| '\'' '|
+ |' '\''four| A U Thor'\'' '|
+ |' '\''one| A U Thor'\'' '|
+ |' '\''signed-tag| '\'' '|
+ |' '\''three| A U Thor'\'' '|
+ |' '\''two| A U Thor'\'' '|
+ EOF
+ git for-each-ref --shell --format=\"|%(align:30,middle)'%(refname:short)| %(authorname)'%(end)|\" >actual &&
+ test_cmp expect actual
+"
+
+test_expect_success 'nested alignment with quote formatting' "
+ cat >expect <<-\EOF &&
+ |' master '|
+ |' side '|
+ |' odd/spot '|
+ |' double-tag '|
+ |' four '|
+ |' one '|
+ |' signed-tag '|
+ |' three '|
+ |' two '|
+ EOF
+ git for-each-ref --shell --format='|%(align:30,left)%(align:15,right)%(refname:short)%(end)%(end)|' >actual &&
+ test_cmp expect actual
+"
+
+test_expect_success 'check `%(contents:lines=1)`' '
+ cat >expect <<-\EOF &&
+ master |three
+ side |four
+ odd/spot |three
+ double-tag |Annonated doubly
+ four |four
+ one |one
+ signed-tag |A signed tag message
+ three |three
+ two |two
+ EOF
+ git for-each-ref --format="%(refname:short) |%(contents:lines=1)" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'check `%(contents:lines=0)`' '
+ cat >expect <<-\EOF &&
+ master |
+ side |
+ odd/spot |
+ double-tag |
+ four |
+ one |
+ signed-tag |
+ three |
+ two |
+ EOF
+ git for-each-ref --format="%(refname:short) |%(contents:lines=0)" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'check `%(contents:lines=99999)`' '
+ cat >expect <<-\EOF &&
+ master |three
+ side |four
+ odd/spot |three
+ double-tag |Annonated doubly
+ four |four
+ one |one
+ signed-tag |A signed tag message
+ three |three
+ two |two
+ EOF
+ git for-each-ref --format="%(refname:short) |%(contents:lines=99999)" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '`%(contents:lines=-1)` should fail' '
+ test_must_fail git for-each-ref --format="%(refname:short) |%(contents:lines=-1)"
+'
+
+test_expect_success 'setup for version sort' '
+ test_commit foo1.3 &&
+ test_commit foo1.6 &&
+ test_commit foo1.10
+'
+
+test_expect_success 'version sort' '
+ git for-each-ref --sort=version:refname --format="%(refname:short)" refs/tags/ | grep "foo" >actual &&
+ cat >expect <<-\EOF &&
+ foo1.3
+ foo1.6
+ foo1.10
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'version sort (shortened)' '
+ git for-each-ref --sort=v:refname --format="%(refname:short)" refs/tags/ | grep "foo" >actual &&
+ cat >expect <<-\EOF &&
+ foo1.3
+ foo1.6
+ foo1.10
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'reverse version sort' '
+ git for-each-ref --sort=-version:refname --format="%(refname:short)" refs/tags/ | grep "foo" >actual &&
+ cat >expect <<-\EOF &&
+ foo1.10
+ foo1.6
+ foo1.3
+ EOF
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t6500-gc.sh b/t/t6500-gc.sh
index 63194d819e..5d7d414617 100755
--- a/t/t6500-gc.sh
+++ b/t/t6500-gc.sh
@@ -30,4 +30,17 @@ test_expect_success 'gc -h with invalid configuration' '
test_i18ngrep "[Uu]sage" broken/usage
'
+test_expect_success 'gc is not aborted due to a stale symref' '
+ git init remote &&
+ (
+ cd remote &&
+ test_commit initial &&
+ git clone . ../client &&
+ git branch -m develop &&
+ cd ../client &&
+ git fetch --prune &&
+ git gc
+ )
+'
+
test_done
diff --git a/t/t7003-filter-branch.sh b/t/t7003-filter-branch.sh
index 855afda80a..377c648e04 100755
--- a/t/t7003-filter-branch.sh
+++ b/t/t7003-filter-branch.sh
@@ -2,6 +2,7 @@
test_description='git filter-branch'
. ./test-lib.sh
+. "$TEST_DIRECTORY/lib-gpg.sh"
test_expect_success 'setup' '
test_commit A &&
@@ -292,6 +293,19 @@ test_expect_success 'Tag name filtering strips gpg signature' '
test_cmp expect actual
'
+test_expect_success GPG 'Filtering retains message of gpg signed commit' '
+ mkdir gpg &&
+ touch gpg/foo &&
+ git add gpg &&
+ test_tick &&
+ git commit -S -m "Adding gpg" &&
+
+ git log -1 --format="%s" > expect &&
+ git filter-branch -f --msg-filter "cat" &&
+ git log -1 --format="%s" > actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'Tag name filtering allows slashes in tag names' '
git tag -m tag-with-slash X/1 &&
git cat-file tag X/1 | sed -e s,X/1,X/2, > expect &&
diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh
index d31788cc6c..3dd2f51e49 100755
--- a/t/t7004-tag.sh
+++ b/t/t7004-tag.sh
@@ -1462,13 +1462,7 @@ test_expect_success 'invalid sort parameter on command line' '
test_expect_success 'invalid sort parameter in configuratoin' '
git config tag.sort "v:notvalid" &&
- git tag -l "foo*" >actual &&
- cat >expect <<-\EOF &&
- foo1.10
- foo1.3
- foo1.6
- EOF
- test_cmp expect actual
+ test_must_fail git tag -l "foo*"
'
test_expect_success 'version sort with prerelease reordering' '
@@ -1525,4 +1519,43 @@ EOF"
test_cmp expect actual
'
+test_expect_success '--format should list tags as per format given' '
+ cat >expect <<-\EOF &&
+ refname : refs/tags/foo1.10
+ refname : refs/tags/foo1.3
+ refname : refs/tags/foo1.6
+ refname : refs/tags/foo1.6-rc1
+ refname : refs/tags/foo1.6-rc2
+ EOF
+ git tag -l --format="refname : %(refname)" "foo*" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'setup --merged test tags' '
+ git tag mergetest-1 HEAD~2 &&
+ git tag mergetest-2 HEAD~1 &&
+ git tag mergetest-3 HEAD
+'
+
+test_expect_success '--merged cannot be used in non-list mode' '
+ test_must_fail git tag --merged=mergetest-2 foo
+'
+
+test_expect_success '--merged shows merged tags' '
+ cat >expect <<-\EOF &&
+ mergetest-1
+ mergetest-2
+ EOF
+ git tag -l --merged=mergetest-2 mergetest-* >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--no-merged show unmerged tags' '
+ cat >expect <<-\EOF &&
+ mergetest-3
+ EOF
+ git tag -l --no-merged=mergetest-2 mergetest-* >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t7410-submodule-checkout-to.sh b/t/t7410-submodule-checkout-to.sh
index 3f609e8909..1acef32647 100755
--- a/t/t7410-submodule-checkout-to.sh
+++ b/t/t7410-submodule-checkout-to.sh
@@ -47,4 +47,14 @@ test_expect_success 'checkout main and initialize independed clones' \
test_expect_success 'can see submodule diffs after independed cloning' \
'(cd fully_cloned_submodule/main && git diff --submodule master"^!" | grep "file1 updated")'
+test_expect_success 'checkout sub manually' \
+ 'mkdir linked_submodule &&
+ (cd clone/main &&
+ git worktree add "$base_path/linked_submodule/main" "$rev1_hash_main") &&
+ (cd clone/main/sub &&
+ git worktree add "$base_path/linked_submodule/main/sub" "$rev1_hash_sub")'
+
+test_expect_success 'can see submodule diffs after manual checkout of linked submodule' \
+ '(cd linked_submodule/main && git diff --submodule master"^!" | grep "file1 updated")'
+
test_done
diff --git a/t/t7610-mergetool.sh b/t/t7610-mergetool.sh
index 7eeb207b32..6f12b235b3 100755
--- a/t/t7610-mergetool.sh
+++ b/t/t7610-mergetool.sh
@@ -174,9 +174,9 @@ test_expect_success 'mergetool skips autoresolved' '
'
test_expect_success 'mergetool merges all from subdir' '
+ test_config rerere.enabled false &&
(
cd subdir &&
- test_config rerere.enabled false &&
test_must_fail git merge master &&
( yes "r" | git mergetool ../submod ) &&
( yes "d" "d" | git mergetool --no-prompt ) &&
diff --git a/t/t7800-difftool.sh b/t/t7800-difftool.sh
index ea35a0241c..48c6e2bc83 100755
--- a/t/t7800-difftool.sh
+++ b/t/t7800-difftool.sh
@@ -492,12 +492,12 @@ test_expect_success PERL 'difftool --no-symlinks detects conflict ' '
test_expect_success PERL 'difftool properly honors gitlink and core.worktree' '
git submodule add ./. submod/ule &&
+ test_config -C submod/ule diff.tool checktrees &&
+ test_config -C submod/ule difftool.checktrees.cmd '\''
+ test -d "$LOCAL" && test -d "$REMOTE" && echo good
+ '\'' &&
(
cd submod/ule &&
- test_config diff.tool checktrees &&
- test_config difftool.checktrees.cmd '\''
- test -d "$LOCAL" && test -d "$REMOTE" && echo good
- '\'' &&
echo good >expect &&
git difftool --tool=checktrees --dir-diff HEAD~ >actual &&
test_cmp expect actual
diff --git a/t/t9811-git-p4-label-import.sh b/t/t9811-git-p4-label-import.sh
index 095238fffe..decb66ba30 100755
--- a/t/t9811-git-p4-label-import.sh
+++ b/t/t9811-git-p4-label-import.sh
@@ -214,6 +214,51 @@ test_expect_success 'use git config to enable import/export of tags' '
)
'
+p4_head_revision() {
+ p4 changes -m 1 "$@" | awk '{print $2}'
+}
+
+# Importing a label that references a P4 commit that
+# has not been seen. The presence of a label on a commit
+# we haven't seen should not cause git-p4 to fail. It should
+# merely skip that label, and still import other labels.
+test_expect_success 'importing labels with missing revisions' '
+ test_when_finished cleanup_git &&
+ (
+ rm -fr "$cli" "$git" &&
+ mkdir "$cli" &&
+ P4CLIENT=missing-revision &&
+ client_view "//depot/missing-revision/... //missing-revision/..." &&
+ cd "$cli" &&
+ >f1 && p4 add f1 && p4 submit -d "start" &&
+
+ p4 tag -l TAG_S0 ... &&
+
+ >f2 && p4 add f2 && p4 submit -d "second" &&
+
+ startrev=$(p4_head_revision //depot/missing-revision/...) &&
+
+ >f3 && p4 add f3 && p4 submit -d "third" &&
+
+ p4 edit f2 && date >f2 && p4 submit -d "change" f2 &&
+
+ endrev=$(p4_head_revision //depot/missing-revision/...) &&
+
+ p4 tag -l TAG_S1 ... &&
+
+ # we should skip TAG_S0 since it is before our startpoint,
+ # but pick up TAG_S1.
+
+ git p4 clone --dest="$git" --import-labels -v \
+ //depot/missing-revision/...@$startrev,$endrev &&
+ (
+ cd "$git" &&
+ git rev-parse TAG_S1 &&
+ ! git rev-parse TAG_S0
+ )
+ )
+'
+
test_expect_success 'kill p4d' '
kill_p4d
diff --git a/t/t9815-git-p4-submit-fail.sh b/t/t9815-git-p4-submit-fail.sh
index 4cff6a760f..37b42d03a2 100755
--- a/t/t9815-git-p4-submit-fail.sh
+++ b/t/t9815-git-p4-submit-fail.sh
@@ -417,11 +417,8 @@ test_expect_success 'cleanup chmod after submit cancel' '
! p4 fstat -T action text &&
test_path_is_file text+x &&
! p4 fstat -T action text+x &&
- if test_have_prereq !CYGWIN
- then
- stat --format=%A text | egrep ^-r-- &&
- stat --format=%A text+x | egrep ^-r-x
- fi
+ ls -l text | egrep ^-r-- &&
+ ls -l text+x | egrep ^-r-x
)
'
diff --git a/t/t9819-git-p4-case-folding.sh b/t/t9819-git-p4-case-folding.sh
index 78f1d0f92d..d808c008c1 100755
--- a/t/t9819-git-p4-case-folding.sh
+++ b/t/t9819-git-p4-case-folding.sh
@@ -4,6 +4,12 @@ test_description='interaction with P4 case-folding'
. ./lib-git-p4.sh
+if test_have_prereq CASE_INSENSITIVE_FS
+then
+ skip_all='skipping P4 case-folding tests; case insensitive file system detected'
+ test_done
+fi
+
test_expect_success 'start p4d with case folding enabled' '
start_p4d -C1
'
diff --git a/t/t9822-git-p4-path-encoding.sh b/t/t9822-git-p4-path-encoding.sh
new file mode 100755
index 0000000000..7b83e696a9
--- /dev/null
+++ b/t/t9822-git-p4-path-encoding.sh
@@ -0,0 +1,58 @@
+#!/bin/sh
+
+test_description='Clone repositories with non ASCII paths'
+
+. ./lib-git-p4.sh
+
+UTF8_ESCAPED="a-\303\244_o-\303\266_u-\303\274.txt"
+ISO8859_ESCAPED="a-\344_o-\366_u-\374.txt"
+
+test_expect_success 'start p4d' '
+ start_p4d
+'
+
+test_expect_success 'Create a repo containing iso8859-1 encoded paths' '
+ (
+ cd "$cli" &&
+ ISO8859="$(printf "$ISO8859_ESCAPED")" &&
+ echo content123 >"$ISO8859" &&
+ p4 add "$ISO8859" &&
+ p4 submit -d "test commit"
+ )
+'
+
+test_expect_failure 'Clone auto-detects depot with iso8859-1 paths' '
+ git p4 clone --destination="$git" //depot &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ UTF8="$(printf "$UTF8_ESCAPED")" &&
+ echo "$UTF8" >expect &&
+ git -c core.quotepath=false ls-files >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'Clone repo containing iso8859-1 encoded paths with git-p4.pathEncoding' '
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git init . &&
+ git config git-p4.pathEncoding iso8859-1 &&
+ git p4 clone --use-client-spec --destination="$git" //depot &&
+ UTF8="$(printf "$UTF8_ESCAPED")" &&
+ echo "$UTF8" >expect &&
+ git -c core.quotepath=false ls-files >actual &&
+ test_cmp expect actual &&
+
+ echo content123 >expect &&
+ cat "$UTF8" >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'kill p4d' '
+ kill_p4d
+'
+
+test_done
diff --git a/t/t9823-git-p4-mock-lfs.sh b/t/t9823-git-p4-mock-lfs.sh
new file mode 100755
index 0000000000..1f2dc369bf
--- /dev/null
+++ b/t/t9823-git-p4-mock-lfs.sh
@@ -0,0 +1,192 @@
+#!/bin/sh
+
+test_description='Clone repositories and store files in Mock LFS'
+
+. ./lib-git-p4.sh
+
+test_file_is_not_in_mock_lfs () {
+ FILE="$1" &&
+ CONTENT="$2" &&
+ echo "$CONTENT" >expect_content &&
+ test_path_is_file "$FILE" &&
+ test_cmp expect_content "$FILE"
+}
+
+test_file_is_in_mock_lfs () {
+ FILE="$1" &&
+ CONTENT="$2" &&
+ LOCAL_STORAGE=".git/mock-storage/local/$CONTENT" &&
+ SERVER_STORAGE=".git/mock-storage/remote/$CONTENT" &&
+ echo "pointer-$CONTENT" >expect_pointer &&
+ echo "$CONTENT" >expect_content &&
+ test_path_is_file "$FILE" &&
+ test_path_is_file "$LOCAL_STORAGE" &&
+ test_path_is_file "$SERVER_STORAGE" &&
+ test_cmp expect_pointer "$FILE" &&
+ test_cmp expect_content "$LOCAL_STORAGE" &&
+ test_cmp expect_content "$SERVER_STORAGE"
+}
+
+test_file_is_deleted_in_mock_lfs () {
+ FILE="$1" &&
+ CONTENT="$2" &&
+ LOCAL_STORAGE=".git/mock-storage/local/$CONTENT" &&
+ SERVER_STORAGE=".git/mock-storage/remote/$CONTENT" &&
+ echo "pointer-$CONTENT" >expect_pointer &&
+ echo "$CONTENT" >expect_content &&
+ test_path_is_missing "$FILE" &&
+ test_path_is_file "$LOCAL_STORAGE" &&
+ test_path_is_file "$SERVER_STORAGE" &&
+ test_cmp expect_content "$LOCAL_STORAGE" &&
+ test_cmp expect_content "$SERVER_STORAGE"
+}
+
+test_file_count_in_dir () {
+ DIR="$1" &&
+ EXPECTED_COUNT="$2" &&
+ find "$DIR" -type f >actual &&
+ test_line_count = $EXPECTED_COUNT actual
+}
+
+test_expect_success 'start p4d' '
+ start_p4d
+'
+
+test_expect_success 'Create repo with binary files' '
+ client_view "//depot/... //client/..." &&
+ (
+ cd "$cli" &&
+
+ echo "content 1 txt 23 bytes" >file1.txt &&
+ p4 add file1.txt &&
+ echo "content 2-3 bin 25 bytes" >file2.dat &&
+ p4 add file2.dat &&
+ p4 submit -d "Add text and binary file" &&
+
+ mkdir "path with spaces" &&
+ echo "content 2-3 bin 25 bytes" >"path with spaces/file3.bin" &&
+ p4 add "path with spaces/file3.bin" &&
+ p4 submit -d "Add another binary file with same content and spaces in path" &&
+
+ echo "content 4 bin 26 bytes XX" >file4.bin &&
+ p4 add file4.bin &&
+ p4 submit -d "Add another binary file with different content"
+ )
+'
+
+test_expect_success 'Store files in Mock LFS based on size (>24 bytes)' '
+ client_view "//depot/... //client/..." &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git init . &&
+ git config git-p4.useClientSpec true &&
+ git config git-p4.largeFileSystem MockLFS &&
+ git config git-p4.largeFileThreshold 24 &&
+ git config git-p4.largeFilePush True &&
+ git p4 clone --destination="$git" //depot@all &&
+
+ test_file_is_not_in_mock_lfs file1.txt "content 1 txt 23 bytes" &&
+ test_file_is_in_mock_lfs file2.dat "content 2-3 bin 25 bytes" &&
+ test_file_is_in_mock_lfs "path with spaces/file3.bin" "content 2-3 bin 25 bytes" &&
+ test_file_is_in_mock_lfs file4.bin "content 4 bin 26 bytes XX" &&
+
+ test_file_count_in_dir ".git/mock-storage/local" 2 &&
+ test_file_count_in_dir ".git/mock-storage/remote" 2
+ )
+'
+
+test_expect_success 'Store files in Mock LFS based on extension (dat)' '
+ client_view "//depot/... //client/..." &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git init . &&
+ git config git-p4.useClientSpec true &&
+ git config git-p4.largeFileSystem MockLFS &&
+ git config git-p4.largeFileExtensions dat &&
+ git config git-p4.largeFilePush True &&
+ git p4 clone --destination="$git" //depot@all &&
+
+ test_file_is_not_in_mock_lfs file1.txt "content 1 txt 23 bytes" &&
+ test_file_is_in_mock_lfs file2.dat "content 2-3 bin 25 bytes" &&
+ test_file_is_not_in_mock_lfs "path with spaces/file3.bin" "content 2-3 bin 25 bytes" &&
+ test_file_is_not_in_mock_lfs file4.bin "content 4 bin 26 bytes XX" &&
+
+ test_file_count_in_dir ".git/mock-storage/local" 1 &&
+ test_file_count_in_dir ".git/mock-storage/remote" 1
+ )
+'
+
+test_expect_success 'Store files in Mock LFS based on extension (dat) and use git p4 sync and no client spec' '
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git init &&
+ git config git-p4.useClientSpec true &&
+ git config git-p4.largeFileSystem MockLFS &&
+ git config git-p4.largeFileExtensions dat &&
+ git config git-p4.largeFilePush True &&
+ git p4 sync //depot &&
+ git checkout p4/master &&
+
+ test_file_is_not_in_mock_lfs file1.txt "content 1 txt 23 bytes" &&
+ test_file_is_in_mock_lfs file2.dat "content 2-3 bin 25 bytes" &&
+ test_file_is_not_in_mock_lfs "path with spaces/file3.bin" "content 2-3 bin 25 bytes" &&
+ test_file_is_not_in_mock_lfs file4.bin "content 4 bin 26 bytes XX" &&
+
+ test_file_count_in_dir ".git/mock-storage/local" 1 &&
+ test_file_count_in_dir ".git/mock-storage/remote" 1
+ )
+'
+
+test_expect_success 'Remove file from repo and store files in Mock LFS based on size (>24 bytes)' '
+ client_view "//depot/... //client/..." &&
+ (
+ cd "$cli" &&
+ p4 delete file4.bin &&
+ p4 submit -d "Remove file"
+ ) &&
+
+ client_view "//depot/... //client/..." &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git init . &&
+ git config git-p4.useClientSpec true &&
+ git config git-p4.largeFileSystem MockLFS &&
+ git config git-p4.largeFileThreshold 24 &&
+ git config git-p4.largeFilePush True &&
+ git p4 clone --destination="$git" //depot@all &&
+
+ test_file_is_not_in_mock_lfs file1.txt "content 1 txt 23 bytes" &&
+ test_file_is_in_mock_lfs file2.dat "content 2-3 bin 25 bytes" &&
+ test_file_is_in_mock_lfs "path with spaces/file3.bin" "content 2-3 bin 25 bytes" &&
+ test_file_is_deleted_in_mock_lfs file4.bin "content 4 bin 26 bytes XX" &&
+
+ test_file_count_in_dir ".git/mock-storage/local" 2 &&
+ test_file_count_in_dir ".git/mock-storage/remote" 2
+ )
+'
+
+test_expect_success 'Run git p4 submit in repo configured with large file system' '
+ client_view "//depot/... //client/..." &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git init . &&
+ git config git-p4.useClientSpec true &&
+ git config git-p4.largeFileSystem MockLFS &&
+ git config git-p4.largeFileThreshold 24 &&
+ git config git-p4.largeFilePush True &&
+ git p4 clone --destination="$git" //depot@all &&
+
+ test_must_fail git p4 submit
+ )
+'
+
+test_expect_success 'kill p4d' '
+ kill_p4d
+'
+
+test_done
diff --git a/t/t9824-git-p4-git-lfs.sh b/t/t9824-git-p4-git-lfs.sh
new file mode 100755
index 0000000000..0b664a377c
--- /dev/null
+++ b/t/t9824-git-p4-git-lfs.sh
@@ -0,0 +1,288 @@
+#!/bin/sh
+
+test_description='Clone repositories and store files in Git LFS'
+
+. ./lib-git-p4.sh
+
+git lfs help >/dev/null 2>&1 || {
+ skip_all='skipping git p4 Git LFS tests; Git LFS not found'
+ test_done
+}
+
+test_file_in_lfs () {
+ FILE="$1" &&
+ SIZE="$2" &&
+ EXPECTED_CONTENT="$3" &&
+ cat "$FILE" | grep "size $SIZE" &&
+ HASH=$(cat "$FILE" | grep "oid sha256:" | sed -e "s/oid sha256://g") &&
+ LFS_FILE=".git/lfs/objects/$(echo "$HASH" | cut -c1-2)/$(echo "$HASH" | cut -c3-4)/$HASH" &&
+ echo $EXPECTED_CONTENT >expect &&
+ test_path_is_file "$FILE" &&
+ test_path_is_file "$LFS_FILE" &&
+ test_cmp expect "$LFS_FILE"
+}
+
+test_file_count_in_dir () {
+ DIR="$1" &&
+ EXPECTED_COUNT="$2" &&
+ find "$DIR" -type f >actual &&
+ test_line_count = $EXPECTED_COUNT actual
+}
+
+test_expect_success 'start p4d' '
+ start_p4d
+'
+
+test_expect_success 'Create repo with binary files' '
+ client_view "//depot/... //client/..." &&
+ (
+ cd "$cli" &&
+
+ echo "content 1 txt 23 bytes" >file1.txt &&
+ p4 add file1.txt &&
+ echo "content 2-3 bin 25 bytes" >file2.dat &&
+ p4 add file2.dat &&
+ p4 submit -d "Add text and binary file" &&
+
+ mkdir "path with spaces" &&
+ echo "content 2-3 bin 25 bytes" >"path with spaces/file3.bin" &&
+ p4 add "path with spaces/file3.bin" &&
+ p4 submit -d "Add another binary file with same content and spaces in path" &&
+
+ echo "content 4 bin 26 bytes XX" >file4.bin &&
+ p4 add file4.bin &&
+ p4 submit -d "Add another binary file with different content"
+ )
+'
+
+test_expect_success 'Store files in LFS based on size (>24 bytes)' '
+ client_view "//depot/... //client/..." &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git init . &&
+ git config git-p4.useClientSpec true &&
+ git config git-p4.largeFileSystem GitLFS &&
+ git config git-p4.largeFileThreshold 24 &&
+ git p4 clone --destination="$git" //depot@all &&
+
+ test_file_in_lfs file2.dat 25 "content 2-3 bin 25 bytes" &&
+ test_file_in_lfs "path with spaces/file3.bin" 25 "content 2-3 bin 25 bytes" &&
+ test_file_in_lfs file4.bin 26 "content 4 bin 26 bytes XX" &&
+
+ test_file_count_in_dir ".git/lfs/objects" 2 &&
+
+ cat >expect <<-\EOF &&
+
+ #
+ # Git LFS (see https://git-lfs.github.com/)
+ #
+ /file2.dat filter=lfs -text
+ /file4.bin filter=lfs -text
+ /path[[:space:]]with[[:space:]]spaces/file3.bin filter=lfs -text
+ EOF
+ test_path_is_file .gitattributes &&
+ test_cmp expect .gitattributes
+ )
+'
+
+test_expect_success 'Store files in LFS based on size (>25 bytes)' '
+ client_view "//depot/... //client/..." &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git init . &&
+ git config git-p4.useClientSpec true &&
+ git config git-p4.largeFileSystem GitLFS &&
+ git config git-p4.largeFileThreshold 25 &&
+ git p4 clone --destination="$git" //depot@all &&
+
+ test_file_in_lfs file4.bin 26 "content 4 bin 26 bytes XX" &&
+ test_file_count_in_dir ".git/lfs/objects" 1 &&
+
+ cat >expect <<-\EOF &&
+
+ #
+ # Git LFS (see https://git-lfs.github.com/)
+ #
+ /file4.bin filter=lfs -text
+ EOF
+ test_path_is_file .gitattributes &&
+ test_cmp expect .gitattributes
+ )
+'
+
+test_expect_success 'Store files in LFS based on extension (dat)' '
+ client_view "//depot/... //client/..." &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git init . &&
+ git config git-p4.useClientSpec true &&
+ git config git-p4.largeFileSystem GitLFS &&
+ git config git-p4.largeFileExtensions dat &&
+ git p4 clone --destination="$git" //depot@all &&
+
+ test_file_in_lfs file2.dat 25 "content 2-3 bin 25 bytes" &&
+ test_file_count_in_dir ".git/lfs/objects" 1 &&
+
+ cat >expect <<-\EOF &&
+
+ #
+ # Git LFS (see https://git-lfs.github.com/)
+ #
+ *.dat filter=lfs -text
+ EOF
+ test_path_is_file .gitattributes &&
+ test_cmp expect .gitattributes
+ )
+'
+
+test_expect_success 'Store files in LFS based on size (>25 bytes) and extension (dat)' '
+ client_view "//depot/... //client/..." &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git init . &&
+ git config git-p4.useClientSpec true &&
+ git config git-p4.largeFileSystem GitLFS &&
+ git config git-p4.largeFileExtensions dat &&
+ git config git-p4.largeFileThreshold 25 &&
+ git p4 clone --destination="$git" //depot@all &&
+
+ test_file_in_lfs file2.dat 25 "content 2-3 bin 25 bytes" &&
+ test_file_in_lfs file4.bin 26 "content 4 bin 26 bytes XX" &&
+ test_file_count_in_dir ".git/lfs/objects" 2 &&
+
+ cat >expect <<-\EOF &&
+
+ #
+ # Git LFS (see https://git-lfs.github.com/)
+ #
+ *.dat filter=lfs -text
+ /file4.bin filter=lfs -text
+ EOF
+ test_path_is_file .gitattributes &&
+ test_cmp expect .gitattributes
+ )
+'
+
+test_expect_success 'Remove file from repo and store files in LFS based on size (>24 bytes)' '
+ client_view "//depot/... //client/..." &&
+ (
+ cd "$cli" &&
+ p4 delete file4.bin &&
+ p4 submit -d "Remove file"
+ ) &&
+
+ client_view "//depot/... //client/..." &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git init . &&
+ git config git-p4.useClientSpec true &&
+ git config git-p4.largeFileSystem GitLFS &&
+ git config git-p4.largeFileThreshold 24 &&
+ git p4 clone --destination="$git" //depot@all &&
+
+ test_file_in_lfs file2.dat 25 "content 2-3 bin 25 bytes" &&
+ test_file_in_lfs "path with spaces/file3.bin" 25 "content 2-3 bin 25 bytes" &&
+ test_path_is_missing file4.bin &&
+ test_file_count_in_dir ".git/lfs/objects" 2 &&
+
+ cat >expect <<-\EOF &&
+
+ #
+ # Git LFS (see https://git-lfs.github.com/)
+ #
+ /file2.dat filter=lfs -text
+ /path[[:space:]]with[[:space:]]spaces/file3.bin filter=lfs -text
+ EOF
+ test_path_is_file .gitattributes &&
+ test_cmp expect .gitattributes
+ )
+'
+
+test_expect_success 'Add .gitattributes and store files in LFS based on size (>24 bytes)' '
+ client_view "//depot/... //client/..." &&
+ (
+ cd "$cli" &&
+ echo "*.txt text" >.gitattributes &&
+ p4 add .gitattributes &&
+ p4 submit -d "Add .gitattributes"
+ ) &&
+
+ client_view "//depot/... //client/..." &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git init . &&
+ git config git-p4.useClientSpec true &&
+ git config git-p4.largeFileSystem GitLFS &&
+ git config git-p4.largeFileThreshold 24 &&
+ git p4 clone --destination="$git" //depot@all &&
+
+ test_file_in_lfs file2.dat 25 "content 2-3 bin 25 bytes" &&
+ test_file_in_lfs "path with spaces/file3.bin" 25 "content 2-3 bin 25 bytes" &&
+ test_path_is_missing file4.bin &&
+ test_file_count_in_dir ".git/lfs/objects" 2 &&
+
+ cat >expect <<-\EOF &&
+ *.txt text
+
+ #
+ # Git LFS (see https://git-lfs.github.com/)
+ #
+ /file2.dat filter=lfs -text
+ /path[[:space:]]with[[:space:]]spaces/file3.bin filter=lfs -text
+ EOF
+ test_path_is_file .gitattributes &&
+ test_cmp expect .gitattributes
+ )
+'
+
+test_expect_success 'Add big files to repo and store files in LFS based on compressed size (>28 bytes)' '
+ client_view "//depot/... //client/..." &&
+ (
+ cd "$cli" &&
+ echo "content 5 bin 40 bytes XXXXXXXXXXXXXXXX" >file5.bin &&
+ p4 add file5.bin &&
+ p4 submit -d "Add file with small footprint after compression" &&
+
+ echo "content 6 bin 39 bytes XXXXXYYYYYZZZZZ" >file6.bin &&
+ p4 add file6.bin &&
+ p4 submit -d "Add file with large footprint after compression"
+ ) &&
+
+ client_view "//depot/... //client/..." &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ git init . &&
+ git config git-p4.useClientSpec true &&
+ git config git-p4.largeFileSystem GitLFS &&
+ git config git-p4.largeFileCompressedThreshold 28 &&
+ # We only import HEAD here ("@all" is missing!)
+ git p4 clone --destination="$git" //depot &&
+
+ test_file_in_lfs file6.bin 13 "content 6 bin 39 bytes XXXXXYYYYYZZZZZ"
+ test_file_count_in_dir ".git/lfs/objects" 1 &&
+
+ cat >expect <<-\EOF &&
+ *.txt text
+
+ #
+ # Git LFS (see https://git-lfs.github.com/)
+ #
+ /file6.bin filter=lfs -text
+ EOF
+ test_path_is_file .gitattributes &&
+ test_cmp expect .gitattributes
+ )
+'
+
+test_expect_success 'kill p4d' '
+ kill_p4d
+'
+
+test_done
diff --git a/t/t9825-git-p4-handle-utf16-without-bom.sh b/t/t9825-git-p4-handle-utf16-without-bom.sh
new file mode 100755
index 0000000000..1551845dc1
--- /dev/null
+++ b/t/t9825-git-p4-handle-utf16-without-bom.sh
@@ -0,0 +1,50 @@
+#!/bin/sh
+
+test_description='git p4 handling of UTF-16 files without BOM'
+
+. ./lib-git-p4.sh
+
+UTF16="\227\000\227\000"
+
+test_expect_success 'start p4d' '
+ start_p4d
+'
+
+test_expect_success 'init depot with UTF-16 encoded file and artificially remove BOM' '
+ (
+ cd "$cli" &&
+ printf "$UTF16" >file1 &&
+ p4 add -t utf16 file1 &&
+ p4 submit -d "file1"
+ ) &&
+
+ (
+ cd db &&
+ p4d -jc &&
+ # P4D automatically adds a BOM. Remove it here to make the file invalid.
+ sed -e "\$d" depot/file1,v >depot/file1,v.new &&
+ mv depot/file1,v.new depot/file1,v &&
+ printf "@$UTF16@" >>depot/file1,v &&
+ p4d -jrF checkpoint.1
+ )
+'
+
+test_expect_success 'clone depot with invalid UTF-16 file in verbose mode' '
+ git p4 clone --dest="$git" --verbose //depot &&
+ test_when_finished cleanup_git &&
+ (
+ cd "$git" &&
+ printf "$UTF16" >expect &&
+ test_cmp_bin expect file1
+ )
+'
+
+test_expect_failure 'clone depot with invalid UTF-16 file in non-verbose mode' '
+ git p4 clone --dest="$git" //depot
+'
+
+test_expect_success 'kill p4d' '
+ kill_p4d
+'
+
+test_done
diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
index e8d3c0fdbc..6dffb8bcde 100644
--- a/t/test-lib-functions.sh
+++ b/t/test-lib-functions.sh
@@ -201,7 +201,14 @@ test_chmod () {
# Unset a configuration variable, but don't fail if it doesn't exist.
test_unconfig () {
- git config --unset-all "$@"
+ config_dir=
+ if test "$1" = -C
+ then
+ shift
+ config_dir=$1
+ shift
+ fi
+ git ${config_dir:+-C "$config_dir"} config --unset-all "$@"
config_status=$?
case "$config_status" in
5) # ok, nothing to unset
@@ -213,8 +220,15 @@ test_unconfig () {
# Set git config, automatically unsetting it after the test is over.
test_config () {
- test_when_finished "test_unconfig '$1'" &&
- git config "$@"
+ config_dir=
+ if test "$1" = -C
+ then
+ shift
+ config_dir=$1
+ shift
+ fi
+ test_when_finished "test_unconfig ${config_dir:+-C '$config_dir'} '$1'" &&
+ git ${config_dir:+-C "$config_dir"} config "$@"
}
test_config_global () {
@@ -722,6 +736,11 @@ test_seq () {
# what went wrong.
test_when_finished () {
+ # We cannot detect when we are in a subshell in general, but by
+ # doing so on Bash is better than nothing (the test will
+ # silently pass on other shells).
+ test "${BASH_SUBSHELL-0}" = 0 ||
+ error "bug in test script: test_when_finished does nothing in a subshell"
test_cleanup="{ $*
} && (exit \"\$eval_ret\"); eval_ret=\$?; $test_cleanup"
}
diff --git a/tag.c b/tag.c
index 5b0ac62ed8..5b2a06d92b 100644
--- a/tag.c
+++ b/tag.c
@@ -82,7 +82,7 @@ int parse_tag_buffer(struct tag *item, const void *data, unsigned long size)
nl = memchr(bufptr, '\n', tail - bufptr);
if (!nl || sizeof(type) <= (nl - bufptr))
return -1;
- strncpy(type, bufptr, nl - bufptr);
+ memcpy(type, bufptr, nl - bufptr);
type[nl - bufptr] = '\0';
bufptr = nl + 1;
diff --git a/test-dump-cache-tree.c b/test-dump-cache-tree.c
index 54c0872fcb..bb53c0aa65 100644
--- a/test-dump-cache-tree.c
+++ b/test-dump-cache-tree.c
@@ -47,7 +47,7 @@ static int dump_cache_tree(struct cache_tree *it,
struct cache_tree_sub *rdwn;
rdwn = cache_tree_sub(ref, down->name);
- sprintf(path, "%s%.*s/", pfx, down->namelen, down->name);
+ xsnprintf(path, sizeof(path), "%s%.*s/", pfx, down->namelen, down->name);
if (dump_cache_tree(down->cache_tree, rdwn->cache_tree, path))
errs = 1;
}
diff --git a/test-path-utils.c b/test-path-utils.c
index 3dd3744a57..c67bf65b34 100644
--- a/test-path-utils.c
+++ b/test-path-utils.c
@@ -21,8 +21,6 @@ static int normalize_ceiling_entry(struct string_list_item *item, void *unused)
if (normalize_path_copy(buf, ceil) < 0)
die("Path \"%s\" could not be normalized", ceil);
len = strlen(buf);
- if (len > 1 && buf[len-1] == '/')
- die("Normalized path \"%s\" ended with slash", buf);
free(item->string);
item->string = xstrdup(buf);
return 1;
diff --git a/trace.c b/trace.c
index 7393926ebc..4aeea60973 100644
--- a/trace.c
+++ b/trace.c
@@ -277,25 +277,24 @@ void trace_performance_fl(const char *file, int line, uint64_t nanos,
static const char *quote_crnl(const char *path)
{
- static char new_path[PATH_MAX];
- const char *p2 = path;
- char *p1 = new_path;
+ static struct strbuf new_path = STRBUF_INIT;
if (!path)
return NULL;
- while (*p2) {
- switch (*p2) {
- case '\\': *p1++ = '\\'; *p1++ = '\\'; break;
- case '\n': *p1++ = '\\'; *p1++ = 'n'; break;
- case '\r': *p1++ = '\\'; *p1++ = 'r'; break;
+ strbuf_reset(&new_path);
+
+ while (*path) {
+ switch (*path) {
+ case '\\': strbuf_addstr(&new_path, "\\\\"); break;
+ case '\n': strbuf_addstr(&new_path, "\\n"); break;
+ case '\r': strbuf_addstr(&new_path, "\\r"); break;
default:
- *p1++ = *p2;
+ strbuf_addch(&new_path, *path);
}
- p2++;
+ path++;
}
- *p1 = '\0';
- return new_path;
+ return new_path.buf;
}
/* FIXME: move prefix to startup_info struct and get rid of this arg */
diff --git a/transport.c b/transport.c
index 863eb524f9..23b2ed6f0c 100644
--- a/transport.c
+++ b/transport.c
@@ -654,23 +654,24 @@ static void print_ok_ref_status(struct ref *ref, int porcelain)
"[new branch]"),
ref, ref->peer_ref, NULL, porcelain);
else {
- char quickref[84];
+ struct strbuf quickref = STRBUF_INIT;
char type;
const char *msg;
- strcpy(quickref, status_abbrev(ref->old_sha1));
+ strbuf_addstr(&quickref, status_abbrev(ref->old_sha1));
if (ref->forced_update) {
- strcat(quickref, "...");
+ strbuf_addstr(&quickref, "...");
type = '+';
msg = "forced update";
} else {
- strcat(quickref, "..");
+ strbuf_addstr(&quickref, "..");
type = ' ';
msg = NULL;
}
- strcat(quickref, status_abbrev(ref->new_sha1));
+ strbuf_addstr(&quickref, status_abbrev(ref->new_sha1));
- print_ref_status(type, quickref, ref, ref->peer_ref, msg, porcelain);
+ print_ref_status(type, quickref.buf, ref, ref->peer_ref, msg, porcelain);
+ strbuf_release(&quickref);
}
}
diff --git a/unpack-trees.c b/unpack-trees.c
index f932e80e86..8e2032f4e5 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -1350,9 +1350,7 @@ static int verify_clean_subdirectory(const struct cache_entry *ce,
* Then we need to make sure that we do not lose a locally
* present file that is not ignored.
*/
- pathbuf = xmalloc(namelen + 2);
- memcpy(pathbuf, ce->name, namelen);
- strcpy(pathbuf+namelen, "/");
+ pathbuf = xstrfmt("%.*s/", namelen, ce->name);
memset(&d, 0, sizeof(d));
if (o->dir)
diff --git a/url.c b/url.c
index 7ca2a69e10..2d89ad190c 100644
--- a/url.c
+++ b/url.c
@@ -120,8 +120,7 @@ char *url_decode_parameter_value(const char **query)
void end_url_with_slash(struct strbuf *buf, const char *url)
{
strbuf_addstr(buf, url);
- if (buf->len && buf->buf[buf->len - 1] != '/')
- strbuf_addch(buf, '/');
+ strbuf_complete(buf, '/');
}
void str_end_url_with_slash(const char *url, char **dest) {
diff --git a/utf8.c b/utf8.c
index 28e6d76a42..00e10c86ad 100644
--- a/utf8.c
+++ b/utf8.c
@@ -644,3 +644,24 @@ int skip_utf8_bom(char **text, size_t len)
*text += strlen(utf8_bom);
return 1;
}
+
+void strbuf_utf8_align(struct strbuf *buf, align_type position, unsigned int width,
+ const char *s)
+{
+ int slen = strlen(s);
+ int display_len = utf8_strnwidth(s, slen, 0);
+ int utf8_compensation = slen - display_len;
+
+ if (display_len >= width) {
+ strbuf_addstr(buf, s);
+ return;
+ }
+
+ if (position == ALIGN_LEFT)
+ strbuf_addf(buf, "%-*s", width + utf8_compensation, s);
+ else if (position == ALIGN_MIDDLE) {
+ int left = (width - display_len) / 2;
+ strbuf_addf(buf, "%*s%-*s", left, "", width - left + utf8_compensation, s);
+ } else if (position == ALIGN_RIGHT)
+ strbuf_addf(buf, "%*s", width + utf8_compensation, s);
+}
diff --git a/utf8.h b/utf8.h
index 5a9e94bee6..7930b44f19 100644
--- a/utf8.h
+++ b/utf8.h
@@ -55,4 +55,19 @@ int mbs_chrlen(const char **text, size_t *remainder_p, const char *encoding);
*/
int is_hfs_dotgit(const char *path);
+typedef enum {
+ ALIGN_LEFT,
+ ALIGN_MIDDLE,
+ ALIGN_RIGHT
+} align_type;
+
+/*
+ * Align the string given and store it into a strbuf as per the
+ * 'position' and 'width'. If the given string length is larger than
+ * 'width' than then the input string is not truncated and no
+ * alignment is done.
+ */
+void strbuf_utf8_align(struct strbuf *buf, align_type position, unsigned int width,
+ const char *s);
+
#endif
diff --git a/walker.c b/walker.c
index 44a936c1cf..cdeb63f319 100644
--- a/walker.c
+++ b/walker.c
@@ -17,10 +17,9 @@ void walker_say(struct walker *walker, const char *fmt, const char *hex)
static void report_missing(const struct object *obj)
{
- char missing_hex[41];
- strcpy(missing_hex, sha1_to_hex(obj->sha1));
fprintf(stderr, "Cannot obtain needed %s %s\n",
- obj->type ? typename(obj->type): "object", missing_hex);
+ obj->type ? typename(obj->type): "object",
+ sha1_to_hex(obj->sha1));
if (!is_null_sha1(current_commit_sha1))
fprintf(stderr, "while processing commit %s.\n",
sha1_to_hex(current_commit_sha1));
diff --git a/worktree.c b/worktree.c
new file mode 100644
index 0000000000..981f810e80
--- /dev/null
+++ b/worktree.c
@@ -0,0 +1,219 @@
+#include "cache.h"
+#include "refs.h"
+#include "strbuf.h"
+#include "worktree.h"
+
+void free_worktrees(struct worktree **worktrees)
+{
+ int i = 0;
+
+ for (i = 0; worktrees[i]; i++) {
+ free(worktrees[i]->path);
+ free(worktrees[i]->git_dir);
+ free(worktrees[i]->head_ref);
+ free(worktrees[i]);
+ }
+ free (worktrees);
+}
+
+/*
+ * read 'path_to_ref' into 'ref'. Also if is_detached is not NULL,
+ * set is_detached to 1 (0) if the ref is detatched (is not detached).
+ *
+ * $GIT_COMMON_DIR/$symref (e.g. HEAD) is practically outside $GIT_DIR so
+ * for linked worktrees, `resolve_ref_unsafe()` won't work (it uses
+ * git_path). Parse the ref ourselves.
+ *
+ * return -1 if the ref is not a proper ref, 0 otherwise (success)
+ */
+static int parse_ref(char *path_to_ref, struct strbuf *ref, int *is_detached)
+{
+ if (is_detached)
+ *is_detached = 0;
+ if (!strbuf_readlink(ref, path_to_ref, 0)) {
+ /* HEAD is symbolic link */
+ if (!starts_with(ref->buf, "refs/") ||
+ check_refname_format(ref->buf, 0))
+ return -1;
+ } else if (strbuf_read_file(ref, path_to_ref, 0) >= 0) {
+ /* textual symref or detached */
+ if (!starts_with(ref->buf, "ref:")) {
+ if (is_detached)
+ *is_detached = 1;
+ } else {
+ strbuf_remove(ref, 0, strlen("ref:"));
+ strbuf_trim(ref);
+ if (check_refname_format(ref->buf, 0))
+ return -1;
+ }
+ } else
+ return -1;
+ return 0;
+}
+
+/**
+ * Add the head_sha1 and head_ref (if not detached) to the given worktree
+ */
+static void add_head_info(struct strbuf *head_ref, struct worktree *worktree)
+{
+ if (head_ref->len) {
+ if (worktree->is_detached) {
+ get_sha1_hex(head_ref->buf, worktree->head_sha1);
+ } else {
+ resolve_ref_unsafe(head_ref->buf, 0, worktree->head_sha1, NULL);
+ worktree->head_ref = strbuf_detach(head_ref, NULL);
+ }
+ }
+}
+
+/**
+ * get the main worktree
+ */
+static struct worktree *get_main_worktree(void)
+{
+ struct worktree *worktree = NULL;
+ struct strbuf path = STRBUF_INIT;
+ struct strbuf worktree_path = STRBUF_INIT;
+ struct strbuf gitdir = STRBUF_INIT;
+ struct strbuf head_ref = STRBUF_INIT;
+ int is_bare = 0;
+ int is_detached = 0;
+
+ strbuf_addf(&gitdir, "%s", absolute_path(get_git_common_dir()));
+ strbuf_addbuf(&worktree_path, &gitdir);
+ is_bare = !strbuf_strip_suffix(&worktree_path, "/.git");
+ if (is_bare)
+ strbuf_strip_suffix(&worktree_path, "/.");
+
+ strbuf_addf(&path, "%s/HEAD", get_git_common_dir());
+
+ if (parse_ref(path.buf, &head_ref, &is_detached) < 0)
+ goto done;
+
+ worktree = xmalloc(sizeof(struct worktree));
+ worktree->path = strbuf_detach(&worktree_path, NULL);
+ worktree->git_dir = strbuf_detach(&gitdir, NULL);
+ worktree->is_bare = is_bare;
+ worktree->head_ref = NULL;
+ worktree->is_detached = is_detached;
+ add_head_info(&head_ref, worktree);
+
+done:
+ strbuf_release(&path);
+ strbuf_release(&gitdir);
+ strbuf_release(&worktree_path);
+ strbuf_release(&head_ref);
+ return worktree;
+}
+
+static struct worktree *get_linked_worktree(const char *id)
+{
+ struct worktree *worktree = NULL;
+ struct strbuf path = STRBUF_INIT;
+ struct strbuf worktree_path = STRBUF_INIT;
+ struct strbuf gitdir = STRBUF_INIT;
+ struct strbuf head_ref = STRBUF_INIT;
+ int is_detached = 0;
+
+ if (!id)
+ die("Missing linked worktree name");
+
+ strbuf_addf(&gitdir, "%s/worktrees/%s",
+ absolute_path(get_git_common_dir()), id);
+ strbuf_addf(&path, "%s/gitdir", gitdir.buf);
+ if (strbuf_read_file(&worktree_path, path.buf, 0) <= 0)
+ /* invalid gitdir file */
+ goto done;
+
+ strbuf_rtrim(&worktree_path);
+ if (!strbuf_strip_suffix(&worktree_path, "/.git")) {
+ strbuf_reset(&worktree_path);
+ strbuf_addstr(&worktree_path, absolute_path("."));
+ strbuf_strip_suffix(&worktree_path, "/.");
+ }
+
+ strbuf_reset(&path);
+ strbuf_addf(&path, "%s/worktrees/%s/HEAD", get_git_common_dir(), id);
+
+ if (parse_ref(path.buf, &head_ref, &is_detached) < 0)
+ goto done;
+
+ worktree = xmalloc(sizeof(struct worktree));
+ worktree->path = strbuf_detach(&worktree_path, NULL);
+ worktree->git_dir = strbuf_detach(&gitdir, NULL);
+ worktree->is_bare = 0;
+ worktree->head_ref = NULL;
+ worktree->is_detached = is_detached;
+ add_head_info(&head_ref, worktree);
+
+done:
+ strbuf_release(&path);
+ strbuf_release(&gitdir);
+ strbuf_release(&worktree_path);
+ strbuf_release(&head_ref);
+ return worktree;
+}
+
+struct worktree **get_worktrees(void)
+{
+ struct worktree **list = NULL;
+ struct strbuf path = STRBUF_INIT;
+ DIR *dir;
+ struct dirent *d;
+ int counter = 0, alloc = 2;
+
+ list = xmalloc(alloc * sizeof(struct worktree *));
+
+ if ((list[counter] = get_main_worktree()))
+ counter++;
+
+ strbuf_addf(&path, "%s/worktrees", get_git_common_dir());
+ dir = opendir(path.buf);
+ strbuf_release(&path);
+ if (dir) {
+ while ((d = readdir(dir)) != NULL) {
+ struct worktree *linked = NULL;
+ if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
+ continue;
+
+ if ((linked = get_linked_worktree(d->d_name))) {
+ ALLOC_GROW(list, counter + 1, alloc);
+ list[counter++] = linked;
+ }
+ }
+ closedir(dir);
+ }
+ ALLOC_GROW(list, counter + 1, alloc);
+ list[counter] = NULL;
+ return list;
+}
+
+char *find_shared_symref(const char *symref, const char *target)
+{
+ char *existing = NULL;
+ struct strbuf path = STRBUF_INIT;
+ struct strbuf sb = STRBUF_INIT;
+ struct worktree **worktrees = get_worktrees();
+ int i = 0;
+
+ for (i = 0; worktrees[i]; i++) {
+ strbuf_reset(&path);
+ strbuf_reset(&sb);
+ strbuf_addf(&path, "%s/%s", worktrees[i]->git_dir, symref);
+
+ if (parse_ref(path.buf, &sb, NULL)) {
+ continue;
+ }
+
+ if (!strcmp(sb.buf, target)) {
+ existing = xstrdup(worktrees[i]->path);
+ break;
+ }
+ }
+
+ strbuf_release(&path);
+ strbuf_release(&sb);
+ free_worktrees(worktrees);
+
+ return existing;
+}
diff --git a/worktree.h b/worktree.h
new file mode 100644
index 0000000000..b4b3dda792
--- /dev/null
+++ b/worktree.h
@@ -0,0 +1,38 @@
+#ifndef WORKTREE_H
+#define WORKTREE_H
+
+struct worktree {
+ char *path;
+ char *git_dir;
+ char *head_ref;
+ unsigned char head_sha1[20];
+ int is_detached;
+ int is_bare;
+};
+
+/* Functions for acting on the information about worktrees. */
+
+/*
+ * Get the worktrees. The primary worktree will always be the first returned,
+ * and linked worktrees will be pointed to by 'next' in each subsequent
+ * worktree. No specific ordering is done on the linked worktrees.
+ *
+ * The caller is responsible for freeing the memory from the returned
+ * worktree(s).
+ */
+extern struct worktree **get_worktrees(void);
+
+/*
+ * Free up the memory for worktree(s)
+ */
+extern void free_worktrees(struct worktree **);
+
+/*
+ * Check if a per-worktree symref points to a ref in the main worktree
+ * or any linked worktree, and return the path to the exising worktree
+ * if it is. Returns NULL if there is no existing ref. The caller is
+ * responsible for freeing the returned path.
+ */
+extern char *find_shared_symref(const char *symref, const char *target);
+
+#endif
diff --git a/wrapper.c b/wrapper.c
index 0e22d43814..6fcaa4dc62 100644
--- a/wrapper.c
+++ b/wrapper.c
@@ -621,6 +621,22 @@ char *xgetcwd(void)
return strbuf_detach(&sb, NULL);
}
+int xsnprintf(char *dst, size_t max, const char *fmt, ...)
+{
+ va_list ap;
+ int len;
+
+ va_start(ap, fmt);
+ len = vsnprintf(dst, max, fmt, ap);
+ va_end(ap);
+
+ if (len < 0)
+ die("BUG: your snprintf is broken");
+ if (len >= max)
+ die("BUG: attempt to snprintf into too-small buffer");
+ return len;
+}
+
static int write_file_v(const char *path, int fatal,
const char *fmt, va_list params)
{