Merge branch 'ar/clone-dissociate'
authorJunio C Hamano <gitster@pobox.com>
Fri, 30 Oct 2015 20:06:53 +0000 (13:06 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 30 Oct 2015 20:06:53 +0000 (13:06 -0700)
"git clone --dissociate" used to require that "--reference" was
used at the same time, but you can create a new repository that
borrows objects from another without using "--reference", namely
with "clone --local" from a repository that borrows objects from
other repositories.

* ar/clone-dissociate:
clone: allow "--dissociate" without reference

95 files changed:
Documentation/RelNotes/1.7.7.txt
Documentation/RelNotes/1.8.3.1.txt
Documentation/RelNotes/1.8.4.1.txt
Documentation/RelNotes/1.8.4.2.txt
Documentation/RelNotes/1.8.4.3.txt
Documentation/RelNotes/1.8.4.4.txt
Documentation/RelNotes/1.9.0.txt
Documentation/RelNotes/2.7.0.txt
Documentation/git-bisect.txt
Documentation/git-cat-file.txt
Documentation/git-check-attr.txt
Documentation/git-check-ignore.txt
Documentation/git-commit-tree.txt
Documentation/git-fetch.txt
Documentation/git-fmt-merge-msg.txt
Documentation/git-get-tar-commit-id.txt
Documentation/git-hash-object.txt
Documentation/git-mktag.txt
Documentation/git-patch-id.txt
Documentation/git-push.txt
Documentation/git-remote.txt
Documentation/git-show-index.txt
Documentation/git-show-ref.txt
Documentation/git-stripspace.txt
Documentation/git-unpack-objects.txt
Documentation/git-upload-archive.txt
Documentation/git-worktree.txt
Documentation/giteveryday.txt
Documentation/gitrevisions.txt
Documentation/technical/index-format.txt
Documentation/technical/repository-version.txt [new file with mode: 0644]
Documentation/urls-remotes.txt
Documentation/user-manual.txt
Makefile
branch.c
branch.h
builtin/am.c
builtin/branch.c
builtin/cat-file.c
builtin/check-attr.c
builtin/check-ignore.c
builtin/commit-tree.c
builtin/commit.c
builtin/gc.c
builtin/get-tar-commit-id.c
builtin/hash-object.c
builtin/mailinfo.c
builtin/merge.c
builtin/mktag.c
builtin/notes.c
builtin/patch-id.c
builtin/prune.c
builtin/repack.c
builtin/show-ref.c
builtin/stripspace.c
builtin/tag.c
builtin/unpack-objects.c
builtin/worktree.c
cache.h
compat/mingw.c
configure.ac
credential-cache--daemon.c
credential-cache.c
diff.c
dir.c
environment.c
git-p4.py
gitk-git/gitk
gitk-git/po/bg.po
gitk-git/po/ca.po
gitk-git/po/de.po
gitk-git/po/es.po
gitk-git/po/fr.po
gitk-git/po/hu.po
gitk-git/po/it.po
gitk-git/po/ja.po
gitk-git/po/pt_br.po
gitk-git/po/ru.po
gitk-git/po/sv.po
gitk-git/po/vi.po
mailinfo.c [new file with mode: 0644]
mailinfo.h [new file with mode: 0644]
name-hash.c
read-cache.c
remote.c
setup.c
shallow.c
show-index.c
strbuf.c
strbuf.h
t/t1302-repo-version.sh
t/t2027-worktree-list.sh [new file with mode: 0755]
t/t7063-status-untracked-cache.sh
worktree.c [new file with mode: 0644]
worktree.h [new file with mode: 0644]
index 7655cccfaa1585a14257b5a86d663f501c06bf1d..6eff128c80b706822f62bfdd19f7b127df47d286 100644 (file)
@@ -84,7 +84,7 @@ Updates since v1.7.6
    logic used by "git diff" to determine the hunk header.
 
  * Invoking the low-level "git http-fetch" without "-a" option (which
-   git itself never did---normal users should not have to worry about
+   git itself never did--normal users should not have to worry about
    this) is now deprecated.
 
  * The "--decorate" option to "git log" and its family learned to
index fc3ea185a5e6f310994f41de48cad9ab21ecf348..986637b7551f14e530fc0eefb677851324fd5a98 100644 (file)
@@ -1,5 +1,5 @@
 Git v1.8.3.1 Release Notes
-========================
+==========================
 
 Fixes since v1.8.3
 ------------------
index 3aa25a274379dca115034bfd7201e97f72dcc651..96090ef599d34e1bdfe19e56fd85dda41aced65c 100644 (file)
@@ -1,5 +1,5 @@
 Git v1.8.4.1 Release Notes
-========================
+==========================
 
 Fixes since v1.8.4
 ------------------
index 9adccb1efb8d3fe8a117fdd13b40abb1cb757fb7..bf6fb1a02369845b487e00f4e6a02f4cfc8ae57f 100644 (file)
@@ -1,5 +1,5 @@
 Git v1.8.4.2 Release Notes
-========================
+==========================
 
 Fixes since v1.8.4.1
 --------------------
index 03f3d177518c706b4c020ce642e0aeb6b4bfbeeb..267a1b34b4d899ef7cb3ad5c8fcd607b85ee2f82 100644 (file)
@@ -1,5 +1,5 @@
 Git v1.8.4.3 Release Notes
-========================
+==========================
 
 Fixes since v1.8.4.2
 --------------------
index 7bc4c5dcc01b14fd7eb09be85269573f74759e01..a7c1ce15c061726814d22d5ec7ea8eae11398d0b 100644 (file)
@@ -1,5 +1,5 @@
 Git v1.8.4.4 Release Notes
-========================
+==========================
 
 Fixes since v1.8.4.3
 --------------------
index 752d79127a3d7a4aa25df28f1c659b46ee527f35..4e4b88aa5c89440c94df7322398a4b83b9961b01 100644 (file)
@@ -177,7 +177,7 @@ Performance, Internal Implementation, etc.
  * The naming convention of the packfiles has been updated; it used to
    be based on the enumeration of names of the objects that are
    contained in the pack, but now it also depends on how the packed
-   result is represented---packing the same set of objects using
+   result is represented--packing the same set of objects using
    different settings (or delta order) would produce a pack with
    different name.
 
index 85c35202d1af11e091cbc6bb553bad9ba9872283..516d9f33ca769925a447541fdc9d1d5b7be99681 100644 (file)
@@ -48,6 +48,13 @@ UI, Workflows & Features
  * Teach "git p4" to send large blobs outside the repository by
    talking to Git LFS.
 
+ * Prepare for Git on-disk repository representation to undergo
+   backward incompatible changes by introducing a new repository
+   format version "1", with an extension mechanism.
+   (merge 067fbd4 jk/repository-extension later to maint).
+
+ * "git worktree" learned a "list" subcommand.
+
 
 Performance, Internal Implementation, Development Support etc.
 
@@ -58,7 +65,9 @@ Performance, Internal Implementation, Development Support etc.
 
  * 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.
+   implementation can be shared across all three.  The version merged
+   to the 'master' branch earlier had a performance regression in "tag
+   --contains", which has since been corrected.
 
  * Because "test_when_finished" in our test framework queues the
    clean-up tasks to be done in a shell variable, it should not be
@@ -83,6 +92,18 @@ Performance, Internal Implementation, Development Support etc.
    followed by strcpy/sprintf have been replaced with a less error
    prone constructs such as xstrfmt.
 
+ * The internal stripspace() function has been moved to where it
+   logically belongs to, i.e. strbuf API, and the command line parser
+   of "git stripspace" has been updated to use the parse_options API.
+   (merge bed4452 tk/stripspace later to maint).
+
+ * "git am" used to spawn "git mailinfo" via run_command() API once
+   per each patch, but learned to make a direct call to mailinfo()
+   instead.
+
+ * The implementation of "git mailinfo" was refactored so that a
+   mailinfo() function can be directly called from inside a process.
+
 
 Also contains various documentation updates and code clean-ups.
 
@@ -224,7 +245,40 @@ notes for details).
    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.
+ * A test for interaction between untracked cache and sparse checkout
+   added in Git 2.5 days were flaky.
+   (merge 9b680fb dt/t7063-fix-flaky-test later to maint).
+
+ * A couple of commands still showed "[options]" in their usage string
+   to note where options should come on their command line, but we
+   spell that "[<options>]" in most places these days.
+   (merge d96a031 rt/placeholder-in-usage later to maint).
+
+ * The synopsis text and the usage string of subcommands that read
+   list of things from the standard input are often shown as if they
+   only take input from a file on a filesystem, which was misleading.
+   (merge 33e8fc8 jc/usage-stdin later to maint).
+
+ * "git am -3" had a small regression where it is aborted in its error
+   handling codepath when underlying merge-recursive failed in certain
+   ways, as it assumed that the internal call to merge-recursive will
+   never die, which is not the case (yet).
+   (merge c63d4b2 jc/am-3-fallback-regression-fix later to maint).
+
+ * The linkage order of libraries was wrong in places around libcurl.
+   (merge 7e91e8d rp/link-curl-before-ssl later to maint).
+
+ * The name-hash subsystem that is used to cope with case insensitive
+   filesystems keeps track of directories and their on-filesystem
+   cases for all the paths in the index by holding a pointer to a
+   randomly chosen cache entry that is inside the directory (for its
+   ce->ce_name component).  This pointer was not updated even when the
+   cache entry was removed from the index, leading to use after free.
+   This was fixed by recording the path for each directory instead of
+   borrowing cache entries and restructuring the API somewhat.
+   (merge 41284eb dt/name-hash-dir-entry-fix later to maint).
+
+ * Code clean-up, minor fixes etc.
    (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).
@@ -234,3 +288,7 @@ notes for details).
    (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).
+   (merge ae9f274 es/worktree-add later to maint).
+   (merge 3b19dba jc/em-dash-in-doc later to maint).
+   (merge f3f38c7 jc/everyday-markup later to maint).
+   (merge 77d5f71 xf/user-manual-markup later to maint).
index 2044fe6820e0507a6dcc1823f8fe3fce407e4d90..7e79aaedeb58632bca9edb6c1f7715c493139bb6 100644 (file)
@@ -256,7 +256,7 @@ Then compile and test the chosen revision, and afterwards mark
 the revision as good or bad in the usual manner.
 
 Bisect skip
-~~~~~~~~~~~~
+~~~~~~~~~~~
 
 Instead of choosing a nearby commit by yourself, you can ask Git to do
 it for you by issuing the command:
@@ -335,7 +335,7 @@ cannot be tested. If the script exits with this code, the current
 revision will be skipped (see `git bisect skip` above). 125 was chosen
 as the highest sensible value to use for this purpose, because 126 and 127
 are used by POSIX shells to signal specific error status (127 is for
-command not found, 126 is for command found but not executable---these
+command not found, 126 is for command found but not executable--these
 details do not matter, as they are normal errors in the script, as far as
 `bisect run` is concerned).
 
index 3105fc07205ac19805a753603940f9add612d4ae..eb3d6945a9e508c9a1d0af63e838a627311631bc 100644 (file)
@@ -10,7 +10,7 @@ SYNOPSIS
 --------
 [verse]
 'git cat-file' (-t [--allow-unknown-type]| -s [--allow-unknown-type]| -e | -p | <type> | --textconv ) <object>
-'git cat-file' (--batch | --batch-check) [--follow-symlinks] < <list-of-objects>
+'git cat-file' (--batch | --batch-check) [--follow-symlinks]
 
 DESCRIPTION
 -----------
index 00e2aa2df259d449602f59cbec33599346ebd8a0..aa3b2bf2fcf764ca22f44bcfc081afd4e09328bb 100644 (file)
@@ -10,7 +10,7 @@ SYNOPSIS
 --------
 [verse]
 'git check-attr' [-a | --all | attr...] [--] pathname...
-'git check-attr' --stdin [-z] [-a | --all | attr...] < <list-of-paths>
+'git check-attr' --stdin [-z] [-a | --all | attr...]
 
 DESCRIPTION
 -----------
@@ -28,7 +28,8 @@ OPTIONS
        Consider `.gitattributes` in the index only, ignoring the working tree.
 
 --stdin::
-       Read file names from stdin instead of from the command-line.
+       Read pathnames from the standard input, one per line,
+       instead of from the command-line.
 
 -z::
        The output format is modified to be machine-parseable.
index e35cd0489b6961a5322ea49eeb769bbfb883070e..59531abba481c4788357fc07342fcc7672727fbf 100644 (file)
@@ -10,7 +10,7 @@ SYNOPSIS
 --------
 [verse]
 'git check-ignore' [options] pathname...
-'git check-ignore' [options] --stdin < <list-of-paths>
+'git check-ignore' [options] --stdin
 
 DESCRIPTION
 -----------
@@ -35,7 +35,8 @@ OPTIONS
        for each given pathname.
 
 --stdin::
-       Read file names from stdin instead of from the command-line.
+       Read pathnames from the standard input, one per line,
+       instead of from the command-line.
 
 -z::
        The output format is modified to be machine-parseable (see
index a0b5457304008cec1cf728e5ac168c5f547173eb..48c33d7ed7323c7fc4776ffeb98aad9b41b23744 100644 (file)
@@ -9,7 +9,7 @@ git-commit-tree - Create a new commit object
 SYNOPSIS
 --------
 [verse]
-'git commit-tree' <tree> [(-p <parent>)...] < changelog
+'git commit-tree' <tree> [(-p <parent>)...]
 'git commit-tree' [(-p <parent>)...] [-S[<keyid>]] [(-m <message>)...]
                  [(-F <file>)...] <tree>
 
index e62d9a0717f0d383535159c7d522bfb303855160..efe56e08085c70341149c546ab0848674a5bc33f 100644 (file)
@@ -71,7 +71,7 @@ This configuration is used in two ways:
 * When `git fetch` is run without specifying what branches
   and/or tags to fetch on the command line, e.g. `git fetch origin`
   or `git fetch`, `remote.<repository>.fetch` values are used as
-  the refspecs---they specify which refs to fetch and which local refs
+  the refspecs--they specify which refs to fetch and which local refs
   to update.  The example above will fetch
   all branches that exist in the `origin` (i.e. any ref that matches
   the left-hand side of the value, `refs/heads/*`) and update the
index 55a9a4b93a2cba2d1d760b3529f1604b61359004..6526b178e87f9cbf1c2032bd63023295cf1d190e 100644 (file)
@@ -9,7 +9,7 @@ git-fmt-merge-msg - Produce a merge commit message
 SYNOPSIS
 --------
 [verse]
-'git fmt-merge-msg' [-m <message>] [--log[=<n>] | --no-log] <$GIT_DIR/FETCH_HEAD
+'git fmt-merge-msg' [-m <message>] [--log[=<n>] | --no-log]
 'git fmt-merge-msg' [-m <message>] [--log[=<n>] | --no-log] -F <file>
 
 DESCRIPTION
@@ -57,6 +57,18 @@ merge.summary::
        Synonym to `merge.log`; this is deprecated and will be removed in
        the future.
 
+EXAMPLE
+-------
+
+--
+$ git fetch origin master
+$ git fmt-merge-msg --log <$GIT_DIR/FETCH_HEAD
+--
+
+Print a log message describing a merge of the "master" branch from
+the "origin" remote.
+
+
 SEE ALSO
 --------
 linkgit:git-merge[1]
index 1e2a20dd265c2c41aef0ea66ca9831321d951d5c..ac44d85b0b5c7f12a2d5473b3be0b0e4511cf58f 100644 (file)
@@ -9,17 +9,19 @@ git-get-tar-commit-id - Extract commit ID from an archive created using git-arch
 SYNOPSIS
 --------
 [verse]
-'git get-tar-commit-id' < <tarfile>
+'git get-tar-commit-id'
 
 
 DESCRIPTION
 -----------
-Acts as a filter, extracting the commit ID stored in archives created by
-'git archive'.  It reads only the first 1024 bytes of input, thus its
-runtime is not influenced by the size of <tarfile> very much.
+
+Read a tar archive created by 'git archive' from the standard input
+and extract the commit ID stored in it.  It reads only the first
+1024 bytes of input, thus its runtime is not influenced by the size
+of the tar archive very much.
 
 If no commit ID is found, 'git get-tar-commit-id' quietly exists with a
-return code of 1.  This can happen if <tarfile> had not been created
+return code of 1.  This can happen if the archive had not been created
 using 'git archive' or if the first parameter of 'git archive' had been
 a tree ID instead of a commit ID or tag.
 
index 0c75f3b610678d9a6811ac73d208cabd706b2276..814e74406ae4fb6ac68213df1f7e8e0192d1dbaf 100644 (file)
@@ -10,7 +10,7 @@ SYNOPSIS
 --------
 [verse]
 'git hash-object' [-t <type>] [-w] [--path=<file>|--no-filters] [--stdin [--literally]] [--] <file>...
-'git hash-object' [-t <type>] [-w] --stdin-paths [--no-filters] < <list-of-paths>
+'git hash-object' [-t <type>] [-w] --stdin-paths [--no-filters]
 
 DESCRIPTION
 -----------
@@ -35,7 +35,8 @@ OPTIONS
        Read the object from standard input instead of from a file.
 
 --stdin-paths::
-       Read file names from stdin instead of from the command-line.
+       Read file names from the standard input, one per line, instead
+       of from the command-line.
 
 --path::
        Hash object as it were located at the given path. The location of
index 3ca158b05e2792de404c9f0622bf4eb41af6b5e9..fa6a7561236f70808994cf63c37b400b6e337318 100644 (file)
@@ -9,7 +9,7 @@ git-mktag - Creates a tag object
 SYNOPSIS
 --------
 [verse]
-'git mktag' < signature_file
+'git mktag'
 
 DESCRIPTION
 -----------
@@ -20,7 +20,8 @@ The output is the new tag's <object> identifier.
 
 Tag Format
 ----------
-A tag signature file has a very simple fixed format: four lines of
+A tag signature file, to be fed to this command's standard input,
+has a very simple fixed format: four lines of
 
   object <sha1>
   type <typename>
index 31efc587ee694da6885ed1ce9d1a4c1419808397..cf71fba1c0a9c18ea6428ab49820cb9e707608c6 100644 (file)
@@ -8,10 +8,12 @@ git-patch-id - Compute unique ID for a patch
 SYNOPSIS
 --------
 [verse]
-'git patch-id' [--stable | --unstable] < <patch>
+'git patch-id' [--stable | --unstable]
 
 DESCRIPTION
 -----------
+Read a patch from the standard input and compute the patch ID for it.
+
 A "patch ID" is nothing but a sum of SHA-1 of the file diffs associated with a
 patch, with whitespace and line numbers ignored.  As such, it's "reasonably
 stable", but at the same time also reasonably unique, i.e., two patches that
index 1495e3416c66331a9d70e4e298e346e099490ae4..85a4d7d6d5bf1958ea93813787b6a3f20ad6e023 100644 (file)
@@ -62,7 +62,7 @@ be named.
 If `git push [<repository>]` without any `<refspec>` argument is set to
 update some ref at the destination with `<src>` with
 `remote.<repository>.push` configuration variable, `:<dst>` part can
-be omitted---such a push will update a ref that `<src>` normally updates
+be omitted--such a push will update a ref that `<src>` normally updates
 without any `<refspec>` on the command line.  Otherwise, missing
 `:<dst>` means to update the same ref as the `<src>`.
 +
index 3c9bf45829e084a2309117b44520ad4275e9d4d7..1d7eceaa9355bd4651bda867c998fe66ae069d1a 100644 (file)
@@ -1,5 +1,5 @@
 git-remote(1)
-============
+=============
 
 NAME
 ----
index fbdc8adae5b10a8ed88883647d2f0ccc3636cddc..a8a9509e0eb0bb416ce0a32f76c964a505da8f61 100644 (file)
@@ -9,13 +9,14 @@ git-show-index - Show packed archive index
 SYNOPSIS
 --------
 [verse]
-'git show-index' < idx-file
+'git show-index'
 
 
 DESCRIPTION
 -----------
-Reads given idx file for packed Git archive created with
-'git pack-objects' command, and dumps its contents.
+Read the idx file for a Git packfile created with
+'git pack-objects' command from the standard input, and
+dump its contents.
 
 The information it outputs is subset of what you can get from
 'git verify-pack -v'; this command only shows the packfile
index 2a6f89b235f10ea6d41047a2a42a133531ea3cab..3a324519842bd709a8ad440905fb9a9e7eaa889d 100644 (file)
@@ -11,7 +11,7 @@ SYNOPSIS
 'git show-ref' [-q|--quiet] [--verify] [--head] [-d|--dereference]
             [-s|--hash[=<n>]] [--abbrev[=<n>]] [--tags]
             [--heads] [--] [<pattern>...]
-'git show-ref' --exclude-existing[=<pattern>] < ref-list
+'git show-ref' --exclude-existing[=<pattern>]
 
 DESCRIPTION
 -----------
@@ -23,8 +23,9 @@ particular ref exists.
 
 By default, shows the tags, heads, and remote refs.
 
-The --exclude-existing form is a filter that does the inverse, it shows the
-refs from stdin that don't exist in the local repository.
+The --exclude-existing form is a filter that does the inverse. It reads
+refs from stdin, one ref per line, and shows those that don't exist in
+the local repository.
 
 Use of this utility is encouraged in favor of directly accessing files under
 the `.git` directory.
index 60328d5d08d43d31dd80e14b07fe75257a1f4446..2438f76da05ebf013103a08962d634c4baa9ea15 100644 (file)
@@ -9,14 +9,15 @@ git-stripspace - Remove unnecessary whitespace
 SYNOPSIS
 --------
 [verse]
-'git stripspace' [-s | --strip-comments] < input
-'git stripspace' [-c | --comment-lines] < input
+'git stripspace' [-s | --strip-comments]
+'git stripspace' [-c | --comment-lines]
 
 DESCRIPTION
 -----------
 
-Clean the input in the manner used by Git for text such as commit
-messages, notes, tags and branch descriptions.
+Read text, such as commit messages, notes, tags and branch
+descriptions, from the standard input and clean it in the manner
+used by Git.
 
 With no arguments, this will:
 
index 07d432988f249d7439ae56699496da97b7d6f828..3e887d16109c48f4f906d8a7c5c0c860bbd6123d 100644 (file)
@@ -9,7 +9,7 @@ git-unpack-objects - Unpack objects from a packed archive
 SYNOPSIS
 --------
 [verse]
-'git unpack-objects' [-n] [-q] [-r] [--strict] < <packfile>
+'git unpack-objects' [-n] [-q] [-r] [--strict]
 
 
 DESCRIPTION
index cbef61ba88788b6b3dc89e5031b3addfcf0a2e7b..fba0f1c1b27c629b31c86f78fbe22fdfd3ce360d 100644 (file)
@@ -1,5 +1,5 @@
 git-upload-archive(1)
-====================
+=====================
 
 NAME
 ----
index fb68156cf8ad0695eb6e5a7548c9a2e56be5c3f2..5b9ad0429c84d2f11b0569f6d979858c833b3768 100644 (file)
@@ -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)
 
index 7be6e64846e93bea776fd3f5892df4edc7846d8c..35473ad02fb1dcce2e03bca30bdace4dea0232d4 100644 (file)
@@ -1,5 +1,5 @@
 giteveryday(7)
-===============
+==============
 
 NAME
 ----
index c0ed6d19251c151ce268ab11166f1993d21d5f80..e903eb786049b4742e9d7c766b8448a503cbb6a3 100644 (file)
@@ -1,5 +1,5 @@
 gitrevisions(7)
-================
+===============
 
 NAME
 ----
index 7392ff636c6dda148b41993448f4a5589f66938c..ade0b0c4454651812e88b292a846f23a58925f5f 100644 (file)
@@ -170,7 +170,7 @@ Git index format
 
   The entries are written out in the top-down, depth-first order.  The
   first entry represents the root level of the repository, followed by the
-  first subtree---let's call this A---of the root level (with its name
+  first subtree--let's call this A--of the root level (with its name
   relative to the root level), followed by the first subtree of A (with
   its name relative to A), ...
 
diff --git a/Documentation/technical/repository-version.txt b/Documentation/technical/repository-version.txt
new file mode 100644 (file)
index 0000000..00ad379
--- /dev/null
@@ -0,0 +1,88 @@
+Git Repository Format Versions
+==============================
+
+Every git repository is marked with a numeric version in the
+`core.repositoryformatversion` key of its `config` file. This version
+specifies the rules for operating on the on-disk repository data. An
+implementation of git which does not understand a particular version
+advertised by an on-disk repository MUST NOT operate on that repository;
+doing so risks not only producing wrong results, but actually losing
+data.
+
+Because of this rule, version bumps should be kept to an absolute
+minimum. Instead, we generally prefer these strategies:
+
+  - bumping format version numbers of individual data files (e.g.,
+    index, packfiles, etc). This restricts the incompatibilities only to
+    those files.
+
+  - introducing new data that gracefully degrades when used by older
+    clients (e.g., pack bitmap files are ignored by older clients, which
+    simply do not take advantage of the optimization they provide).
+
+A whole-repository format version bump should only be part of a change
+that cannot be independently versioned. For instance, if one were to
+change the reachability rules for objects, or the rules for locking
+refs, that would require a bump of the repository format version.
+
+Note that this applies only to accessing the repository's disk contents
+directly. An older client which understands only format `0` may still
+connect via `git://` to a repository using format `1`, as long as the
+server process understands format `1`.
+
+The preferred strategy for rolling out a version bump (whether whole
+repository or for a single file) is to teach git to read the new format,
+and allow writing the new format with a config switch or command line
+option (for experimentation or for those who do not care about backwards
+compatibility with older gits). Then after a long period to allow the
+reading capability to become common, we may switch to writing the new
+format by default.
+
+The currently defined format versions are:
+
+Version `0`
+-----------
+
+This is the format defined by the initial version of git, including but
+not limited to the format of the repository directory, the repository
+configuration file, and the object and ref storage. Specifying the
+complete behavior of git is beyond the scope of this document.
+
+Version `1`
+-----------
+
+This format is identical to version `0`, with the following exceptions:
+
+  1. When reading the `core.repositoryformatversion` variable, a git
+     implementation which supports version 1 MUST also read any
+     configuration keys found in the `extensions` section of the
+     configuration file.
+
+  2. If a version-1 repository specifies any `extensions.*` keys that
+     the running git has not implemented, the operation MUST NOT
+     proceed. Similarly, if the value of any known key is not understood
+     by the implementation, the operation MUST NOT proceed.
+
+Note that if no extensions are specified in the config file, then
+`core.repositoryformatversion` SHOULD be set to `0` (setting it to `1`
+provides no benefit, and makes the repository incompatible with older
+implementations of git).
+
+This document will serve as the master list for extensions. Any
+implementation wishing to define a new extension should make a note of
+it here, in order to claim the name.
+
+The defined extensions are:
+
+`noop`
+~~~~~~
+
+This extension does not change git's behavior at all. It is useful only
+for testing format-1 compatibility.
+
+`preciousObjects`
+~~~~~~~~~~~~~~~~~
+
+When the config key `extensions.preciousObjects` is set to `true`,
+objects in the repository MUST NOT be deleted (e.g., by `git-prune` or
+`git repack -d`).
index 282758e76887e34c1a162e8c00d3a1007d9e199c..bd184cd6539af2daef0226e7bf462b91ff5f3b4d 100644 (file)
@@ -36,7 +36,7 @@ The `<pushurl>` is used for pushes only. It is optional and defaults
 to `<url>`.
 
 Named file in `$GIT_DIR/remotes`
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 You can choose to provide the name of a
 file in `$GIT_DIR/remotes`.  The URL
index 1b7987e73767f1c71d777909f3b275b5976419aa..764a270c83ba6202ff7a3b58e6e067684ea3301f 100644 (file)
@@ -1491,7 +1491,7 @@ resolving a merge>>.
 
 [[fixing-a-mistake-by-rewriting-history]]
 Fixing a mistake by rewriting history
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 If the problematic commit is the most recent commit, and you have not
 yet made that commit public, then you may just
index 0d9f5dddbc68e2f7fc2c5c5a2f27f2c30466a32e..43ceeb966c51e4e0b684ac64c2c5cbf0aa943cbf 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -39,6 +39,9 @@ all::
 # Define CURLDIR=/foo/bar if your curl header and library files are in
 # /foo/bar/include and /foo/bar/lib directories.
 #
+# Define CURL_CONFIG to curl's configuration program that prints information
+# about the library (e.g., its version number).  The default is 'curl-config'.
+#
 # Define NO_EXPAT if you do not have expat installed.  git-http-push is
 # not built, and you cannot push using http:// and https:// transports (dumb).
 #
@@ -426,6 +429,7 @@ TCL_PATH = tclsh
 TCLTK_PATH = wish
 XGETTEXT = xgettext
 MSGFMT = msgfmt
+CURL_CONFIG = curl-config
 PTHREAD_LIBS = -lpthread
 PTHREAD_CFLAGS =
 GCOV = gcov
@@ -727,6 +731,7 @@ LIB_OBJS += list-objects.o
 LIB_OBJS += ll-merge.o
 LIB_OBJS += lockfile.o
 LIB_OBJS += log-tree.o
+LIB_OBJS += mailinfo.o
 LIB_OBJS += mailmap.o
 LIB_OBJS += match-trees.o
 LIB_OBJS += merge.o
@@ -808,6 +813,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
@@ -1035,7 +1041,7 @@ ifdef HAVE_ALLOCA_H
 endif
 
 IMAP_SEND_BUILDDEPS =
-IMAP_SEND_LDFLAGS = $(OPENSSL_LINK) $(OPENSSL_LIBSSL) $(LIB_4_CRYPTO)
+IMAP_SEND_LDFLAGS =
 
 ifdef NO_CURL
        BASIC_CFLAGS += -DNO_CURL
@@ -1065,13 +1071,13 @@ else
        REMOTE_CURL_NAMES = $(REMOTE_CURL_PRIMARY) $(REMOTE_CURL_ALIASES)
        PROGRAM_OBJS += http-fetch.o
        PROGRAMS += $(REMOTE_CURL_NAMES)
-       curl_check := $(shell (echo 070908; curl-config --vernum | sed -e '/^70[BC]/s/^/0/') 2>/dev/null | sort -r | sed -ne 2p)
+       curl_check := $(shell (echo 070908; $(CURL_CONFIG) --vernum | sed -e '/^70[BC]/s/^/0/') 2>/dev/null | sort -r | sed -ne 2p)
        ifeq "$(curl_check)" "070908"
                ifndef NO_EXPAT
                        PROGRAM_OBJS += http-push.o
                endif
        endif
-       curl_check := $(shell (echo 072200; curl-config --vernum | sed -e '/^70[BC]/s/^/0/') 2>/dev/null | sort -r | sed -ne 2p)
+       curl_check := $(shell (echo 072200; $(CURL_CONFIG) --vernum | sed -e '/^70[BC]/s/^/0/') 2>/dev/null | sort -r | sed -ne 2p)
        ifeq "$(curl_check)" "072200"
                USE_CURL_FOR_IMAP_SEND = YesPlease
        endif
@@ -1092,6 +1098,7 @@ else
                endif
        endif
 endif
+IMAP_SEND_LDFLAGS += $(OPENSSL_LINK) $(OPENSSL_LIBSSL) $(LIB_4_CRYPTO)
 
 ifdef ZLIB_PATH
        BASIC_CFLAGS += -I$(ZLIB_PATH)/include
@@ -1974,10 +1981,10 @@ git-imap-send$X: imap-send.o $(IMAP_SEND_BUILDDEPS) GIT-LDFLAGS $(GITLIBS)
 
 git-http-fetch$X: http.o http-walker.o http-fetch.o GIT-LDFLAGS $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
-               $(LIBS) $(CURL_LIBCURL)
+               $(CURL_LIBCURL) $(LIBS)
 git-http-push$X: http.o http-push.o GIT-LDFLAGS $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
-               $(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
+               $(CURL_LIBCURL) $(EXPAT_LIBEXPAT) $(LIBS)
 
 git-remote-testsvn$X: remote-testsvn.o GIT-LDFLAGS $(GITLIBS) $(VCSSVN_LIB)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS) \
@@ -1991,7 +1998,7 @@ $(REMOTE_CURL_ALIASES): $(REMOTE_CURL_PRIMARY)
 
 $(REMOTE_CURL_PRIMARY): remote-curl.o http.o http-walker.o GIT-LDFLAGS $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
-               $(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
+               $(CURL_LIBCURL) $(EXPAT_LIBEXPAT) $(LIBS)
 
 $(LIB_FILE): $(LIB_OBJS)
        $(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^
index d013374e5a0b5714836be77709b07a5e3f67ed2a..77d7f2a63ee38b81ffbbf4741cdf1ad205e0c054 100644 (file)
--- 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;
index d3446ed73c0c209f0d6b3dc2a7412a643031ea08..58aa45fe72ca356a8bd3648782b36e63e207ee36 100644 (file)
--- 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
index 3bd4fd701bdccab91f1343b371cf19e822c14db5..f1a25ab6ad881fc903a533595792b1a46b40ea91 100644 (file)
@@ -27,6 +27,7 @@
 #include "notes-utils.h"
 #include "rerere.h"
 #include "prompt.h"
+#include "mailinfo.h"
 
 /**
  * Returns 1 if the file is empty or does not exist, 0 otherwise.
@@ -1258,58 +1259,61 @@ static void am_append_signoff(struct am_state *state)
 static int parse_mail(struct am_state *state, const char *mail)
 {
        FILE *fp;
-       struct child_process cp = CHILD_PROCESS_INIT;
        struct strbuf sb = STRBUF_INIT;
        struct strbuf msg = STRBUF_INIT;
        struct strbuf author_name = STRBUF_INIT;
        struct strbuf author_date = STRBUF_INIT;
        struct strbuf author_email = STRBUF_INIT;
        int ret = 0;
+       struct mailinfo mi;
 
-       cp.git_cmd = 1;
-       cp.in = xopen(mail, O_RDONLY, 0);
-       cp.out = xopen(am_path(state, "info"), O_WRONLY | O_CREAT, 0777);
+       setup_mailinfo(&mi);
 
-       argv_array_push(&cp.args, "mailinfo");
-       argv_array_push(&cp.args, state->utf8 ? "-u" : "-n");
+       if (state->utf8)
+               mi.metainfo_charset = get_commit_output_encoding();
+       else
+               mi.metainfo_charset = NULL;
 
        switch (state->keep) {
        case KEEP_FALSE:
                break;
        case KEEP_TRUE:
-               argv_array_push(&cp.args, "-k");
+               mi.keep_subject = 1;
                break;
        case KEEP_NON_PATCH:
-               argv_array_push(&cp.args, "-b");
+               mi.keep_non_patch_brackets_in_subject = 1;
                break;
        default:
                die("BUG: invalid value for state->keep");
        }
 
        if (state->message_id)
-               argv_array_push(&cp.args, "-m");
+               mi.add_message_id = 1;
 
        switch (state->scissors) {
        case SCISSORS_UNSET:
                break;
        case SCISSORS_FALSE:
-               argv_array_push(&cp.args, "--no-scissors");
+               mi.use_scissors = 0;
                break;
        case SCISSORS_TRUE:
-               argv_array_push(&cp.args, "--scissors");
+               mi.use_scissors = 1;
                break;
        default:
                die("BUG: invalid value for state->scissors");
        }
 
-       argv_array_push(&cp.args, am_path(state, "msg"));
-       argv_array_push(&cp.args, am_path(state, "patch"));
-
-       if (run_command(&cp) < 0)
+       mi.input = fopen(mail, "r");
+       if (!mi.input)
+               die("could not open input");
+       mi.output = fopen(am_path(state, "info"), "w");
+       if (!mi.output)
+               die("could not open output 'info'");
+       if (mailinfo(&mi, am_path(state, "msg"), am_path(state, "patch")))
                die("could not parse patch");
 
-       close(cp.in);
-       close(cp.out);
+       fclose(mi.input);
+       fclose(mi.output);
 
        /* Extract message and author information */
        fp = xfopen(am_path(state, "info"), "r");
@@ -1341,9 +1345,8 @@ static int parse_mail(struct am_state *state, const char *mail)
        }
 
        strbuf_addstr(&msg, "\n\n");
-       if (strbuf_read_file(&msg, am_path(state, "msg"), 0) < 0)
-               die_errno(_("could not read '%s'"), am_path(state, "msg"));
-       stripspace(&msg, 0);
+       strbuf_addbuf(&msg, &mi.log_message);
+       strbuf_stripspace(&msg, 0);
 
        if (state->signoff)
                am_signoff(&msg);
@@ -1366,6 +1369,7 @@ static int parse_mail(struct am_state *state, const char *mail)
        strbuf_release(&author_email);
        strbuf_release(&author_name);
        strbuf_release(&sb);
+       clear_mailinfo(&mi);
        return ret;
 }
 
@@ -1589,6 +1593,38 @@ static int build_fake_ancestor(const struct am_state *state, const char *index_f
        return 0;
 }
 
+/**
+ * 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.
  */
@@ -1596,10 +1632,6 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa
 {
        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 +1683,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;
 }
 
@@ -2229,8 +2250,8 @@ int cmd_am(int argc, const char **argv, const char *prefix)
        int in_progress;
 
        const char * const usage[] = {
-               N_("git am [options] [(<mbox>|<Maildir>)...]"),
-               N_("git am [options] (--continue | --skip | --abort)"),
+               N_("git am [<options>] [(<mbox>|<Maildir>)...]"),
+               N_("git am [<options>] (--continue | --skip | --abort)"),
                NULL
        };
 
index 01f9530822d9f4c060e2bcfa6d80b03618f526d9..b99a436ef36ecfcc70636741642fdcf07b5a5810 100644 (file)
@@ -592,7 +592,7 @@ static int edit_branch_description(const char *branch_name)
                strbuf_release(&buf);
                return -1;
        }
-       stripspace(&buf, 1);
+       strbuf_stripspace(&buf, 1);
 
        strbuf_addf(&name, "branch.%s.description", branch_name);
        status = git_config_set(name.buf, buf.len ? buf.buf : NULL);
index 07baad1e59c1977e5fce04d4c7288f8263947f4e..c0fd8dbb1c1bed55f541569e0c13cc865c39ec4f 100644 (file)
@@ -426,7 +426,7 @@ static int batch_objects(struct batch_options *opt)
 
 static const char * const cat_file_usage[] = {
        N_("git cat-file (-t [--allow-unknown-type]|-s [--allow-unknown-type]|-e|-p|<type>|--textconv) <object>"),
-       N_("git cat-file (--batch | --batch-check) [--follow-symlinks] < <list-of-objects>"),
+       N_("git cat-file (--batch | --batch-check) [--follow-symlinks]"),
        NULL
 };
 
index 21d2bedcc930ab216f2a55ec90ba188b2b66dfce..265c9ba02264d29eb3f63f1a8264c9fe7e642332 100644 (file)
@@ -9,7 +9,7 @@ static int cached_attrs;
 static int stdin_paths;
 static const char * const check_attr_usage[] = {
 N_("git check-attr [-a | --all | <attr>...] [--] <pathname>..."),
-N_("git check-attr --stdin [-z] [-a | --all | <attr>...] < <list-of-paths>"),
+N_("git check-attr --stdin [-z] [-a | --all | <attr>...]"),
 NULL
 };
 
index dc8d97c56c60991ec5c1b5cc9eb6e20a76e5e405..43f361797ac73b64c79bb163cbb38baf78981fc9 100644 (file)
@@ -8,7 +8,7 @@
 static int quiet, verbose, stdin_paths, show_non_matching, no_index;
 static const char * const check_ignore_usage[] = {
 "git check-ignore [<options>] <pathname>...",
-"git check-ignore [<options>] --stdin < <list-of-paths>",
+"git check-ignore [<options>] --stdin",
 NULL
 };
 
index 25aa2cdef3557c67a9482492a4d0294bc7b89b04..8747c0f2fbe6426b97571bd29df0892efd1fa810 100644 (file)
@@ -10,7 +10,7 @@
 #include "utf8.h"
 #include "gpg-interface.h"
 
-static const char commit_tree_usage[] = "git commit-tree [(-p <sha1>)...] [-S[<keyid>]] [-m <message>] [-F <file>] <sha1> <changelog";
+static const char commit_tree_usage[] = "git commit-tree [(-p <sha1>)...] [-S[<keyid>]] [-m <message>] [-F <file>] <sha1>";
 
 static const char *sign_commit;
 
index 63772d016a23905d2e58bff0ad117673ed6651e7..dca09e2c3bd8a839b27e279c427ebba71e88966e 100644 (file)
@@ -775,7 +775,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
        s->hints = 0;
 
        if (clean_message_contents)
-               stripspace(&sb, 0);
+               strbuf_stripspace(&sb, 0);
 
        if (signoff)
                append_signoff(&sb, ignore_non_trailer(&sb), 0);
@@ -1014,7 +1014,7 @@ static int template_untouched(struct strbuf *sb)
        if (!template_file || strbuf_read_file(&tmpl, template_file, 0) <= 0)
                return 0;
 
-       stripspace(&tmpl, cleanup_mode == CLEANUP_ALL);
+       strbuf_stripspace(&tmpl, cleanup_mode == CLEANUP_ALL);
        if (!skip_prefix(sb->buf, tmpl.buf, &start))
                start = sb->buf;
        strbuf_release(&tmpl);
@@ -1726,7 +1726,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
                wt_status_truncate_message_at_cut_line(&sb);
 
        if (cleanup_mode != CLEANUP_NONE)
-               stripspace(&sb, cleanup_mode == CLEANUP_ALL);
+               strbuf_stripspace(&sb, cleanup_mode == CLEANUP_ALL);
        if (template_untouched(&sb) && !allow_empty_message) {
                rollback_index_files();
                fprintf(stderr, _("Aborting commit; you did not edit the message.\n"));
index eeeb21b1c46dae897ec9db51e58f8d15fc4f915e..b677923ffc2eec4b12ee9253ca4849ec063daa10 100644 (file)
@@ -394,15 +394,17 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
        if (gc_before_repack())
                return -1;
 
-       if (run_command_v_opt(repack.argv, RUN_GIT_CMD))
-               return error(FAILED_RUN, repack.argv[0]);
-
-       if (prune_expire) {
-               argv_array_push(&prune, prune_expire);
-               if (quiet)
-                       argv_array_push(&prune, "--no-progress");
-               if (run_command_v_opt(prune.argv, RUN_GIT_CMD))
-                       return error(FAILED_RUN, prune.argv[0]);
+       if (!repository_format_precious_objects) {
+               if (run_command_v_opt(repack.argv, RUN_GIT_CMD))
+                       return error(FAILED_RUN, repack.argv[0]);
+
+               if (prune_expire) {
+                       argv_array_push(&prune, prune_expire);
+                       if (quiet)
+                               argv_array_push(&prune, "--no-progress");
+                       if (run_command_v_opt(prune.argv, RUN_GIT_CMD))
+                               return error(FAILED_RUN, prune.argv[0]);
+               }
        }
 
        if (prune_worktrees_expire) {
index 6f4147ad02b98de298fb054f38fab8e6d10e7335..e21c5416cd478eab3b0dddcba5ca9eeefad83d34 100644 (file)
@@ -8,7 +8,7 @@
 #include "quote.h"
 
 static const char builtin_get_tar_commit_id_usage[] =
-"git get-tar-commit-id < <tarfile>";
+"git get-tar-commit-id";
 
 /* ustar header + extended global header content */
 #define RECORDSIZE     (512)
index 07fef3cc6b832c257bd3256dc6fff3d4c51760d9..43b098b76c0db28a93c6b1ebf1ea1131b0d67a0b 100644 (file)
@@ -78,7 +78,7 @@ int cmd_hash_object(int argc, const char **argv, const char *prefix)
 {
        static const char * const hash_object_usage[] = {
                N_("git hash-object [-t <type>] [-w] [--path=<file> | --no-filters] [--stdin] [--] <file>..."),
-               N_("git hash-object  --stdin-paths < <list-of-paths>"),
+               N_("git hash-object  --stdin-paths"),
                NULL
        };
        const char *type = blob_type;
index 999a5250fbe7e6d38a2c8927fa3c37233142ef19..f6df2741112144a38d048ebfd1fe201c8576a040 100644 (file)
 #include "builtin.h"
 #include "utf8.h"
 #include "strbuf.h"
-
-static FILE *cmitmsg, *patchfile, *fin, *fout;
-
-static int keep_subject;
-static int keep_non_patch_brackets_in_subject;
-static const char *metainfo_charset;
-static struct strbuf line = STRBUF_INIT;
-static struct strbuf name = STRBUF_INIT;
-static struct strbuf email = STRBUF_INIT;
-static char *message_id;
-
-static enum  {
-       TE_DONTCARE, TE_QP, TE_BASE64
-} transfer_encoding;
-
-static struct strbuf charset = STRBUF_INIT;
-static int patch_lines;
-static struct strbuf **p_hdr_data, **s_hdr_data;
-static int use_scissors;
-static int add_message_id;
-static int use_inbody_headers = 1;
-
-#define MAX_HDR_PARSED 10
-#define MAX_BOUNDARIES 5
-
-static void cleanup_space(struct strbuf *sb);
-
-
-static void get_sane_name(struct strbuf *out, struct strbuf *name, struct strbuf *email)
-{
-       struct strbuf *src = name;
-       if (name->len < 3 || 60 < name->len || strchr(name->buf, '@') ||
-               strchr(name->buf, '<') || strchr(name->buf, '>'))
-               src = email;
-       else if (name == out)
-               return;
-       strbuf_reset(out);
-       strbuf_addbuf(out, src);
-}
-
-static void parse_bogus_from(const struct strbuf *line)
-{
-       /* John Doe <johndoe> */
-
-       char *bra, *ket;
-       /* This is fallback, so do not bother if we already have an
-        * e-mail address.
-        */
-       if (email.len)
-               return;
-
-       bra = strchr(line->buf, '<');
-       if (!bra)
-               return;
-       ket = strchr(bra, '>');
-       if (!ket)
-               return;
-
-       strbuf_reset(&email);
-       strbuf_add(&email, bra + 1, ket - bra - 1);
-
-       strbuf_reset(&name);
-       strbuf_add(&name, line->buf, bra - line->buf);
-       strbuf_trim(&name);
-       get_sane_name(&name, &name, &email);
-}
-
-static void handle_from(const struct strbuf *from)
-{
-       char *at;
-       size_t el;
-       struct strbuf f;
-
-       strbuf_init(&f, from->len);
-       strbuf_addbuf(&f, from);
-
-       at = strchr(f.buf, '@');
-       if (!at) {
-               parse_bogus_from(from);
-               return;
-       }
-
-       /*
-        * If we already have one email, don't take any confusing lines
-        */
-       if (email.len && strchr(at + 1, '@')) {
-               strbuf_release(&f);
-               return;
-       }
-
-       /* Pick up the string around '@', possibly delimited with <>
-        * pair; that is the email part.
-        */
-       while (at > f.buf) {
-               char c = at[-1];
-               if (isspace(c))
-                       break;
-               if (c == '<') {
-                       at[-1] = ' ';
-                       break;
-               }
-               at--;
-       }
-       el = strcspn(at, " \n\t\r\v\f>");
-       strbuf_reset(&email);
-       strbuf_add(&email, at, el);
-       strbuf_remove(&f, at - f.buf, el + (at[el] ? 1 : 0));
-
-       /* The remainder is name.  It could be
-        *
-        * - "John Doe <john.doe@xz>"                   (a), or
-        * - "john.doe@xz (John Doe)"                   (b), or
-        * - "John (zzz) Doe <john.doe@xz> (Comment)"   (c)
-        *
-        * but we have removed the email part, so
-        *
-        * - remove extra spaces which could stay after email (case 'c'), and
-        * - trim from both ends, possibly removing the () pair at the end
-        *   (cases 'a' and 'b').
-        */
-       cleanup_space(&f);
-       strbuf_trim(&f);
-       if (f.buf[0] == '(' && f.len && f.buf[f.len - 1] == ')') {
-               strbuf_remove(&f, 0, 1);
-               strbuf_setlen(&f, f.len - 1);
-       }
-
-       get_sane_name(&name, &f, &email);
-       strbuf_release(&f);
-}
-
-static void handle_header(struct strbuf **out, const struct strbuf *line)
-{
-       if (!*out) {
-               *out = xmalloc(sizeof(struct strbuf));
-               strbuf_init(*out, line->len);
-       } else
-               strbuf_reset(*out);
-
-       strbuf_addbuf(*out, line);
-}
-
-/* NOTE NOTE NOTE.  We do not claim we do full MIME.  We just attempt
- * to have enough heuristics to grok MIME encoded patches often found
- * on our mailing lists.  For example, we do not even treat header lines
- * case insensitively.
- */
-
-static int slurp_attr(const char *line, const char *name, struct strbuf *attr)
-{
-       const char *ends, *ap = strcasestr(line, name);
-       size_t sz;
-
-       strbuf_setlen(attr, 0);
-       if (!ap)
-               return 0;
-       ap += strlen(name);
-       if (*ap == '"') {
-               ap++;
-               ends = "\"";
-       }
-       else
-               ends = "; \t";
-       sz = strcspn(ap, ends);
-       strbuf_add(attr, ap, sz);
-       return 1;
-}
-
-static struct strbuf *content[MAX_BOUNDARIES];
-
-static struct strbuf **content_top = content;
-
-static void handle_content_type(struct strbuf *line)
-{
-       struct strbuf *boundary = xmalloc(sizeof(struct strbuf));
-       strbuf_init(boundary, line->len);
-
-       if (slurp_attr(line->buf, "boundary=", boundary)) {
-               strbuf_insert(boundary, 0, "--", 2);
-               if (++content_top > &content[MAX_BOUNDARIES]) {
-                       fprintf(stderr, "Too many boundaries to handle\n");
-                       exit(1);
-               }
-               *content_top = boundary;
-               boundary = NULL;
-       }
-       slurp_attr(line->buf, "charset=", &charset);
-
-       if (boundary) {
-               strbuf_release(boundary);
-               free(boundary);
-       }
-}
-
-static void handle_message_id(const struct strbuf *line)
-{
-       if (add_message_id)
-               message_id = strdup(line->buf);
-}
-
-static void handle_content_transfer_encoding(const struct strbuf *line)
-{
-       if (strcasestr(line->buf, "base64"))
-               transfer_encoding = TE_BASE64;
-       else if (strcasestr(line->buf, "quoted-printable"))
-               transfer_encoding = TE_QP;
-       else
-               transfer_encoding = TE_DONTCARE;
-}
-
-static int is_multipart_boundary(const struct strbuf *line)
-{
-       return (((*content_top)->len <= line->len) &&
-               !memcmp(line->buf, (*content_top)->buf, (*content_top)->len));
-}
-
-static void cleanup_subject(struct strbuf *subject)
-{
-       size_t at = 0;
-
-       while (at < subject->len) {
-               char *pos;
-               size_t remove;
-
-               switch (subject->buf[at]) {
-               case 'r': case 'R':
-                       if (subject->len <= at + 3)
-                               break;
-                       if ((subject->buf[at + 1] == 'e' ||
-                            subject->buf[at + 1] == 'E') &&
-                           subject->buf[at + 2] == ':') {
-                               strbuf_remove(subject, at, 3);
-                               continue;
-                       }
-                       at++;
-                       break;
-               case ' ': case '\t': case ':':
-                       strbuf_remove(subject, at, 1);
-                       continue;
-               case '[':
-                       pos = strchr(subject->buf + at, ']');
-                       if (!pos)
-                               break;
-                       remove = pos - subject->buf + at + 1;
-                       if (!keep_non_patch_brackets_in_subject ||
-                           (7 <= remove &&
-                            memmem(subject->buf + at, remove, "PATCH", 5)))
-                               strbuf_remove(subject, at, remove);
-                       else {
-                               at += remove;
-                               /*
-                                * If the input had a space after the ], keep
-                                * it.  We don't bother with finding the end of
-                                * the space, since we later normalize it
-                                * anyway.
-                                */
-                               if (isspace(subject->buf[at]))
-                                       at += 1;
-                       }
-                       continue;
-               }
-               break;
-       }
-       strbuf_trim(subject);
-}
-
-static void cleanup_space(struct strbuf *sb)
-{
-       size_t pos, cnt;
-       for (pos = 0; pos < sb->len; pos++) {
-               if (isspace(sb->buf[pos])) {
-                       sb->buf[pos] = ' ';
-                       for (cnt = 0; isspace(sb->buf[pos + cnt + 1]); cnt++);
-                       strbuf_remove(sb, pos + 1, cnt);
-               }
-       }
-}
-
-static void decode_header(struct strbuf *line);
-static const char *header[MAX_HDR_PARSED] = {
-       "From","Subject","Date",
-};
-
-static inline int cmp_header(const struct strbuf *line, const char *hdr)
-{
-       int len = strlen(hdr);
-       return !strncasecmp(line->buf, hdr, len) && line->len > len &&
-                       line->buf[len] == ':' && isspace(line->buf[len + 1]);
-}
-
-static int is_format_patch_separator(const char *line, int len)
-{
-       static const char SAMPLE[] =
-               "From e6807f3efca28b30decfecb1732a56c7db1137ee Mon Sep 17 00:00:00 2001\n";
-       const char *cp;
-
-       if (len != strlen(SAMPLE))
-               return 0;
-       if (!skip_prefix(line, "From ", &cp))
-               return 0;
-       if (strspn(cp, "0123456789abcdef") != 40)
-               return 0;
-       cp += 40;
-       return !memcmp(SAMPLE + (cp - line), cp, strlen(SAMPLE) - (cp - line));
-}
-
-static int check_header(const struct strbuf *line,
-                               struct strbuf *hdr_data[], int overwrite)
-{
-       int i, ret = 0, len;
-       struct strbuf sb = STRBUF_INIT;
-       /* search for the interesting parts */
-       for (i = 0; header[i]; i++) {
-               int len = strlen(header[i]);
-               if ((!hdr_data[i] || overwrite) && cmp_header(line, header[i])) {
-                       /* Unwrap inline B and Q encoding, and optionally
-                        * normalize the meta information to utf8.
-                        */
-                       strbuf_add(&sb, line->buf + len + 2, line->len - len - 2);
-                       decode_header(&sb);
-                       handle_header(&hdr_data[i], &sb);
-                       ret = 1;
-                       goto check_header_out;
-               }
-       }
-
-       /* Content stuff */
-       if (cmp_header(line, "Content-Type")) {
-               len = strlen("Content-Type: ");
-               strbuf_add(&sb, line->buf + len, line->len - len);
-               decode_header(&sb);
-               strbuf_insert(&sb, 0, "Content-Type: ", len);
-               handle_content_type(&sb);
-               ret = 1;
-               goto check_header_out;
-       }
-       if (cmp_header(line, "Content-Transfer-Encoding")) {
-               len = strlen("Content-Transfer-Encoding: ");
-               strbuf_add(&sb, line->buf + len, line->len - len);
-               decode_header(&sb);
-               handle_content_transfer_encoding(&sb);
-               ret = 1;
-               goto check_header_out;
-       }
-       if (cmp_header(line, "Message-Id")) {
-               len = strlen("Message-Id: ");
-               strbuf_add(&sb, line->buf + len, line->len - len);
-               decode_header(&sb);
-               handle_message_id(&sb);
-               ret = 1;
-               goto check_header_out;
-       }
-
-       /* for inbody stuff */
-       if (starts_with(line->buf, ">From") && isspace(line->buf[5])) {
-               ret = is_format_patch_separator(line->buf + 1, line->len - 1);
-               goto check_header_out;
-       }
-       if (starts_with(line->buf, "[PATCH]") && isspace(line->buf[7])) {
-               for (i = 0; header[i]; i++) {
-                       if (!strcmp("Subject", header[i])) {
-                               handle_header(&hdr_data[i], line);
-                               ret = 1;
-                               goto check_header_out;
-                       }
-               }
-       }
-
-check_header_out:
-       strbuf_release(&sb);
-       return ret;
-}
-
-static int is_rfc2822_header(const struct strbuf *line)
-{
-       /*
-        * The section that defines the loosest possible
-        * field name is "3.6.8 Optional fields".
-        *
-        * optional-field = field-name ":" unstructured CRLF
-        * field-name = 1*ftext
-        * ftext = %d33-57 / %59-126
-        */
-       int ch;
-       char *cp = line->buf;
-
-       /* Count mbox From headers as headers */
-       if (starts_with(cp, "From ") || starts_with(cp, ">From "))
-               return 1;
-
-       while ((ch = *cp++)) {
-               if (ch == ':')
-                       return 1;
-               if ((33 <= ch && ch <= 57) ||
-                   (59 <= ch && ch <= 126))
-                       continue;
-               break;
-       }
-       return 0;
-}
-
-static int read_one_header_line(struct strbuf *line, FILE *in)
-{
-       /* Get the first part of the line. */
-       if (strbuf_getline(line, in, '\n'))
-               return 0;
-
-       /*
-        * Is it an empty line or not a valid rfc2822 header?
-        * If so, stop here, and return false ("not a header")
-        */
-       strbuf_rtrim(line);
-       if (!line->len || !is_rfc2822_header(line)) {
-               /* Re-add the newline */
-               strbuf_addch(line, '\n');
-               return 0;
-       }
-
-       /*
-        * Now we need to eat all the continuation lines..
-        * Yuck, 2822 header "folding"
-        */
-       for (;;) {
-               int peek;
-               struct strbuf continuation = STRBUF_INIT;
-
-               peek = fgetc(in); ungetc(peek, in);
-               if (peek != ' ' && peek != '\t')
-                       break;
-               if (strbuf_getline(&continuation, in, '\n'))
-                       break;
-               continuation.buf[0] = ' ';
-               strbuf_rtrim(&continuation);
-               strbuf_addbuf(line, &continuation);
-       }
-
-       return 1;
-}
-
-static struct strbuf *decode_q_segment(const struct strbuf *q_seg, int rfc2047)
-{
-       const char *in = q_seg->buf;
-       int c;
-       struct strbuf *out = xmalloc(sizeof(struct strbuf));
-       strbuf_init(out, q_seg->len);
-
-       while ((c = *in++) != 0) {
-               if (c == '=') {
-                       int d = *in++;
-                       if (d == '\n' || !d)
-                               break; /* drop trailing newline */
-                       strbuf_addch(out, (hexval(d) << 4) | hexval(*in++));
-                       continue;
-               }
-               if (rfc2047 && c == '_') /* rfc2047 4.2 (2) */
-                       c = 0x20;
-               strbuf_addch(out, c);
-       }
-       return out;
-}
-
-static struct strbuf *decode_b_segment(const struct strbuf *b_seg)
-{
-       /* Decode in..ep, possibly in-place to ot */
-       int c, pos = 0, acc = 0;
-       const char *in = b_seg->buf;
-       struct strbuf *out = xmalloc(sizeof(struct strbuf));
-       strbuf_init(out, b_seg->len);
-
-       while ((c = *in++) != 0) {
-               if (c == '+')
-                       c = 62;
-               else if (c == '/')
-                       c = 63;
-               else if ('A' <= c && c <= 'Z')
-                       c -= 'A';
-               else if ('a' <= c && c <= 'z')
-                       c -= 'a' - 26;
-               else if ('0' <= c && c <= '9')
-                       c -= '0' - 52;
-               else
-                       continue; /* garbage */
-               switch (pos++) {
-               case 0:
-                       acc = (c << 2);
-                       break;
-               case 1:
-                       strbuf_addch(out, (acc | (c >> 4)));
-                       acc = (c & 15) << 4;
-                       break;
-               case 2:
-                       strbuf_addch(out, (acc | (c >> 2)));
-                       acc = (c & 3) << 6;
-                       break;
-               case 3:
-                       strbuf_addch(out, (acc | c));
-                       acc = pos = 0;
-                       break;
-               }
-       }
-       return out;
-}
-
-static void convert_to_utf8(struct strbuf *line, const char *charset)
-{
-       char *out;
-
-       if (!charset || !*charset)
-               return;
-
-       if (same_encoding(metainfo_charset, charset))
-               return;
-       out = reencode_string(line->buf, metainfo_charset, charset);
-       if (!out)
-               die("cannot convert from %s to %s",
-                   charset, metainfo_charset);
-       strbuf_attach(line, out, strlen(out), strlen(out));
-}
-
-static int decode_header_bq(struct strbuf *it)
-{
-       char *in, *ep, *cp;
-       struct strbuf outbuf = STRBUF_INIT, *dec;
-       struct strbuf charset_q = STRBUF_INIT, piecebuf = STRBUF_INIT;
-       int rfc2047 = 0;
-
-       in = it->buf;
-       while (in - it->buf <= it->len && (ep = strstr(in, "=?")) != NULL) {
-               int encoding;
-               strbuf_reset(&charset_q);
-               strbuf_reset(&piecebuf);
-               rfc2047 = 1;
-
-               if (in != ep) {
-                       /*
-                        * We are about to process an encoded-word
-                        * that begins at ep, but there is something
-                        * before the encoded word.
-                        */
-                       char *scan;
-                       for (scan = in; scan < ep; scan++)
-                               if (!isspace(*scan))
-                                       break;
-
-                       if (scan != ep || in == it->buf) {
-                               /*
-                                * We should not lose that "something",
-                                * unless we have just processed an
-                                * encoded-word, and there is only LWS
-                                * before the one we are about to process.
-                                */
-                               strbuf_add(&outbuf, in, ep - in);
-                       }
-               }
-               /* E.g.
-                * ep : "=?iso-2022-jp?B?GyR...?= foo"
-                * ep : "=?ISO-8859-1?Q?Foo=FCbar?= baz"
-                */
-               ep += 2;
-
-               if (ep - it->buf >= it->len || !(cp = strchr(ep, '?')))
-                       goto decode_header_bq_out;
-
-               if (cp + 3 - it->buf > it->len)
-                       goto decode_header_bq_out;
-               strbuf_add(&charset_q, ep, cp - ep);
-
-               encoding = cp[1];
-               if (!encoding || cp[2] != '?')
-                       goto decode_header_bq_out;
-               ep = strstr(cp + 3, "?=");
-               if (!ep)
-                       goto decode_header_bq_out;
-               strbuf_add(&piecebuf, cp + 3, ep - cp - 3);
-               switch (tolower(encoding)) {
-               default:
-                       goto decode_header_bq_out;
-               case 'b':
-                       dec = decode_b_segment(&piecebuf);
-                       break;
-               case 'q':
-                       dec = decode_q_segment(&piecebuf, 1);
-                       break;
-               }
-               if (metainfo_charset)
-                       convert_to_utf8(dec, charset_q.buf);
-
-               strbuf_addbuf(&outbuf, dec);
-               strbuf_release(dec);
-               free(dec);
-               in = ep + 2;
-       }
-       strbuf_addstr(&outbuf, in);
-       strbuf_reset(it);
-       strbuf_addbuf(it, &outbuf);
-decode_header_bq_out:
-       strbuf_release(&outbuf);
-       strbuf_release(&charset_q);
-       strbuf_release(&piecebuf);
-       return rfc2047;
-}
-
-static void decode_header(struct strbuf *it)
-{
-       if (decode_header_bq(it))
-               return;
-       /* otherwise "it" is a straight copy of the input.
-        * This can be binary guck but there is no charset specified.
-        */
-       if (metainfo_charset)
-               convert_to_utf8(it, "");
-}
-
-static void decode_transfer_encoding(struct strbuf *line)
-{
-       struct strbuf *ret;
-
-       switch (transfer_encoding) {
-       case TE_QP:
-               ret = decode_q_segment(line, 0);
-               break;
-       case TE_BASE64:
-               ret = decode_b_segment(line);
-               break;
-       case TE_DONTCARE:
-       default:
-               return;
-       }
-       strbuf_reset(line);
-       strbuf_addbuf(line, ret);
-       strbuf_release(ret);
-       free(ret);
-}
-
-static void handle_filter(struct strbuf *line);
-
-static int find_boundary(void)
-{
-       while (!strbuf_getline(&line, fin, '\n')) {
-               if (*content_top && is_multipart_boundary(&line))
-                       return 1;
-       }
-       return 0;
-}
-
-static int handle_boundary(void)
-{
-       struct strbuf newline = STRBUF_INIT;
-
-       strbuf_addch(&newline, '\n');
-again:
-       if (line.len >= (*content_top)->len + 2 &&
-           !memcmp(line.buf + (*content_top)->len, "--", 2)) {
-               /* we hit an end boundary */
-               /* pop the current boundary off the stack */
-               strbuf_release(*content_top);
-               free(*content_top);
-               *content_top = NULL;
-
-               /* technically won't happen as is_multipart_boundary()
-                  will fail first.  But just in case..
-                */
-               if (--content_top < content) {
-                       fprintf(stderr, "Detected mismatched boundaries, "
-                                       "can't recover\n");
-                       exit(1);
-               }
-               handle_filter(&newline);
-               strbuf_release(&newline);
-
-               /* skip to the next boundary */
-               if (!find_boundary())
-                       return 0;
-               goto again;
-       }
-
-       /* set some defaults */
-       transfer_encoding = TE_DONTCARE;
-       strbuf_reset(&charset);
-
-       /* slurp in this section's info */
-       while (read_one_header_line(&line, fin))
-               check_header(&line, p_hdr_data, 0);
-
-       strbuf_release(&newline);
-       /* replenish line */
-       if (strbuf_getline(&line, fin, '\n'))
-               return 0;
-       strbuf_addch(&line, '\n');
-       return 1;
-}
-
-static inline int patchbreak(const struct strbuf *line)
-{
-       size_t i;
-
-       /* Beginning of a "diff -" header? */
-       if (starts_with(line->buf, "diff -"))
-               return 1;
-
-       /* CVS "Index: " line? */
-       if (starts_with(line->buf, "Index: "))
-               return 1;
-
-       /*
-        * "--- <filename>" starts patches without headers
-        * "---<sp>*" is a manual separator
-        */
-       if (line->len < 4)
-               return 0;
-
-       if (starts_with(line->buf, "---")) {
-               /* space followed by a filename? */
-               if (line->buf[3] == ' ' && !isspace(line->buf[4]))
-                       return 1;
-               /* Just whitespace? */
-               for (i = 3; i < line->len; i++) {
-                       unsigned char c = line->buf[i];
-                       if (c == '\n')
-                               return 1;
-                       if (!isspace(c))
-                               break;
-               }
-               return 0;
-       }
-       return 0;
-}
-
-static int is_scissors_line(const struct strbuf *line)
-{
-       size_t i, len = line->len;
-       int scissors = 0, gap = 0;
-       int first_nonblank = -1;
-       int last_nonblank = 0, visible, perforation = 0, in_perforation = 0;
-       const char *buf = line->buf;
-
-       for (i = 0; i < len; i++) {
-               if (isspace(buf[i])) {
-                       if (in_perforation) {
-                               perforation++;
-                               gap++;
-                       }
-                       continue;
-               }
-               last_nonblank = i;
-               if (first_nonblank < 0)
-                       first_nonblank = i;
-               if (buf[i] == '-') {
-                       in_perforation = 1;
-                       perforation++;
-                       continue;
-               }
-               if (i + 1 < len &&
-                   (!memcmp(buf + i, ">8", 2) || !memcmp(buf + i, "8<", 2) ||
-                    !memcmp(buf + i, ">%", 2) || !memcmp(buf + i, "%<", 2))) {
-                       in_perforation = 1;
-                       perforation += 2;
-                       scissors += 2;
-                       i++;
-                       continue;
-               }
-               in_perforation = 0;
-       }
-
-       /*
-        * The mark must be at least 8 bytes long (e.g. "-- >8 --").
-        * Even though there can be arbitrary cruft on the same line
-        * (e.g. "cut here"), in order to avoid misidentification, the
-        * perforation must occupy more than a third of the visible
-        * width of the line, and dashes and scissors must occupy more
-        * than half of the perforation.
-        */
-
-       visible = last_nonblank - first_nonblank + 1;
-       return (scissors && 8 <= visible &&
-               visible < perforation * 3 &&
-               gap * 2 < perforation);
-}
-
-static int handle_commit_msg(struct strbuf *line)
-{
-       static int still_looking = 1;
-
-       if (!cmitmsg)
-               return 0;
-
-       if (still_looking) {
-               if (!line->len || (line->len == 1 && line->buf[0] == '\n'))
-                       return 0;
-       }
-
-       if (use_inbody_headers && still_looking) {
-               still_looking = check_header(line, s_hdr_data, 0);
-               if (still_looking)
-                       return 0;
-       } else
-               /* Only trim the first (blank) line of the commit message
-                * when ignoring in-body headers.
-                */
-               still_looking = 0;
-
-       /* normalize the log message to UTF-8. */
-       if (metainfo_charset)
-               convert_to_utf8(line, charset.buf);
-
-       if (use_scissors && is_scissors_line(line)) {
-               int i;
-               if (fseek(cmitmsg, 0L, SEEK_SET))
-                       die_errno("Could not rewind output message file");
-               if (ftruncate(fileno(cmitmsg), 0))
-                       die_errno("Could not truncate output message file at scissors");
-               still_looking = 1;
-
-               /*
-                * We may have already read "secondary headers"; purge
-                * them to give ourselves a clean restart.
-                */
-               for (i = 0; header[i]; i++) {
-                       if (s_hdr_data[i])
-                               strbuf_release(s_hdr_data[i]);
-                       s_hdr_data[i] = NULL;
-               }
-               return 0;
-       }
-
-       if (patchbreak(line)) {
-               if (message_id)
-                       fprintf(cmitmsg, "Message-Id: %s\n", message_id);
-               fclose(cmitmsg);
-               cmitmsg = NULL;
-               return 1;
-       }
-
-       fputs(line->buf, cmitmsg);
-       return 0;
-}
-
-static void handle_patch(const struct strbuf *line)
-{
-       fwrite(line->buf, 1, line->len, patchfile);
-       patch_lines++;
-}
-
-static void handle_filter(struct strbuf *line)
-{
-       static int filter = 0;
-
-       /* filter tells us which part we left off on */
-       switch (filter) {
-       case 0:
-               if (!handle_commit_msg(line))
-                       break;
-               filter++;
-       case 1:
-               handle_patch(line);
-               break;
-       }
-}
-
-static void handle_body(void)
-{
-       struct strbuf prev = STRBUF_INIT;
-
-       /* Skip up to the first boundary */
-       if (*content_top) {
-               if (!find_boundary())
-                       goto handle_body_out;
-       }
-
-       do {
-               /* process any boundary lines */
-               if (*content_top && is_multipart_boundary(&line)) {
-                       /* flush any leftover */
-                       if (prev.len) {
-                               handle_filter(&prev);
-                               strbuf_reset(&prev);
-                       }
-                       if (!handle_boundary())
-                               goto handle_body_out;
-               }
-
-               /* Unwrap transfer encoding */
-               decode_transfer_encoding(&line);
-
-               switch (transfer_encoding) {
-               case TE_BASE64:
-               case TE_QP:
-               {
-                       struct strbuf **lines, **it, *sb;
-
-                       /* Prepend any previous partial lines */
-                       strbuf_insert(&line, 0, prev.buf, prev.len);
-                       strbuf_reset(&prev);
-
-                       /*
-                        * This is a decoded line that may contain
-                        * multiple new lines.  Pass only one chunk
-                        * at a time to handle_filter()
-                        */
-                       lines = strbuf_split(&line, '\n');
-                       for (it = lines; (sb = *it); it++) {
-                               if (*(it + 1) == NULL) /* The last line */
-                                       if (sb->buf[sb->len - 1] != '\n') {
-                                               /* Partial line, save it for later. */
-                                               strbuf_addbuf(&prev, sb);
-                                               break;
-                                       }
-                               handle_filter(sb);
-                       }
-                       /*
-                        * The partial chunk is saved in "prev" and will be
-                        * appended by the next iteration of read_line_with_nul().
-                        */
-                       strbuf_list_free(lines);
-                       break;
-               }
-               default:
-                       handle_filter(&line);
-               }
-
-       } while (!strbuf_getwholeline(&line, fin, '\n'));
-
-handle_body_out:
-       strbuf_release(&prev);
-}
-
-static void output_header_lines(FILE *fout, const char *hdr, const struct strbuf *data)
-{
-       const char *sp = data->buf;
-       while (1) {
-               char *ep = strchr(sp, '\n');
-               int len;
-               if (!ep)
-                       len = strlen(sp);
-               else
-                       len = ep - sp;
-               fprintf(fout, "%s: %.*s\n", hdr, len, sp);
-               if (!ep)
-                       break;
-               sp = ep + 1;
-       }
-}
-
-static void handle_info(void)
-{
-       struct strbuf *hdr;
-       int i;
-
-       for (i = 0; header[i]; i++) {
-               /* only print inbody headers if we output a patch file */
-               if (patch_lines && s_hdr_data[i])
-                       hdr = s_hdr_data[i];
-               else if (p_hdr_data[i])
-                       hdr = p_hdr_data[i];
-               else
-                       continue;
-
-               if (!strcmp(header[i], "Subject")) {
-                       if (!keep_subject) {
-                               cleanup_subject(hdr);
-                               cleanup_space(hdr);
-                       }
-                       output_header_lines(fout, "Subject", hdr);
-               } else if (!strcmp(header[i], "From")) {
-                       cleanup_space(hdr);
-                       handle_from(hdr);
-                       fprintf(fout, "Author: %s\n", name.buf);
-                       fprintf(fout, "Email: %s\n", email.buf);
-               } else {
-                       cleanup_space(hdr);
-                       fprintf(fout, "%s: %s\n", header[i], hdr->buf);
-               }
-       }
-       fprintf(fout, "\n");
-}
-
-static int mailinfo(FILE *in, FILE *out, const char *msg, const char *patch)
-{
-       int peek;
-       fin = in;
-       fout = out;
-
-       cmitmsg = fopen(msg, "w");
-       if (!cmitmsg) {
-               perror(msg);
-               return -1;
-       }
-       patchfile = fopen(patch, "w");
-       if (!patchfile) {
-               perror(patch);
-               fclose(cmitmsg);
-               return -1;
-       }
-
-       p_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(*p_hdr_data));
-       s_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(*s_hdr_data));
-
-       do {
-               peek = fgetc(in);
-       } while (isspace(peek));
-       ungetc(peek, in);
-
-       /* process the email header */
-       while (read_one_header_line(&line, fin))
-               check_header(&line, p_hdr_data, 1);
-
-       handle_body();
-       handle_info();
-
-       return 0;
-}
-
-static int git_mailinfo_config(const char *var, const char *value, void *unused)
-{
-       if (!starts_with(var, "mailinfo."))
-               return git_default_config(var, value, unused);
-       if (!strcmp(var, "mailinfo.scissors")) {
-               use_scissors = git_config_bool(var, value);
-               return 0;
-       }
-       /* perhaps others here */
-       return 0;
-}
+#include "mailinfo.h"
 
 static const char mailinfo_usage[] =
        "git mailinfo [-k | -b] [-m | --message-id] [-u | --encoding=<encoding> | -n] [--scissors | --no-scissors] <msg> <patch> < mail >info";
@@ -1036,34 +14,36 @@ static const char mailinfo_usage[] =
 int cmd_mailinfo(int argc, const char **argv, const char *prefix)
 {
        const char *def_charset;
+       struct mailinfo mi;
+       int status;
 
        /* NEEDSWORK: might want to do the optional .git/ directory
         * discovery
         */
-       git_config(git_mailinfo_config, NULL);
+       setup_mailinfo(&mi);
 
        def_charset = get_commit_output_encoding();
-       metainfo_charset = def_charset;
+       mi.metainfo_charset = def_charset;
 
        while (1 < argc && argv[1][0] == '-') {
                if (!strcmp(argv[1], "-k"))
-                       keep_subject = 1;
+                       mi.keep_subject = 1;
                else if (!strcmp(argv[1], "-b"))
-                       keep_non_patch_brackets_in_subject = 1;
+                       mi.keep_non_patch_brackets_in_subject = 1;
                else if (!strcmp(argv[1], "-m") || !strcmp(argv[1], "--message-id"))
-                       add_message_id = 1;
+                       mi.add_message_id = 1;
                else if (!strcmp(argv[1], "-u"))
-                       metainfo_charset = def_charset;
+                       mi.metainfo_charset = def_charset;
                else if (!strcmp(argv[1], "-n"))
-                       metainfo_charset = NULL;
+                       mi.metainfo_charset = NULL;
                else if (starts_with(argv[1], "--encoding="))
-                       metainfo_charset = argv[1] + 11;
+                       mi.metainfo_charset = argv[1] + 11;
                else if (!strcmp(argv[1], "--scissors"))
-                       use_scissors = 1;
+                       mi.use_scissors = 1;
                else if (!strcmp(argv[1], "--no-scissors"))
-                       use_scissors = 0;
+                       mi.use_scissors = 0;
                else if (!strcmp(argv[1], "--no-inbody-headers"))
-                       use_inbody_headers = 0;
+                       mi.use_inbody_headers = 0;
                else
                        usage(mailinfo_usage);
                argc--; argv++;
@@ -1072,5 +52,10 @@ int cmd_mailinfo(int argc, const char **argv, const char *prefix)
        if (argc != 3)
                usage(mailinfo_usage);
 
-       return !!mailinfo(stdin, stdout, argv[1], argv[2]);
+       mi.input = stdin;
+       mi.output = stdout;
+       status = !!mailinfo(&mi, argv[1], argv[2]);
+       clear_mailinfo(&mi);
+
+       return status;
 }
index a0a93282922ebb8c58b79ff365d859500041351a..977ffff2876de4d817025bbf6d11ec251b674141 100644 (file)
@@ -806,7 +806,7 @@ static void prepare_to_commit(struct commit_list *remoteheads)
                        abort_commit(remoteheads, NULL);
        }
        read_merge_msg(&msg);
-       stripspace(&msg, 0 < option_edit);
+       strbuf_stripspace(&msg, 0 < option_edit);
        if (!msg.len)
                abort_commit(remoteheads, _("Empty commit message."));
        strbuf_release(&merge_msg);
index 640ab64f418208842ae3901ce37ac8918740037e..031b750f068dea5e1652129a5df6d684f6bca850 100644 (file)
@@ -154,7 +154,7 @@ int cmd_mktag(int argc, const char **argv, const char *prefix)
        unsigned char result_sha1[20];
 
        if (argc != 1)
-               usage("git mktag < signaturefile");
+               usage("git mktag");
 
        if (strbuf_read(&buf, 0, 4096) < 0) {
                die_errno("could not read from stdin");
index 3608c64785283ffe7cec6df425618a279d1616c5..515cebbeb8a33eb982a1ddccdc9d69e09beefb1d 100644 (file)
@@ -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>]]"),
@@ -192,7 +192,7 @@ static void prepare_note_data(const unsigned char *object, struct note_data *d,
                if (launch_editor(d->edit_path, &d->buf, NULL)) {
                        die(_("Please supply the note contents using either -m or -F option"));
                }
-               stripspace(&d->buf, 1);
+               strbuf_stripspace(&d->buf, 1);
        }
 }
 
@@ -215,7 +215,7 @@ static int parse_msg_arg(const struct option *opt, const char *arg, int unset)
        if (d->buf.len)
                strbuf_addch(&d->buf, '\n');
        strbuf_addstr(&d->buf, arg);
-       stripspace(&d->buf, 0);
+       strbuf_stripspace(&d->buf, 0);
 
        d->given = 1;
        return 0;
@@ -232,7 +232,7 @@ static int parse_file_arg(const struct option *opt, const char *arg, int unset)
                        die_errno(_("cannot read '%s'"), arg);
        } else if (strbuf_read_file(&d->buf, arg, 1024) < 0)
                die_errno(_("could not open or read '%s'"), arg);
-       stripspace(&d->buf, 0);
+       strbuf_stripspace(&d->buf, 0);
 
        d->given = 1;
        return 0;
index ba34dac4d2fae9bce24932c92fcee2a4bfc0f102..366ce5a5d419649dba34f607ed3f54b6a28bd3ea 100644 (file)
@@ -165,7 +165,7 @@ static void generate_id_list(int stable)
        strbuf_release(&line_buf);
 }
 
-static const char patch_id_usage[] = "git patch-id [--stable | --unstable] < patch";
+static const char patch_id_usage[] = "git patch-id [--stable | --unstable]";
 
 static int git_patch_id_config(const char *var, const char *value, void *cb)
 {
index 10b03d3e4cb5ced78118251ac390dd6a33f9a71b..8f4f0522856b988a8798fd65c6d81d8826709aa2 100644 (file)
@@ -119,6 +119,9 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
 
        argc = parse_options(argc, argv, prefix, options, prune_usage, 0);
 
+       if (repository_format_precious_objects)
+               die(_("cannot prune in a precious-objects repo"));
+
        while (argc--) {
                unsigned char sha1[20];
                const char *name = *argv++;
index 70b9b1eaf17f5447021f7174f31e5c19d9754486..945611006a4ddcad1b834d87fe62698549ebb837 100644 (file)
@@ -193,6 +193,9 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
        argc = parse_options(argc, argv, prefix, builtin_repack_options,
                                git_repack_usage, 0);
 
+       if (delete_redundant && repository_format_precious_objects)
+               die(_("cannot delete packs in a precious-objects repo"));
+
        if (pack_kept_objects < 0)
                pack_kept_objects = write_bitmaps;
 
index 131ef28e5cb5d0a08bc00a7ad99e137bdd44eb66..264c3920075952b179170b65960e47b3a5b06c61 100644 (file)
@@ -8,7 +8,7 @@
 
 static const char * const show_ref_usage[] = {
        N_("git show-ref [-q | --quiet] [--verify] [--head] [-d | --dereference] [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags] [--heads] [--] [<pattern>...]"),
-       N_("git show-ref --exclude-existing[=<pattern>] < <ref-list>"),
+       N_("git show-ref --exclude-existing[=<pattern>]"),
        NULL
 };
 
index 1259ed708b6b5dd1322b6db5683cf5555f41570f..7ff8434f7c3cf71dbd77cbf868d5dc56dd943fdb 100644 (file)
@@ -1,71 +1,7 @@
 #include "builtin.h"
 #include "cache.h"
-
-/*
- * Returns the length of a line, without trailing spaces.
- *
- * If the line ends with newline, it will be removed too.
- */
-static size_t cleanup(char *line, size_t len)
-{
-       while (len) {
-               unsigned char c = line[len - 1];
-               if (!isspace(c))
-                       break;
-               len--;
-       }
-
-       return len;
-}
-
-/*
- * Remove empty lines from the beginning and end
- * and also trailing spaces from every line.
- *
- * Turn multiple consecutive empty lines between paragraphs
- * into just one empty line.
- *
- * If the input has only empty lines and spaces,
- * no output will be produced.
- *
- * If last line does not have a newline at the end, one is added.
- *
- * Enable skip_comments to skip every line starting with comment
- * character.
- */
-void stripspace(struct strbuf *sb, int skip_comments)
-{
-       int empties = 0;
-       size_t i, j, len, newlen;
-       char *eol;
-
-       /* We may have to add a newline. */
-       strbuf_grow(sb, 1);
-
-       for (i = j = 0; i < sb->len; i += len, j += newlen) {
-               eol = memchr(sb->buf + i, '\n', sb->len - i);
-               len = eol ? eol - (sb->buf + i) + 1 : sb->len - i;
-
-               if (skip_comments && len && sb->buf[i] == comment_line_char) {
-                       newlen = 0;
-                       continue;
-               }
-               newlen = cleanup(sb->buf + i, len);
-
-               /* Not just an empty line? */
-               if (newlen) {
-                       if (empties > 0 && j > 0)
-                               sb->buf[j++] = '\n';
-                       empties = 0;
-                       memmove(sb->buf + j, sb->buf + i, newlen);
-                       sb->buf[newlen + j++] = '\n';
-               } else {
-                       empties++;
-               }
-       }
-
-       strbuf_setlen(sb, j);
-}
+#include "parse-options.h"
+#include "strbuf.h"
 
 static void comment_lines(struct strbuf *buf)
 {
@@ -77,41 +13,45 @@ static void comment_lines(struct strbuf *buf)
        free(msg);
 }
 
-static const char *usage_msg = "\n"
-"  git stripspace [-s | --strip-comments] < input\n"
-"  git stripspace [-c | --comment-lines] < input";
+static const char * const stripspace_usage[] = {
+       N_("git stripspace [-s | --strip-comments]"),
+       N_("git stripspace [-c | --comment-lines]"),
+       NULL
+};
+
+enum stripspace_mode {
+       STRIP_DEFAULT = 0,
+       STRIP_COMMENTS,
+       COMMENT_LINES
+};
 
 int cmd_stripspace(int argc, const char **argv, const char *prefix)
 {
        struct strbuf buf = STRBUF_INIT;
-       int strip_comments = 0;
-       enum { INVAL = 0, STRIP_SPACE = 1, COMMENT_LINES = 2 } mode = STRIP_SPACE;
-
-       if (argc == 2) {
-               if (!strcmp(argv[1], "-s") ||
-                   !strcmp(argv[1], "--strip-comments")) {
-                       strip_comments = 1;
-               } else if (!strcmp(argv[1], "-c") ||
-                          !strcmp(argv[1], "--comment-lines")) {
-                       mode = COMMENT_LINES;
-               } else {
-                       mode = INVAL;
-               }
-       } else if (argc > 1) {
-               mode = INVAL;
-       }
-
-       if (mode == INVAL)
-               usage(usage_msg);
-
-       if (strip_comments || mode == COMMENT_LINES)
+       enum stripspace_mode mode = STRIP_DEFAULT;
+
+       const struct option options[] = {
+               OPT_CMDMODE('s', "strip-comments", &mode,
+                           N_("skip and remove all lines starting with comment character"),
+                           STRIP_COMMENTS),
+               OPT_CMDMODE('c', "comment-lines", &mode,
+                           N_("prepend comment character and blank to each line"),
+                           COMMENT_LINES),
+               OPT_END()
+       };
+
+       argc = parse_options(argc, argv, prefix, options, stripspace_usage, 0);
+       if (argc)
+               usage_with_options(stripspace_usage, options);
+
+       if (mode == STRIP_COMMENTS || mode == COMMENT_LINES)
                git_config(git_default_config, NULL);
 
        if (strbuf_read(&buf, 0, 1024) < 0)
                die_errno("could not read the input");
 
-       if (mode == STRIP_SPACE)
-               stripspace(&buf, strip_comments);
+       if (mode == STRIP_DEFAULT || mode == STRIP_COMMENTS)
+               strbuf_stripspace(&buf, mode == STRIP_COMMENTS);
        else
                comment_lines(&buf);
 
index 9e17dca7cac30738c966d0e23258f56c0cb9bc70..8db8c87e57ef05edadce8de19316572498c40753 100644 (file)
@@ -52,6 +52,7 @@ static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting, con
        }
 
        verify_ref_format(format);
+       filter->with_commit_tag_algo = 1;
        filter_refs(&array, filter, FILTER_REFS_TAGS);
        ref_array_sort(sorting, &array);
 
@@ -268,7 +269,7 @@ static void create_tag(const unsigned char *object, const char *tag,
        }
 
        if (opt->cleanup_mode != CLEANUP_NONE)
-               stripspace(buf, opt->cleanup_mode == CLEANUP_ALL);
+               strbuf_stripspace(buf, opt->cleanup_mode == CLEANUP_ALL);
 
        if (!opt->message_given && !buf->len)
                die(_("no tag message?"));
index 7cc086f5f2fb4c4aa9a24a6b676f1bd6626f1b69..7c3e79c48d68251f8af94d91ef51d22567c53c07 100644 (file)
@@ -13,7 +13,7 @@
 #include "fsck.h"
 
 static int dry_run, quiet, recover, has_errors, strict;
-static const char unpack_usage[] = "git unpack-objects [-n] [-q] [-r] [--strict] < pack-file";
+static const char unpack_usage[] = "git unpack-objects [-n] [-q] [-r] [--strict]";
 
 /* We always read in 4kB chunks. */
 static unsigned char buffer[4096];
index 71bb770f7a4b4b864efcbf4c3983bbd4a645f4e2..d281f6d887691fb43ebb052091f84e78aabe6d01 100644 (file)
@@ -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 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/cache.h b/cache.h
index f735d14dc29310f07ee48ad54d1584c881f97110..3ba0b8f3d7a86eac8839bb175b0291b76013d263 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -521,7 +521,8 @@ extern int write_locked_index(struct index_state *, struct lock_file *lock, unsi
 extern int discard_index(struct index_state *);
 extern int unmerged_index(const struct index_state *);
 extern int verify_path(const char *path);
-extern struct cache_entry *index_dir_exists(struct index_state *istate, const char *name, int namelen);
+extern int index_dir_exists(struct index_state *istate, const char *name, int namelen);
+extern void adjust_dirname_case(struct index_state *istate, char *name);
 extern struct cache_entry *index_file_exists(struct index_state *istate, const char *name, int namelen, int igncase);
 extern int index_name_pos(const struct index_state *, const char *name, int namelen);
 #define ADD_CACHE_OK_TO_ADD 1          /* Ok to add */
@@ -697,8 +698,15 @@ extern char *notes_ref_name;
 
 extern int grafts_replace_parents;
 
+/*
+ * GIT_REPO_VERSION is the version we write by default. The
+ * _READ variant is the highest number we know how to
+ * handle.
+ */
 #define GIT_REPO_VERSION 0
+#define GIT_REPO_VERSION_READ 1
 extern int repository_format_version;
+extern int repository_format_precious_objects;
 extern int check_repository_format(void);
 
 #define MTIME_CHANGED  0x0001
index a168800ae0d1a6ffe741dfda26dbe5133cc4c4fc..90bdb1edde15ccd39055c376f5d920c29af013c0 100644 (file)
@@ -2131,7 +2131,7 @@ void mingw_startup()
 
 int uname(struct utsname *buf)
 {
-       DWORD v = GetVersion();
+       unsigned v = (unsigned)GetVersion();
        memset(buf, 0, sizeof(*buf));
        xsnprintf(buf->sysname, sizeof(buf->sysname), "Windows");
        xsnprintf(buf->release, sizeof(buf->release),
index 3fcca618d252d999c69de2bb9372b5d7a4478014..76170ad06d1e8a591af209b91c622d3073c691dd 100644 (file)
@@ -521,10 +521,33 @@ AC_CHECK_LIB([curl], [curl_global_init],
 [NO_CURL=],
 [NO_CURL=YesPlease])
 
+if test -z "${NO_CURL}" && test -z "${NO_OPENSSL}"; then
+
+AC_CHECK_LIB([curl], [Curl_ssl_init],
+[NEEDS_SSL_WITH_CURL=YesPlease],
+[NEEDS_SSL_WITH_CURL=])
+
+GIT_CONF_SUBST([NEEDS_SSL_WITH_CURL])
+
+fi
+
 GIT_UNSTASH_FLAGS($CURLDIR)
 
 GIT_CONF_SUBST([NO_CURL])
 
+if test -z "$NO_CURL"; then
+
+AC_CHECK_PROG([CURL_CONFIG], [curl-config],
+[curl-config],
+[no])
+
+if test $CURL_CONFIG != no; then
+    GIT_CONF_SUBST([CURL_CONFIG])
+fi
+
+fi
+
+
 #
 # Define NO_EXPAT if you do not have expat installed.  git-http-push is
 # not built, and you cannot push using http:// and https:// transports.
index eef6fce4c72ab37d75a0f75129666009a6a1ee54..82715aa8b8c050591559a1c4f9f92b2e99602212 100644 (file)
@@ -2,7 +2,6 @@
 #include "tempfile.h"
 #include "credential.h"
 #include "unix-socket.h"
-#include "sigchain.h"
 #include "parse-options.h"
 
 static struct tempfile socket_file;
index 8689a1519a5635a1d92e9e4106936b4159d2e106..f4afdc6988c32cd95769dee488f8495b8a6ce5b6 100644 (file)
@@ -88,7 +88,7 @@ int main(int argc, const char **argv)
        int timeout = 900;
        const char *op;
        const char * const usage[] = {
-               "git credential-cache [options] <action>",
+               "git credential-cache [<options>] <action>",
                NULL
        };
        struct option options[] = {
diff --git a/diff.c b/diff.c
index 835a12e84d65b3fa9d680d55b452f7d748a340b1..80eb0c2156a0f3727f8a9598f3e83233224539b0 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -13,7 +13,6 @@
 #include "run-command.h"
 #include "utf8.h"
 #include "userdiff.h"
-#include "sigchain.h"
 #include "submodule-config.h"
 #include "submodule.h"
 #include "ll-merge.h"
diff --git a/dir.c b/dir.c
index 109ceeaed3fb899c0e98a7abfc2f38a6cfa7fe0e..d2a8f06b0243bda92c04f57d96c0b18838ee7dce 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -1279,29 +1279,15 @@ enum exist_status {
  */
 static enum exist_status directory_exists_in_index_icase(const char *dirname, int len)
 {
-       const struct cache_entry *ce = cache_dir_exists(dirname, len);
-       unsigned char endchar;
+       struct cache_entry *ce;
 
-       if (!ce)
-               return index_nonexistent;
-       endchar = ce->name[len];
-
-       /*
-        * The cache_entry structure returned will contain this dirname
-        * and possibly additional path components.
-        */
-       if (endchar == '/')
+       if (cache_dir_exists(dirname, len))
                return index_directory;
 
-       /*
-        * If there are no additional path components, then this cache_entry
-        * represents a submodule.  Submodules, despite being directories,
-        * are stored in the cache without a closing slash.
-        */
-       if (!endchar && S_ISGITLINK(ce->ce_mode))
+       ce = cache_file_exists(dirname, len, ignore_case);
+       if (ce && S_ISGITLINK(ce->ce_mode))
                return index_gitdir;
 
-       /* This should never be hit, but it exists just in case. */
        return index_nonexistent;
 }
 
index c5b65f5e231e02f315d8a24ee48034aede3ad5e8..2da7fe2e06ff38b977209d44b7404c93358430af 100644 (file)
@@ -26,6 +26,7 @@ int warn_ambiguous_refs = 1;
 int warn_on_object_refname_ambiguity = 1;
 int ref_paranoia = -1;
 int repository_format_version;
+int repository_format_precious_objects;
 const char *git_commit_encoding;
 const char *git_log_output_encoding;
 int shared_repository = PERM_UMASK;
index daa60c60d6703ae76fd5cd168907f598207844d3..212ef2be9670bc2fe5573eee856471113eb96889 100755 (executable)
--- a/git-p4.py
+++ b/git-p4.py
@@ -24,6 +24,7 @@
 import stat
 import zipfile
 import zlib
+import ctypes
 
 try:
     from subprocess import CalledProcessError
index 2028b554f48798c87852cdb3c5ab6b558ad6722e..fcc606eab7353e67f82de873c3532cefc137a9e5 100755 (executable)
@@ -12452,8 +12452,8 @@ if {$cmdline_files ne {} || $revtreeargs ne {} || $revtreeargscmd ne {}} {
     set viewchanged(1) 0
     set vdatemode(1) 0
     addviewmenu 1
-    .bar.view entryconf [mca "Edit view..."] -state normal
-    .bar.view entryconf [mca "Delete view"] -state normal
+    .bar.view entryconf [mca "&Edit view..."] -state normal
+    .bar.view entryconf [mca "&Delete view"] -state normal
 }
 
 if {[info exists permviews]} {
index 61073ebf6b56824ec5ade2bf43a6a31a17e5b96e..909a56463fe6ddb35427301624ba648950070787 100644 (file)
@@ -89,71 +89,71 @@ msgid "Cancel"
 msgstr "Отказ"
 
 #: gitk:2069
-msgid "Update"
+msgid "&Update"
 msgstr "Обновяване"
 
 #: gitk:2070
-msgid "Reload"
+msgid "&Reload"
 msgstr "Презареждане"
 
 #: gitk:2071
-msgid "Reread references"
+msgid "Reread re&ferences"
 msgstr "Наново прочитане на настройките"
 
 #: gitk:2072
-msgid "List references"
+msgid "&List references"
 msgstr "Изброяване на указателите"
 
 #: gitk:2074
-msgid "Start git gui"
+msgid "Start git &gui"
 msgstr "Стартиране на „git gui“"
 
 #: gitk:2076
-msgid "Quit"
+msgid "&Quit"
 msgstr "Спиране на програмата"
 
 #: gitk:2068
-msgid "File"
+msgid "&File"
 msgstr "Файл"
 
 #: gitk:2080
-msgid "Preferences"
+msgid "&Preferences"
 msgstr "Настройки"
 
 #: gitk:2079
-msgid "Edit"
+msgid "&Edit"
 msgstr "Редактиране"
 
 #: gitk:2084
-msgid "New view..."
+msgid "&New view..."
 msgstr "Нов изглед…"
 
 #: gitk:2085
-msgid "Edit view..."
+msgid "&Edit view..."
 msgstr "Редактиране на изгледа…"
 
 #: gitk:2086
-msgid "Delete view"
+msgid "&Delete view"
 msgstr "Изтриване на изгледа"
 
 #: gitk:2088 gitk:4043
-msgid "All files"
+msgid "&All files"
 msgstr "Всички файлове"
 
 #: gitk:2083 gitk:4067
-msgid "View"
+msgid "&View"
 msgstr "Изглед"
 
 #: gitk:2093 gitk:2103 gitk:3012
-msgid "About gitk"
+msgid "&About gitk"
 msgstr "Относно gitk"
 
 #: gitk:2094 gitk:2108
-msgid "Key bindings"
+msgid "&Key bindings"
 msgstr "Клавишни комбинации"
 
 #: gitk:2092 gitk:2107
-msgid "Help"
+msgid "&Help"
 msgstr "Помощ"
 
 #: gitk:2185 gitk:8652
index 976037a645041e9aad0be5ee1350d1c8af783835..5ad066f7ce84c4be7801a467498ee3de3abccbf9 100644 (file)
@@ -9,7 +9,7 @@ msgstr ""
 "Project-Id-Version: gitk\n"
 "Report-Msgid-Bugs-To: \n"
 "POT-Creation-Date: 2015-05-17 14:32+1000\n"
-"PO-Revision-Date: 2015-02-01 22:49-0700\n"
+"PO-Revision-Date: 2015-10-05 22:23-0600\n"
 "Last-Translator: Alex Henrie <alexhenrie24@gmail.com>\n"
 "Language-Team: Catalan\n"
 "Language: ca\n"
@@ -17,7 +17,7 @@ msgstr ""
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
-"X-Generator: Poedit 1.7.3\n"
+"X-Generator: Poedit 1.8.5\n"
 
 #: gitk:140
 msgid "Couldn't get list of unmerged files:"
@@ -91,71 +91,71 @@ msgid "Cancel"
 msgstr "Cancel·la"
 
 #: gitk:2069
-msgid "Update"
+msgid "&Update"
 msgstr "Actualitza"
 
 #: gitk:2070
-msgid "Reload"
+msgid "&Reload"
 msgstr "Recarrega"
 
 #: gitk:2071
-msgid "Reread references"
+msgid "Reread re&ferences"
 msgstr "Rellegeix les referències"
 
 #: gitk:2072
-msgid "List references"
+msgid "&List references"
 msgstr "Llista les referències"
 
 #: gitk:2074
-msgid "Start git gui"
+msgid "Start git &gui"
 msgstr "Inicia el git gui"
 
 #: gitk:2076
-msgid "Quit"
+msgid "&Quit"
 msgstr "Surt"
 
 #: gitk:2068
-msgid "File"
+msgid "&File"
 msgstr "Fitxer"
 
 #: gitk:2080
-msgid "Preferences"
+msgid "&Preferences"
 msgstr "Preferències"
 
 #: gitk:2079
-msgid "Edit"
+msgid "&Edit"
 msgstr "Edita"
 
 #: gitk:2084
-msgid "New view..."
+msgid "&New view..."
 msgstr "Vista nova..."
 
 #: gitk:2085
-msgid "Edit view..."
+msgid "&Edit view..."
 msgstr "Edita la vista..."
 
 #: gitk:2086
-msgid "Delete view"
-msgstr "Suprimeix vista"
+msgid "&Delete view"
+msgstr "Suprimeix la vista"
 
 #: gitk:2088 gitk:4043
-msgid "All files"
+msgid "&All files"
 msgstr "Tots els fitxers"
 
 #: gitk:2083 gitk:4067
-msgid "View"
+msgid "&View"
 msgstr "Vista"
 
 #: gitk:2093 gitk:2103 gitk:3012
-msgid "About gitk"
+msgid "&About gitk"
 msgstr "Quant al gitk"
 
 #: gitk:2094 gitk:2108
-msgid "Key bindings"
+msgid "&Key bindings"
 msgstr "Associacions de tecles"
 
 #: gitk:2092 gitk:2107
-msgid "Help"
+msgid "&Help"
 msgstr "Ajuda"
 
 #: gitk:2185 gitk:8652
@@ -330,7 +330,7 @@ msgstr "Elimina aquesta branca"
 
 #: gitk:2649
 msgid "Copy branch name"
-msgstr ""
+msgstr "Copia el nom de branca"
 
 #: gitk:2656
 msgid "Highlight this too"
@@ -350,7 +350,7 @@ msgstr "Culpabilitat de la comissió mare"
 
 #: gitk:2660
 msgid "Copy path"
-msgstr ""
+msgstr "Copia el camí"
 
 #: gitk:2667
 msgid "Show origin of this line"
@@ -408,11 +408,11 @@ msgstr "<Fi>\t\tVés a l'última comissió"
 
 #: gitk:3052
 msgid "<Up>, p, k\tMove up one commit"
-msgstr "<Amunt>, p, k\tMou-te una comissió amunt"
+msgstr "<Amunt>, p, k\tMou-te cap amunt per una comissió"
 
 #: gitk:3053
 msgid "<Down>, n, j\tMove down one commit"
-msgstr "<Avall>, n, j\tMou-te una comissió avall"
+msgstr "<Avall>, n, j\tMou-te cap avall per una comissió"
 
 #: gitk:3054
 msgid "<Left>, z, h\tGo back in history list"
@@ -430,11 +430,11 @@ msgstr ""
 
 #: gitk:3057
 msgid "<PageUp>\tMove up one page in commit list"
-msgstr "<RePàg>\tBaixa una pàgina en la llista de comissions"
+msgstr "<RePàg>\tMou-te cap amunt per una pàgina en la llista de comissions"
 
 #: gitk:3058
 msgid "<PageDown>\tMove down one page in commit list"
-msgstr "<AvPàg>\tBaixa per una pàgina en la llista de comissions"
+msgstr "<AvPàg>\tMou-te cap avall per una pàgina en la llista de comissions"
 
 #: gitk:3059
 #, tcl-format
@@ -449,50 +449,50 @@ msgstr "<%s-Fi>\tDesplaça't a la part inferior de la llista de comissions"
 #: gitk:3061
 #, tcl-format
 msgid "<%s-Up>\tScroll commit list up one line"
-msgstr "<%s-Amunt>\tDesplaça la llista de comissions una línia cap amunt"
+msgstr "<%s-Amunt>\tDesplaça la llista de comissions cap amunt per una línia"
 
 #: gitk:3062
 #, tcl-format
 msgid "<%s-Down>\tScroll commit list down one line"
-msgstr "<%s-Avall>\tDesplaça la llista de comissions una línia cap avall"
+msgstr "<%s-Avall>\tDesplaça la llista de comissions cap avall per una línia"
 
 #: gitk:3063
 #, tcl-format
 msgid "<%s-PageUp>\tScroll commit list up one page"
-msgstr "<%s-RePàg>\tDesplaça la llista de comissions amunt per una pàgina"
+msgstr "<%s-RePàg>\tDesplaça la llista de comissions cap amunt per una pàgina"
 
 #: gitk:3064
 #, tcl-format
 msgid "<%s-PageDown>\tScroll commit list down one page"
-msgstr "<%s-AvPàg>\tDesplaça la llista de comissions una pàgina cap avall"
+msgstr "<%s-AvPàg>\tDesplaça la llista de comissions cap avall per una pàgina"
 
 #: gitk:3065
 msgid "<Shift-Up>\tFind backwards (upwards, later commits)"
-msgstr "<Maj-Amunt>\tCerca cap enrere (amunt, les comissions més noves)"
+msgstr "<Maj-Amunt>\tCerca cap enrere (cap amunt, les comissions més noves)"
 
 #: gitk:3066
 msgid "<Shift-Down>\tFind forwards (downwards, earlier commits)"
-msgstr "<Maj-Avall>\tCerca cap endavant (avall, les comissions més velles)"
+msgstr "<Maj-Avall>\tCerca cap endavant (cap avall, les comissions més velles)"
 
 #: gitk:3067
 msgid "<Delete>, b\tScroll diff view up one page"
-msgstr "<Supr>, b\tDesplaça la vista de diferència una pàgina cap amunt"
+msgstr "<Supr>, b\tDesplaça la vista de diferència cap amunt per una pàgina"
 
 #: gitk:3068
 msgid "<Backspace>\tScroll diff view up one page"
-msgstr "<Retrocés>\tDesplaça la vista de diferència una pàgina cap amunt"
+msgstr "<Retrocés>\tDesplaça la vista de diferència cap amunt per una pàgina"
 
 #: gitk:3069
 msgid "<Space>\t\tScroll diff view down one page"
-msgstr "<Espai>\t\tDesplaça la vista de diferència una pàgina cap avall"
+msgstr "<Espai>\t\tDesplaça la vista de diferència cap avall per una pàgina"
 
 #: gitk:3070
 msgid "u\t\tScroll diff view up 18 lines"
-msgstr "u\t\tDesplaça la vista de diferència 18 línies cap amunt"
+msgstr "u\t\tDesplaça la vista de diferència cap amunt per 18 línies"
 
 #: gitk:3071
 msgid "d\t\tScroll diff view down 18 lines"
-msgstr "d\t\tDesplaça la vista de diferència 18 línies cap avall "
+msgstr "d\t\tDesplaça la vista de diferència cap avall per 18 línies"
 
 #: gitk:3072
 #, tcl-format
@@ -509,9 +509,8 @@ msgid "<Return>\tMove to next find hit"
 msgstr "<Retorn>\tMou-te a la propera coincidència de la cerca"
 
 #: gitk:3075
-#, fuzzy
 msgid "g\t\tGo to commit"
-msgstr "<Fi>\t\tVés a l'última comissió"
+msgstr "g\t\tVés a l'última comissió"
 
 #: gitk:3076
 msgid "/\t\tFocus the search box"
@@ -668,9 +667,8 @@ msgid "Matches all Commit Info criteria"
 msgstr "Coincideix amb tots els criteris d'informació de comissió"
 
 #: gitk:4086
-#, fuzzy
 msgid "Matches no Commit Info criteria"
-msgstr "Coincideix amb tots els criteris d'informació de comissió"
+msgstr "No coincideix amb cap criteri d'informació de comissió"
 
 #: gitk:4087
 msgid "Changes to Files:"
@@ -1310,7 +1308,7 @@ msgstr "fons de la línia marcada"
 
 #: gitk:11450
 msgid "Select bg"
-msgstr "fons de la selecció"
+msgstr "Fons de la selecció"
 
 #: gitk:11459
 msgid "Fonts: press to choose"
@@ -1354,6 +1352,8 @@ msgid ""
 "Sorry, gitk cannot run with this version of Tcl/Tk.\n"
 " Gitk requires at least Tcl/Tk 8.4."
 msgstr ""
+"Perdó, el gitk no pot executar-se amb aquesta versió de Tcl/Tk.\n"
+" El Gitk requereix com a mínim el Tcl/Tk 8.4."
 
 #: gitk:12269
 msgid "Cannot find a git repository here."
@@ -1367,6 +1367,3 @@ msgstr "Paràmetre ambigu '%s': és tant revisió com nom de fitxer"
 #: gitk:12328
 msgid "Bad arguments to gitk:"
 msgstr "Paràmetres dolents al gitk:"
-
-#~ msgid "mc"
-#~ msgstr "mc"
index 1a3264b2b0020a0912da8497259ce9485288589d..d9ba4052e20bc8dddc091e3a60103d33af349a63 100644 (file)
@@ -9,7 +9,7 @@ msgstr ""
 "Project-Id-Version: git-gui\n"
 "Report-Msgid-Bugs-To: \n"
 "POT-Creation-Date: 2015-05-17 14:32+1000\n"
-"PO-Revision-Date: 2010-01-27 20:27+0100\n"
+"PO-Revision-Date: 2015-10-20 14:20+0200\n"
 "Last-Translator: Christian Stimming <stimming@tuhh.de>\n"
 "Language-Team: German\n"
 "Language: \n"
@@ -89,72 +89,72 @@ msgid "Cancel"
 msgstr "Abbrechen"
 
 #: gitk:2069
-msgid "Update"
-msgstr "Aktualisieren"
+msgid "&Update"
+msgstr "&Aktualisieren"
 
 #: gitk:2070
-msgid "Reload"
-msgstr "Neu laden"
+msgid "&Reload"
+msgstr "&Neu laden"
 
 #: gitk:2071
-msgid "Reread references"
-msgstr "Zweige neu laden"
+msgid "Reread re&ferences"
+msgstr "&Zweige neu laden"
 
 #: gitk:2072
-msgid "List references"
-msgstr "Zweige/Markierungen auflisten"
+msgid "&List references"
+msgstr "Zweige/Markierungen auf&listen"
 
 #: gitk:2074
-msgid "Start git gui"
-msgstr "»git gui« starten"
+msgid "Start git &gui"
+msgstr "»git &gui« starten"
 
 #: gitk:2076
-msgid "Quit"
-msgstr "Beenden"
+msgid "&Quit"
+msgstr "&Beenden"
 
 #: gitk:2068
-msgid "File"
-msgstr "Datei"
+msgid "&File"
+msgstr "&Datei"
 
 #: gitk:2080
-msgid "Preferences"
-msgstr "Einstellungen"
+msgid "&Preferences"
+msgstr "&Einstellungen"
 
 #: gitk:2079
-msgid "Edit"
-msgstr "Bearbeiten"
+msgid "&Edit"
+msgstr "&Bearbeiten"
 
 #: gitk:2084
-msgid "New view..."
-msgstr "Neue Ansicht ..."
+msgid "&New view..."
+msgstr "&Neue Ansicht ..."
 
 #: gitk:2085
-msgid "Edit view..."
-msgstr "Ansicht bearbeiten ..."
+msgid "&Edit view..."
+msgstr "Ansicht &bearbeiten ..."
 
 #: gitk:2086
-msgid "Delete view"
-msgstr "Ansicht entfernen"
+msgid "&Delete view"
+msgstr "Ansicht &entfernen"
 
 #: gitk:2088 gitk:4043
-msgid "All files"
-msgstr "Alle Dateien"
+msgid "&All files"
+msgstr "&Alle Dateien"
 
 #: gitk:2083 gitk:4067
-msgid "View"
-msgstr "Ansicht"
+msgid "&View"
+msgstr "&Ansicht"
 
 #: gitk:2093 gitk:2103 gitk:3012
-msgid "About gitk"
-msgstr "Über gitk"
+msgid "&About gitk"
+msgstr "Über &gitk"
 
 #: gitk:2094 gitk:2108
-msgid "Key bindings"
-msgstr "Tastenkürzel"
+msgid "&Key bindings"
+msgstr "&Tastenkürzel"
 
 #: gitk:2092 gitk:2107
-msgid "Help"
-msgstr "Hilfe"
+msgid "&Help"
+msgstr "&Hilfe"
 
 #: gitk:2185 gitk:8652
 msgid "SHA1 ID:"
index 6402a411a6e866595617de1ad3641105b5067a42..ddcb0a5f68dc27127d72f2a42f6d77d2bbf7c23f 100644 (file)
@@ -91,71 +91,71 @@ msgid "Cancel"
 msgstr "Cancelar"
 
 #: gitk:2069
-msgid "Update"
+msgid "&Update"
 msgstr "Actualizar"
 
 #: gitk:2070
-msgid "Reload"
+msgid "&Reload"
 msgstr ""
 
 #: gitk:2071
-msgid "Reread references"
+msgid "Reread re&ferences"
 msgstr "Releer referencias"
 
 #: gitk:2072
-msgid "List references"
+msgid "&List references"
 msgstr "Lista de referencias"
 
 #: gitk:2074
-msgid "Start git gui"
+msgid "Start git &gui"
 msgstr ""
 
 #: gitk:2076
-msgid "Quit"
+msgid "&Quit"
 msgstr "Salir"
 
 #: gitk:2068
-msgid "File"
+msgid "&File"
 msgstr "Archivo"
 
 #: gitk:2080
-msgid "Preferences"
+msgid "&Preferences"
 msgstr "Preferencias"
 
 #: gitk:2079
-msgid "Edit"
+msgid "&Edit"
 msgstr "Editar"
 
 #: gitk:2084
-msgid "New view..."
+msgid "&New view..."
 msgstr "Nueva vista..."
 
 #: gitk:2085
-msgid "Edit view..."
+msgid "&Edit view..."
 msgstr "Modificar vista..."
 
 #: gitk:2086
-msgid "Delete view"
+msgid "&Delete view"
 msgstr "Eliminar vista"
 
 #: gitk:2088 gitk:4043
-msgid "All files"
+msgid "&All files"
 msgstr "Todos los archivos"
 
 #: gitk:2083 gitk:4067
-msgid "View"
+msgid "&View"
 msgstr "Vista"
 
 #: gitk:2093 gitk:2103 gitk:3012
-msgid "About gitk"
+msgid "&About gitk"
 msgstr "Acerca de gitk"
 
 #: gitk:2094 gitk:2108
-msgid "Key bindings"
+msgid "&Key bindings"
 msgstr "Combinaciones de teclas"
 
 #: gitk:2092 gitk:2107
-msgid "Help"
+msgid "&Help"
 msgstr "Ayuda"
 
 #: gitk:2185 gitk:8652
index 6b1f05c6b9061e4cf2a98664b00f927a58ff0cea..80f72fb4535a2cfb4dc33b9f12fd002beb3f5d86 100644 (file)
@@ -93,71 +93,71 @@ msgid "Cancel"
 msgstr "Annuler"
 
 #: gitk:2069
-msgid "Update"
+msgid "&Update"
 msgstr "Mise à jour"
 
 #: gitk:2070
-msgid "Reload"
+msgid "&Reload"
 msgstr "Recharger"
 
 #: gitk:2071
-msgid "Reread references"
+msgid "Reread re&ferences"
 msgstr "Relire les références"
 
 #: gitk:2072
-msgid "List references"
+msgid "&List references"
 msgstr "Lister les références"
 
 #: gitk:2074
-msgid "Start git gui"
+msgid "Start git &gui"
 msgstr "Démarrer git gui"
 
 #: gitk:2076
-msgid "Quit"
+msgid "&Quit"
 msgstr "Quitter"
 
 #: gitk:2068
-msgid "File"
+msgid "&File"
 msgstr "Fichier"
 
 #: gitk:2080
-msgid "Preferences"
+msgid "&Preferences"
 msgstr "Préférences"
 
 #: gitk:2079
-msgid "Edit"
+msgid "&Edit"
 msgstr "Éditer"
 
 #: gitk:2084
-msgid "New view..."
+msgid "&New view..."
 msgstr "Nouvelle vue..."
 
 #: gitk:2085
-msgid "Edit view..."
+msgid "&Edit view..."
 msgstr "Éditer la vue..."
 
 #: gitk:2086
-msgid "Delete view"
+msgid "&Delete view"
 msgstr "Supprimer la vue"
 
 #: gitk:2088 gitk:4043
-msgid "All files"
+msgid "&All files"
 msgstr "Tous les fichiers"
 
 #: gitk:2083 gitk:4067
-msgid "View"
+msgid "&View"
 msgstr "Vue"
 
 #: gitk:2093 gitk:2103 gitk:3012
-msgid "About gitk"
+msgid "&About gitk"
 msgstr "À propos de gitk"
 
 #: gitk:2094 gitk:2108
-msgid "Key bindings"
+msgid "&Key bindings"
 msgstr "Raccourcis clavier"
 
 #: gitk:2092 gitk:2107
-msgid "Help"
+msgid "&Help"
 msgstr "Aide"
 
 #: gitk:2185 gitk:8652
index 00023f1cab4103f76d10438eeb793e7f6c1fd9b7..66fd75ba5b1634f13f067e4aab3cc1d921ca804b 100644 (file)
@@ -88,71 +88,71 @@ msgid "Cancel"
 msgstr "Visszavonás"
 
 #: gitk:2069
-msgid "Update"
+msgid "&Update"
 msgstr "Frissités"
 
 #: gitk:2070
-msgid "Reload"
+msgid "&Reload"
 msgstr "Újratöltés"
 
 #: gitk:2071
-msgid "Reread references"
+msgid "Reread re&ferences"
 msgstr "Referenciák újraolvasása"
 
 #: gitk:2072
-msgid "List references"
+msgid "&List references"
 msgstr "Referenciák listázása"
 
 #: gitk:2074
-msgid "Start git gui"
+msgid "Start git &gui"
 msgstr "Git gui indítása"
 
 #: gitk:2076
-msgid "Quit"
+msgid "&Quit"
 msgstr "Kilépés"
 
 #: gitk:2068
-msgid "File"
+msgid "&File"
 msgstr "Fájl"
 
 #: gitk:2080
-msgid "Preferences"
+msgid "&Preferences"
 msgstr "Beállítások"
 
 #: gitk:2079
-msgid "Edit"
+msgid "&Edit"
 msgstr "Szerkesztés"
 
 #: gitk:2084
-msgid "New view..."
+msgid "&New view..."
 msgstr "Új nézet ..."
 
 #: gitk:2085
-msgid "Edit view..."
+msgid "&Edit view..."
 msgstr "Nézet szerkesztése ..."
 
 #: gitk:2086
-msgid "Delete view"
+msgid "&Delete view"
 msgstr "Nézet törlése"
 
 #: gitk:2088 gitk:4043
-msgid "All files"
+msgid "&All files"
 msgstr "Minden fájl"
 
 #: gitk:2083 gitk:4067
-msgid "View"
+msgid "&View"
 msgstr "Nézet"
 
 #: gitk:2093 gitk:2103 gitk:3012
-msgid "About gitk"
+msgid "&About gitk"
 msgstr "Gitk névjegy"
 
 #: gitk:2094 gitk:2108
-msgid "Key bindings"
+msgid "&Key bindings"
 msgstr "Billentyűkombináció"
 
 #: gitk:2092 gitk:2107
-msgid "Help"
+msgid "&Help"
 msgstr "Segítség"
 
 #: gitk:2185 gitk:8652
index b8212b17009b1909ff22f4f48f70518a7531597c..b5f002db7d52d3106d3a52dbb03047ad67233b62 100644 (file)
@@ -89,71 +89,71 @@ msgid "Cancel"
 msgstr "Annulla"
 
 #: gitk:2069
-msgid "Update"
+msgid "&Update"
 msgstr "Aggiorna"
 
 #: gitk:2070
-msgid "Reload"
+msgid "&Reload"
 msgstr "Ricarica"
 
 #: gitk:2071
-msgid "Reread references"
+msgid "Reread re&ferences"
 msgstr "Rileggi riferimenti"
 
 #: gitk:2072
-msgid "List references"
+msgid "&List references"
 msgstr "Elenca riferimenti"
 
 #: gitk:2074
-msgid "Start git gui"
+msgid "Start git &gui"
 msgstr "Avvia git gui"
 
 #: gitk:2076
-msgid "Quit"
+msgid "&Quit"
 msgstr "Esci"
 
 #: gitk:2068
-msgid "File"
-msgstr "File"
+msgid "&File"
+msgstr "&File"
 
 #: gitk:2080
-msgid "Preferences"
+msgid "&Preferences"
 msgstr "Preferenze"
 
 #: gitk:2079
-msgid "Edit"
+msgid "&Edit"
 msgstr "Modifica"
 
 #: gitk:2084
-msgid "New view..."
+msgid "&New view..."
 msgstr "Nuova vista..."
 
 #: gitk:2085
-msgid "Edit view..."
+msgid "&Edit view..."
 msgstr "Modifica vista..."
 
 #: gitk:2086
-msgid "Delete view"
+msgid "&Delete view"
 msgstr "Elimina vista"
 
 #: gitk:2088 gitk:4043
-msgid "All files"
+msgid "&All files"
 msgstr "Tutti i file"
 
 #: gitk:2083 gitk:4067
-msgid "View"
+msgid "&View"
 msgstr "Vista"
 
 #: gitk:2093 gitk:2103 gitk:3012
-msgid "About gitk"
+msgid "&About gitk"
 msgstr "Informazioni su gitk"
 
 #: gitk:2094 gitk:2108
-msgid "Key bindings"
+msgid "&Key bindings"
 msgstr "Scorciatoie da tastiera"
 
 #: gitk:2092 gitk:2107
-msgid "Help"
+msgid "&Help"
 msgstr "Aiuto"
 
 #: gitk:2185 gitk:8652
index 8bbc67f6b58ab8a850f4950f834dbc8448ae22d3..59e42a89fd7e85dccc5acadbc1ad5bc4c8cd5e8f 100644 (file)
@@ -90,72 +90,72 @@ msgid "Cancel"
 msgstr "キャンセル"
 
 #: gitk:2069
-msgid "Update"
-msgstr "更新"
+msgid "&Update"
+msgstr "更新(&U)"
 
 #: gitk:2070
-msgid "Reload"
-msgstr "リロード"
+msgid "&Reload"
+msgstr "リロード(&R)"
 
 #: gitk:2071
-msgid "Reread references"
-msgstr "リファレンスを再読み込み"
+msgid "Reread re&ferences"
+msgstr "リファレンスを再読み込み(&F)"
 
 #: gitk:2072
-msgid "List references"
-msgstr "リファレンスリストを表示"
+msgid "&List references"
+msgstr "リファレンスリストを表示(&L)"
 
 #: gitk:2074
-msgid "Start git gui"
-msgstr "git gui の開始"
+msgid "Start git &gui"
+msgstr "git gui の開始(&G)"
 
 #: gitk:2076
-msgid "Quit"
-msgstr "終了"
+msgid "&Quit"
+msgstr "終了(&Q)"
 
 #: gitk:2068
-msgid "File"
-msgstr "ファイル"
+msgid "&File"
+msgstr "ファイル(&F)"
 
 #: gitk:2080
-msgid "Preferences"
-msgstr "設定"
+msgid "&Preferences"
+msgstr "設定(&P)"
 
 #: gitk:2079
-msgid "Edit"
-msgstr "編集"
+msgid "&Edit"
+msgstr "編集(&E)"
 
 #: gitk:2084
-msgid "New view..."
-msgstr "新規ビュー..."
+msgid "&New view..."
+msgstr "新規ビュー...(&N)"
 
 #: gitk:2085
-msgid "Edit view..."
-msgstr "ビュー編集..."
+msgid "&Edit view..."
+msgstr "ビュー編集...(&E)"
 
 #: gitk:2086
-msgid "Delete view"
-msgstr "ビュー削除"
+msgid "&Delete view"
+msgstr "ビュー削除(&D)"
 
 #: gitk:2088 gitk:4043
-msgid "All files"
-msgstr "全てのファイル"
+msgid "&All files"
+msgstr "全てのファイル(&A)"
 
 #: gitk:2083 gitk:4067
-msgid "View"
-msgstr "ビュー"
+msgid "&View"
+msgstr "ビュー(&V)"
 
 #: gitk:2093 gitk:2103 gitk:3012
-msgid "About gitk"
-msgstr "gitk について"
+msgid "&About gitk"
+msgstr "gitk について(&A)"
 
 #: gitk:2094 gitk:2108
-msgid "Key bindings"
-msgstr "キーバインディング"
+msgid "&Key bindings"
+msgstr "キーバインディング(&K)"
 
 #: gitk:2092 gitk:2107
-msgid "Help"
-msgstr "ヘルプ"
+msgid "&Help"
+msgstr "ヘルプ(&H)"
 
 #: gitk:2185 gitk:8652
 msgid "SHA1 ID:"
index 07e5d63b65cced4233d0a69f408b45d6ad072c6a..3f78f1b7482614098e295f3f1ffb6c0925ae6383 100644 (file)
@@ -90,71 +90,71 @@ msgid "Cancel"
 msgstr "Cancelar"
 
 #: gitk:2069
-msgid "Update"
+msgid "&Update"
 msgstr "Atualizar"
 
 #: gitk:2070
-msgid "Reload"
+msgid "&Reload"
 msgstr "Recarregar"
 
 #: gitk:2071
-msgid "Reread references"
+msgid "Reread re&ferences"
 msgstr "Ler as referências novamente"
 
 #: gitk:2072
-msgid "List references"
+msgid "&List references"
 msgstr "Listar referências"
 
 #: gitk:2074
-msgid "Start git gui"
+msgid "Start git &gui"
 msgstr "Iniciar Git GUI"
 
 #: gitk:2076
-msgid "Quit"
+msgid "&Quit"
 msgstr "Sair"
 
 #: gitk:2068
-msgid "File"
+msgid "&File"
 msgstr "Arquivo"
 
 #: gitk:2080
-msgid "Preferences"
+msgid "&Preferences"
 msgstr "Preferências"
 
 #: gitk:2079
-msgid "Edit"
+msgid "&Edit"
 msgstr "Editar"
 
 #: gitk:2084
-msgid "New view..."
+msgid "&New view..."
 msgstr "Nova vista..."
 
 #: gitk:2085
-msgid "Edit view..."
+msgid "&Edit view..."
 msgstr "Editar vista..."
 
 #: gitk:2086
-msgid "Delete view"
+msgid "&Delete view"
 msgstr "Apagar vista"
 
 #: gitk:2088 gitk:4043
-msgid "All files"
+msgid "&All files"
 msgstr "Todos os arquivos"
 
 #: gitk:2083 gitk:4067
-msgid "View"
+msgid "&View"
 msgstr "Exibir"
 
 #: gitk:2093 gitk:2103 gitk:3012
-msgid "About gitk"
+msgid "&About gitk"
 msgstr "Sobre o gitk"
 
 #: gitk:2094 gitk:2108
-msgid "Key bindings"
+msgid "&Key bindings"
 msgstr "Atalhos de teclado"
 
 #: gitk:2092 gitk:2107
-msgid "Help"
+msgid "&Help"
 msgstr "Ajuda"
 
 #: gitk:2185 gitk:8652
index f1bac879e3a6dcd05c651d23223dccd126daaa09..17ed026aa7da7c636457c302b2c28f7b866ae45a 100644 (file)
@@ -1,18 +1,24 @@
-#
 # Translation of gitk to Russian.
 #
+# Translators:
+# 0xAX <kuleshovmail@gmail.com>, 2014
+# Alex Riesen <raa.lkml@gmail.com>, 2015
+# Dimitriy Ryazantcev <DJm00n@mail.ru>, 2015
+# Dmitry Potapov <dpotapov@gmail.com>, 2009
+# Skip <bsvskip@rambler.ru>, 2011
 msgid ""
 msgstr ""
-"Project-Id-Version: gitk\n"
+"Project-Id-Version: Git Russian Localization Project\n"
 "Report-Msgid-Bugs-To: \n"
 "POT-Creation-Date: 2015-05-17 14:32+1000\n"
-"PO-Revision-Date: 2009-04-24 16:00+0200\n"
-"Last-Translator: Alex Riesen <raa.lkml@gmail.com>\n"
-"Language-Team: Russian\n"
-"Language: \n"
+"PO-Revision-Date: 2015-10-12 10:14+0000\n"
+"Last-Translator: Dimitriy Ryazantcev <DJm00n@mail.ru>\n"
+"Language-Team: Russian (http://www.transifex.com/djm00n/git-po-ru/language/ru/)\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
+"Language: ru\n"
+"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n"
 
 #: gitk:140
 msgid "Couldn't get list of unmerged files:"
@@ -20,15 +26,15 @@ msgstr "Невозможно получить список файлов неза
 
 #: gitk:212 gitk:2381
 msgid "Color words"
-msgstr ""
+msgstr "Цветные слова"
 
 #: gitk:217 gitk:2381 gitk:8220 gitk:8253
 msgid "Markup words"
-msgstr ""
+msgstr "Помеченые слова"
 
 #: gitk:324
 msgid "Error parsing revisions:"
-msgstr "Ð\9eÑ\88ибка Ð² Ð¸Ð´ÐµÐ½Ñ\82иÑ\84икаÑ\82оÑ\80е Ð²ÐµÑ\80Ñ\81ии:"
+msgstr "Ð\9eÑ\88ибка Ð¿Ñ\80и Ñ\80азбоÑ\80е Ñ\80едакÑ\86ии:"
 
 #: gitk:380
 msgid "Error executing --argscmd command:"
@@ -36,17 +42,13 @@ msgstr "Ошибка выполнения команды заданной --args
 
 #: gitk:393
 msgid "No files selected: --merge specified but no files are unmerged."
-msgstr ""
-"Файлы не выбраны: указан --merge, но не было найдено ни одного файла где эта "
-"операция должна быть завершена."
+msgstr "Файлы не выбраны: указан --merge, но не было найдено ни одного файла где эта операция должна быть завершена."
 
 #: gitk:396
 msgid ""
 "No files selected: --merge specified but no unmerged files are within file "
 "limit."
-msgstr ""
-"Файлы не выбраны: указан --merge, но в рамках указанного ограничения на "
-"имена файлов нет ни одного где эта операция должна быть завершена."
+msgstr "Файлы не выбраны: указан --merge, но в рамках указанного ограничения на имена файлов нет ни одного где эта операция должна быть завершена."
 
 #: gitk:418 gitk:566
 msgid "Error executing git log:"
@@ -58,7 +60,7 @@ msgstr "Чтение"
 
 #: gitk:496 gitk:4525
 msgid "Reading commits..."
-msgstr "ЧÑ\82ение Ð²ÐµÑ\80Ñ\81ий..."
+msgstr "ЧÑ\82ение ÐºÐ¾Ð¼Ð¼Ð¸Ñ\82ов..."
 
 #: gitk:499 gitk:1637 gitk:4528
 msgid "No commits selected"
@@ -74,7 +76,7 @@ msgstr "Ошибка обработки вывода команды git log:"
 
 #: gitk:1740
 msgid "No commit information available"
-msgstr "Нет информации о состоянии"
+msgstr "Нет информации о коммите"
 
 #: gitk:1903 gitk:1932 gitk:4315 gitk:9669 gitk:11241 gitk:11521
 msgid "OK"
@@ -86,71 +88,71 @@ msgid "Cancel"
 msgstr "Отмена"
 
 #: gitk:2069
-msgid "Update"
+msgid "&Update"
 msgstr "Обновить"
 
 #: gitk:2070
-msgid "Reload"
+msgid "&Reload"
 msgstr "Перечитать"
 
 #: gitk:2071
-msgid "Reread references"
+msgid "Reread re&ferences"
 msgstr "Обновить список ссылок"
 
 #: gitk:2072
-msgid "List references"
+msgid "&List references"
 msgstr "Список ссылок"
 
 #: gitk:2074
-msgid "Start git gui"
+msgid "Start git &gui"
 msgstr "Запустить git gui"
 
 #: gitk:2076
-msgid "Quit"
+msgid "&Quit"
 msgstr "Завершить"
 
 #: gitk:2068
-msgid "File"
+msgid "&File"
 msgstr "Файл"
 
 #: gitk:2080
-msgid "Preferences"
+msgid "&Preferences"
 msgstr "Настройки"
 
 #: gitk:2079
-msgid "Edit"
+msgid "&Edit"
 msgstr "Редактировать"
 
 #: gitk:2084
-msgid "New view..."
+msgid "&New view..."
 msgstr "Новое представление..."
 
 #: gitk:2085
-msgid "Edit view..."
+msgid "&Edit view..."
 msgstr "Редактировать представление..."
 
 #: gitk:2086
-msgid "Delete view"
+msgid "&Delete view"
 msgstr "Удалить представление"
 
 #: gitk:2088 gitk:4043
-msgid "All files"
+msgid "&All files"
 msgstr "Все файлы"
 
 #: gitk:2083 gitk:4067
-msgid "View"
+msgid "&View"
 msgstr "Представление"
 
 #: gitk:2093 gitk:2103 gitk:3012
-msgid "About gitk"
+msgid "&About gitk"
 msgstr "О gitk"
 
 #: gitk:2094 gitk:2108
-msgid "Key bindings"
+msgid "&Key bindings"
 msgstr "Назначения клавиатуры"
 
 #: gitk:2092 gitk:2107
-msgid "Help"
+msgid "&Help"
 msgstr "Подсказка"
 
 #: gitk:2185 gitk:8652
@@ -167,7 +169,7 @@ msgstr "Поиск"
 
 #: gitk:2295
 msgid "commit"
-msgstr "состояние"
+msgstr "коммит"
 
 #: gitk:2299 gitk:2301 gitk:4687 gitk:4710 gitk:4734 gitk:6755 gitk:6827
 #: gitk:6912
@@ -184,7 +186,7 @@ msgstr "добавив/удалив строку:"
 
 #: gitk:2304 gitk:4779
 msgid "changing lines matching:"
-msgstr ""
+msgstr "изменяя совпадающие строки:"
 
 #: gitk:2313 gitk:2315 gitk:4766
 msgid "Exact"
@@ -217,7 +219,7 @@ msgstr "Автор"
 
 #: gitk:2319 gitk:4871 gitk:6786 gitk:7326
 msgid "Committer"
-msgstr "СоÑ\85Ñ\80анивÑ\88ий Ñ\81оÑ\81Ñ\82оÑ\8fние"
+msgstr "Ð\9aоммиÑ\82еÑ\80"
 
 #: gitk:2350
 msgid "Search"
@@ -245,7 +247,7 @@ msgstr "Игнорировать пробелы"
 
 #: gitk:2378 gitk:2380 gitk:7959 gitk:8206
 msgid "Line diff"
-msgstr ""
+msgstr "Изменения строк"
 
 #: gitk:2445
 msgid "Patch"
@@ -257,11 +259,11 @@ msgstr "Файлы"
 
 #: gitk:2617 gitk:2637
 msgid "Diff this -> selected"
-msgstr "Сравнить это состояние с выделенным"
+msgstr "Сравнить этот коммит с выделенным"
 
 #: gitk:2618 gitk:2638
 msgid "Diff selected -> this"
-msgstr "Сравнить выделенное с этим состоянием"
+msgstr "Сравнить выделенный с этим коммитом"
 
 #: gitk:2619 gitk:2639
 msgid "Make patch"
@@ -273,63 +275,59 @@ msgstr "Создать метку"
 
 #: gitk:2621 gitk:9371
 msgid "Write commit to file"
-msgstr "СоÑ\85Ñ\80аниÑ\82Ñ\8c Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ\8f в файл"
+msgstr "СоÑ\85Ñ\80аниÑ\82Ñ\8c ÐºÐ¾Ð¼Ð¼Ð¸Ñ\82 в файл"
 
 #: gitk:2622 gitk:9428
 msgid "Create new branch"
-msgstr "СоздаÑ\82Ñ\8c Ð²ÐµÑ\82вÑ\8c"
+msgstr "СоздаÑ\82Ñ\8c Ð²ÐµÑ\82кÑ\83"
 
 #: gitk:2623
 msgid "Cherry-pick this commit"
-msgstr "СкопиÑ\80оваÑ\82Ñ\8c Ñ\8dÑ\82о Ñ\81оÑ\81Ñ\82оÑ\8fние"
+msgstr "Ð\9eÑ\82боÑ\80 Ð»Ñ\83Ñ\87Ñ\88его Ð´Ð»Ñ\8f Ñ\8dÑ\82ого ÐºÐ¾Ð¼Ð¼Ð¸Ñ\82а"
 
 #: gitk:2624
 msgid "Reset HEAD branch to here"
-msgstr "Установить HEAD на это состояние"
+msgstr "Установить HEAD на этот коммит"
 
 #: gitk:2625
-#, fuzzy
 msgid "Mark this commit"
-msgstr "СкопиÑ\80оваÑ\82Ñ\8c Ñ\8dÑ\82о Ñ\81оÑ\81Ñ\82оÑ\8fние"
+msgstr "Ð\9fомеÑ\82иÑ\82Ñ\8c Ñ\8dÑ\82оÑ\82 ÐºÐ¾Ð¼Ð¼Ð¸Ñ\82"
 
 #: gitk:2626
 msgid "Return to mark"
-msgstr ""
+msgstr "Вернуться на пометку"
 
 #: gitk:2627
 msgid "Find descendant of this and mark"
-msgstr ""
+msgstr "Найти и пометить потомка этого коммита"
 
 #: gitk:2628
 msgid "Compare with marked commit"
-msgstr ""
+msgstr "Сравнить с помеченным коммитом"
 
 #: gitk:2629 gitk:2640
-#, fuzzy
 msgid "Diff this -> marked commit"
-msgstr "Сравнить это состояние с выделенным"
+msgstr "Сравнить выделенное с помеченным коммитом"
 
 #: gitk:2630 gitk:2641
-#, fuzzy
 msgid "Diff marked commit -> this"
-msgstr "СÑ\80авниÑ\82Ñ\8c Ð²Ñ\8bделенное Ñ\81 Ñ\8dÑ\82им Ñ\81оÑ\81Ñ\82оÑ\8fнием"
+msgstr "СÑ\80авниÑ\82Ñ\8c Ð¿Ð¾Ð¼ÐµÑ\87еннÑ\8bй Ñ\81 Ñ\8dÑ\82им ÐºÐ¾Ð¼Ð¼Ð¸Ñ\82ом"
 
 #: gitk:2631
-#, fuzzy
 msgid "Revert this commit"
-msgstr "СкопиÑ\80оваÑ\82Ñ\8c Ñ\8dÑ\82о Ñ\81оÑ\81Ñ\82оÑ\8fние"
+msgstr "Ð\92озвÑ\80аÑ\82 Ñ\8dÑ\82ого ÐºÐ¾Ð¼Ð¼Ð¸Ñ\82а"
 
 #: gitk:2647
 msgid "Check out this branch"
-msgstr "Ð\9fеÑ\80ейÑ\82и Ð½Ð° Ñ\8dÑ\82Ñ\83 Ð²ÐµÑ\82вÑ\8c"
+msgstr "Ð\9fеÑ\80ейÑ\82и Ð½Ð° Ñ\8dÑ\82Ñ\83 Ð²ÐµÑ\82кÑ\83"
 
 #: gitk:2648
 msgid "Remove this branch"
-msgstr "УдалиÑ\82Ñ\8c Ñ\8dÑ\82Ñ\83 Ð²ÐµÑ\82вÑ\8c"
+msgstr "УдалиÑ\82Ñ\8c Ñ\8dÑ\82Ñ\83 Ð²ÐµÑ\82кÑ\83"
 
 #: gitk:2649
 msgid "Copy branch name"
-msgstr ""
+msgstr "Копировать имя ветки"
 
 #: gitk:2656
 msgid "Highlight this too"
@@ -345,11 +343,11 @@ msgstr "Программа сравнения"
 
 #: gitk:2659
 msgid "Blame parent commit"
-msgstr "Ð\90нноÑ\82иÑ\80оваÑ\82Ñ\8c Ñ\80одиÑ\82елÑ\8cÑ\81кое Ñ\81оÑ\81Ñ\82оÑ\8fние"
+msgstr "Ð\90вÑ\82оÑ\80Ñ\8b Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ð¹ Ñ\80одиÑ\82елÑ\8cÑ\81кого ÐºÐ¾Ð¼Ð¼Ð¸Ñ\82а"
 
 #: gitk:2660
 msgid "Copy path"
-msgstr ""
+msgstr "Копировать путь"
 
 #: gitk:2667
 msgid "Show origin of this line"
@@ -360,21 +358,14 @@ msgid "Run git gui blame on this line"
 msgstr "Запустить git gui blame для этой строки"
 
 #: gitk:3014
-#, fuzzy
 msgid ""
 "\n"
 "Gitk - a commit viewer for git\n"
 "\n"
-"Copyright © 2005-2014 Paul Mackerras\n"
+"Copyright  2005-2014 Paul Mackerras\n"
 "\n"
 "Use and redistribute under the terms of the GNU General Public License"
-msgstr ""
-"\n"
-"Gitk - программа просмотра истории репозиториев Git\n"
-"\n"
-"Copyright \\u00a9 2005-2010 Paul Mackerras\n"
-"\n"
-"Использование и распространение согласно условиям GNU General Public License"
+msgstr "\nGitk - программа просмотра истории репозиториев git\n\n©  2005-2014 Paul Mackerras\n\nИспользование и распространение согласно условиям GNU General Public License"
 
 #: gitk:3022 gitk:3089 gitk:9857
 msgid "Close"
@@ -394,87 +385,84 @@ msgid "<%s-Q>\t\tQuit"
 msgstr "<%s-Q>\t\tЗавершить"
 
 #: gitk:3049
-#, fuzzy, tcl-format
+#, tcl-format
 msgid "<%s-W>\t\tClose window"
-msgstr "<%s-F>\t\tПоиск"
+msgstr "<%s-W>\t\tЗакрыть окно"
 
 #: gitk:3050
 msgid "<Home>\t\tMove to first commit"
-msgstr "<Home>\t\tПерейти к первому состоянию"
+msgstr "<Home>\t\tПерейти к первому коммиту"
 
 #: gitk:3051
 msgid "<End>\t\tMove to last commit"
-msgstr "<End>\t\tПерейти к последнему состоянию"
+msgstr "<End>\t\tПерейти к последнему коммиту"
 
 #: gitk:3052
-#, fuzzy
 msgid "<Up>, p, k\tMove up one commit"
-msgstr "<Up>, p, i\tПерейти к следующему состоянию"
+msgstr "<Up>, p, k\tПерейти на один коммит вверх"
 
 #: gitk:3053
-#, fuzzy
 msgid "<Down>, n, j\tMove down one commit"
-msgstr "<Down>, n, k\tПерейти к предыдущему состоянию"
+msgstr "<Down>, n, j\tПерейти на один коммит вниз"
 
 #: gitk:3054
-#, fuzzy
 msgid "<Left>, z, h\tGo back in history list"
-msgstr "<Left>, z, j\tПоказать ранее посещённое состояние"
+msgstr "<Left>, z, h\tПоказать ранее посещённое состояние"
 
 #: gitk:3055
 msgid "<Right>, x, l\tGo forward in history list"
-msgstr "<Right>, x, l\tÐ\9fоказаÑ\82Ñ\8c Ñ\81ледÑ\83Ñ\8eÑ\89ее Ð¿Ð¾Ñ\81еÑ\89Ñ\91нное Ñ\81оÑ\81Ñ\82оÑ\8fние"
+msgstr "<Right>, x, l\tÐ\9fоказаÑ\82Ñ\8c Ñ\81ледÑ\83Ñ\8eÑ\89ий Ð¿Ð¾Ñ\81еÑ\89Ñ\91ннÑ\8bй ÐºÐ¾Ð¼Ð¼Ð¸Ñ\82"
 
 #: gitk:3056
 #, tcl-format
 msgid "<%s-n>\tGo to n-th parent of current commit in history list"
-msgstr ""
+msgstr "<%s-n>\tПерейти на n родителя от текущего коммита"
 
 #: gitk:3057
 msgid "<PageUp>\tMove up one page in commit list"
-msgstr "<PageUp>\tПерейти на страницу выше в списке состояний"
+msgstr "<PageUp>\tПерейти на страницу выше в списке коммитов"
 
 #: gitk:3058
 msgid "<PageDown>\tMove down one page in commit list"
-msgstr "<PageDown>\tПерейти на страницу ниже в списке состояний"
+msgstr "<PageDown>\tПерейти на страницу ниже в списке коммитов"
 
 #: gitk:3059
 #, tcl-format
 msgid "<%s-Home>\tScroll to top of commit list"
-msgstr "<%s-Home>\tÐ\9fоказаÑ\82Ñ\8c Ð½Ð°Ñ\87ало Ñ\81пиÑ\81ка Ñ\81оÑ\81Ñ\82оÑ\8fний"
+msgstr "<%s-Home>\tÐ\9fеÑ\80ейÑ\82и Ð½Ð° Ð½Ð°Ñ\87ало Ñ\81пиÑ\81ка ÐºÐ¾Ð¼Ð¼Ð¸Ñ\82ов"
 
 #: gitk:3060
 #, tcl-format
 msgid "<%s-End>\tScroll to bottom of commit list"
-msgstr "<%s-End>\tÐ\9fоказаÑ\82Ñ\8c ÐºÐ¾Ð½ÐµÑ\86 Ñ\81пиÑ\81ка Ñ\81оÑ\81Ñ\82оÑ\8fний"
+msgstr "<%s-End>\tÐ\9fеÑ\80ейÑ\82и Ð½Ð° ÐºÐ¾Ð½ÐµÑ\86 Ñ\81пиÑ\81ка ÐºÐ¾Ð¼Ð¼Ð¸Ñ\82ов"
 
 #: gitk:3061
 #, tcl-format
 msgid "<%s-Up>\tScroll commit list up one line"
-msgstr "<%s-Up>\tПровернуть список состояний вверх"
+msgstr "<%s-Up>\tПровернуть список коммитов вверх"
 
 #: gitk:3062
 #, tcl-format
 msgid "<%s-Down>\tScroll commit list down one line"
-msgstr "<%s-Down>\tПровернуть список состояний вниз"
+msgstr "<%s-Down>\tПровернуть список коммитов вниз"
 
 #: gitk:3063
 #, tcl-format
 msgid "<%s-PageUp>\tScroll commit list up one page"
-msgstr "<%s-PageUp>\tПровернуть список состояний на страницу вверх"
+msgstr "<%s-PageUp>\tПровернуть список коммитов на страницу вверх"
 
 #: gitk:3064
 #, tcl-format
 msgid "<%s-PageDown>\tScroll commit list down one page"
-msgstr "<%s-PageDown>\tПровернуть список состояний на страницу вниз"
+msgstr "<%s-PageDown>\tПровернуть список коммитов на страницу вниз"
 
 #: gitk:3065
 msgid "<Shift-Up>\tFind backwards (upwards, later commits)"
-msgstr "<Shift-Up>\tПоиск в обратном порядке (вверх, среди новых состояний)"
+msgstr "<Shift-Up>\tПоиск в обратном порядке (вверх, среди новых коммитов)"
 
 #: gitk:3066
 msgid "<Shift-Down>\tFind forwards (downwards, earlier commits)"
-msgstr "<Shift-Down>\tПоиск (вниз, среди старых состояний)"
+msgstr "<Shift-Down>\tПоиск (вниз, среди старых коммитов)"
 
 #: gitk:3067
 msgid "<Delete>, b\tScroll diff view up one page"
@@ -504,16 +492,15 @@ msgstr "<%s-F>\t\tПоиск"
 #: gitk:3073
 #, tcl-format
 msgid "<%s-G>\t\tMove to next find hit"
-msgstr "<%s-G>\t\tПерейти к следующему найденному состоянию"
+msgstr "<%s-G>\t\tПерейти к следующему найденному коммиту"
 
 #: gitk:3074
 msgid "<Return>\tMove to next find hit"
-msgstr "<Return>\tПерейти к следующему найденному состоянию"
+msgstr "<Return>\tПерейти к следующему найденному коммиту"
 
 #: gitk:3075
-#, fuzzy
 msgid "g\t\tGo to commit"
-msgstr "<End>\t\tПерейти к последнему состоянию"
+msgstr "g\t\tПерейти на коммит"
 
 #: gitk:3076
 msgid "/\t\tFocus the search box"
@@ -521,7 +508,7 @@ msgstr "/\t\tПерейти к полю поиска"
 
 #: gitk:3077
 msgid "?\t\tMove to previous find hit"
-msgstr "?\t\tПерейти к предыдущему найденному состоянию"
+msgstr "?\t\tПерейти к предыдущему найденному коммиту"
 
 #: gitk:3078
 msgid "f\t\tScroll diff view to next file"
@@ -569,7 +556,7 @@ msgstr "Ошибка создания временного каталога %s:"
 #: gitk:3572
 #, tcl-format
 msgid "Error getting \"%s\" from %s:"
-msgstr "Ошибка получения \"%s\" из %s:"
+msgstr "Ошибка получения «%s» из %s:"
 
 #: gitk:3635
 msgid "command failed:"
@@ -577,7 +564,7 @@ msgstr "ошибка выполнения команды:"
 
 #: gitk:3784
 msgid "No such commit"
-msgstr "СоÑ\81Ñ\82оÑ\8fние Ð½Ðµ Ð½Ð°Ð¹Ð´ÐµÐ½Ð¾"
+msgstr "Ð\9aоммиÑ\82 Ð½Ðµ Ð½Ð°Ð¹Ð´ÐµÐ½"
 
 #: gitk:3798
 msgid "git gui blame: command failed:"
@@ -610,8 +597,7 @@ msgstr "Ошибка выполнения git blame: %s"
 #: gitk:3925
 #, tcl-format
 msgid "That line comes from commit %s,  which is not in this view"
-msgstr ""
-"Эта строка принадлежит состоянию %s, которое не показано в этом представлении"
+msgstr "Эта строка принадлежит коммиту %s, который не показан в этом представлении"
 
 #: gitk:3939
 msgid "External diff viewer failed:"
@@ -627,103 +613,97 @@ msgstr "Запомнить представление"
 
 #: gitk:4075
 msgid "References (space separated list):"
-msgstr ""
+msgstr "Ссылки (разделённые пробелом):"
 
 #: gitk:4076
 msgid "Branches & tags:"
-msgstr ""
+msgstr "Ветки и метки"
 
 #: gitk:4077
-#, fuzzy
 msgid "All refs"
-msgstr "Ð\92Ñ\81е Ñ\84айлÑ\8b"
+msgstr "Ð\92Ñ\81е Ñ\81Ñ\81Ñ\8bлки"
 
 #: gitk:4078
 msgid "All (local) branches"
-msgstr ""
+msgstr "Все (локальные) ветки"
 
 #: gitk:4079
 msgid "All tags"
-msgstr ""
+msgstr "Все метки"
 
 #: gitk:4080
 msgid "All remote-tracking branches"
-msgstr ""
+msgstr "Все внешние отслеживаемые ветки"
 
 #: gitk:4081
 msgid "Commit Info (regular expressions):"
-msgstr ""
+msgstr "Информация о коммите (регулярные выражения):"
 
 #: gitk:4082
-#, fuzzy
 msgid "Author:"
-msgstr "Автор"
+msgstr "Автор:"
 
 #: gitk:4083
-#, fuzzy
 msgid "Committer:"
-msgstr "СоÑ\85Ñ\80анивÑ\88ий Ñ\81оÑ\81Ñ\82оÑ\8fние"
+msgstr "Ð\9aоммиÑ\82еÑ\80:"
 
 #: gitk:4084
 msgid "Commit Message:"
-msgstr ""
+msgstr "Сообщение коммита:"
 
 #: gitk:4085
 msgid "Matches all Commit Info criteria"
-msgstr ""
+msgstr "Совпадает со всеми условиями информации о коммите"
 
 #: gitk:4086
 msgid "Matches no Commit Info criteria"
-msgstr ""
+msgstr "Не совпадает с условиями информации о коммите"
 
 #: gitk:4087
 msgid "Changes to Files:"
-msgstr ""
+msgstr "Изменения файлов:"
 
 #: gitk:4088
 msgid "Fixed String"
-msgstr ""
+msgstr "Обычная строка"
 
 #: gitk:4089
 msgid "Regular Expression"
-msgstr ""
+msgstr "Регулярное выражение:"
 
 #: gitk:4090
-#, fuzzy
 msgid "Search string:"
-msgstr "Ð\9fоиÑ\81к"
+msgstr "СÑ\82Ñ\80ока Ð´Ð»Ñ\8f Ð¿Ð¾Ð¸Ñ\81ка:"
 
 #: gitk:4091
 msgid ""
 "Commit Dates (\"2 weeks ago\", \"2009-03-17 15:27:38\", \"March 17, 2009 "
 "15:27:38\"):"
-msgstr ""
+msgstr "Даты коммита («2 недели назад», «2009-03-17 15:27:38», «17 марта 2009 15:27:38»):"
 
 #: gitk:4092
-#, fuzzy
 msgid "Since:"
 msgstr "С даты:"
 
 #: gitk:4093
-#, fuzzy
 msgid "Until:"
 msgstr "По дату:"
 
 #: gitk:4094
 msgid "Limit and/or skip a number of revisions (positive integer):"
-msgstr ""
+msgstr "Ограничить и/или пропустить количество редакций (положительное число):"
 
 #: gitk:4095
 msgid "Number to show:"
-msgstr ""
+msgstr "Показать количество:"
 
 #: gitk:4096
 msgid "Number to skip:"
-msgstr ""
+msgstr "Пропустить количество:"
 
 #: gitk:4097
 msgid "Miscellaneous options:"
-msgstr ""
+msgstr "Различные опции:"
 
 #: gitk:4098
 msgid "Strictly sort by date"
@@ -731,7 +711,7 @@ msgstr "Строгая сортировка по дате"
 
 #: gitk:4099
 msgid "Mark branch sides"
-msgstr "Ð\9eÑ\82меÑ\82иÑ\82Ñ\8c Ñ\81Ñ\82оÑ\80онÑ\8b Ð²ÐµÑ\82вей"
+msgstr "Ð\9eÑ\82меÑ\82иÑ\82Ñ\8c Ñ\81Ñ\82оÑ\80онÑ\8b Ð²ÐµÑ\82ок"
 
 #: gitk:4100
 msgid "Limit to first parent"
@@ -739,12 +719,11 @@ msgstr "Ограничить первым предком"
 
 #: gitk:4101
 msgid "Simple history"
-msgstr ""
+msgstr "Упрощенная история"
 
 #: gitk:4102
-#, fuzzy
 msgid "Additional arguments to git log:"
-msgstr "Ð\92клÑ\8eÑ\87иÑ\82Ñ\8c Ñ\81оÑ\81Ñ\82оÑ\8fниÑ\8f (аÑ\80гÑ\83менÑ\82Ñ\8b Ð´Ð»Ñ\8f git-log):"
+msgstr "Ð\94ополниÑ\82елÑ\8cнÑ\8bе Ð°Ñ\80гÑ\83менÑ\82Ñ\8b Ð´Ð»Ñ\8f git log:"
 
 #: gitk:4103
 msgid "Enter files and directories to include, one per line:"
@@ -752,21 +731,19 @@ msgstr "Файлы и каталоги для ограничения истор
 
 #: gitk:4104
 msgid "Command to generate more commits to include:"
-msgstr "Дополнительная команда для списка состояний:"
+msgstr "Дополнительная команда для списка коммитов:"
 
 #: gitk:4228
 msgid "Gitk: edit view"
-msgstr ""
+msgstr "Gitk: изменить представление"
 
 #: gitk:4236
-#, fuzzy
 msgid "-- criteria for selecting revisions"
-msgstr "Ошибка в идентификаторе версии:"
+msgstr "— критерий поиска редакций"
 
 #: gitk:4241
-#, fuzzy
 msgid "View Name"
-msgstr "Ð\9fÑ\80едÑ\81Ñ\82авление"
+msgstr "Ð\98мÑ\8f Ð¿Ñ\80едÑ\81Ñ\82авлениÑ\8f"
 
 #: gitk:4316
 msgid "Apply (F5)"
@@ -774,7 +751,7 @@ msgstr "Применить (F5)"
 
 #: gitk:4354
 msgid "Error in commit selection arguments:"
-msgstr "Ошибка в параметрах выбора состояний:"
+msgstr "Ошибка в параметрах выбора коммитов:"
 
 #: gitk:4409 gitk:4462 gitk:4924 gitk:4938 gitk:6208 gitk:12373 gitk:12374
 msgid "None"
@@ -798,23 +775,23 @@ msgstr "Не предок"
 
 #: gitk:5324
 msgid "Local changes checked in to index but not committed"
-msgstr "Ð\98зменениÑ\8f Ð·Ð°Ñ\80егиÑ\81Ñ\82Ñ\80иÑ\80ованнÑ\8bе Ð² Ð¸Ð½Ð´ÐµÐºÑ\81е, Ð½Ð¾ Ð½Ðµ Ñ\81оÑ\85Ñ\80анÑ\91ннÑ\8bе"
+msgstr "Ð\9fÑ\80оиндекÑ\81иÑ\80ованнÑ\8bе Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ\8f"
 
 #: gitk:5360
 msgid "Local uncommitted changes, not checked in to index"
-msgstr "Ð\98зменениÑ\8f Ð² Ñ\80абоÑ\87ем ÐºÐ°Ñ\82алоге, Ð½Ðµ Ð·Ð°Ñ\80егиÑ\81Ñ\82Ñ\80иÑ\80ованнÑ\8bе Ð² Ð¸Ð½Ð´ÐµÐºÑ\81е"
+msgstr "Ð\9dепÑ\80оиндекÑ\81иÑ\80ованнÑ\8bе Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ\8f"
 
 #: gitk:7134
 msgid "and many more"
-msgstr ""
+msgstr "и многое другое"
 
 #: gitk:7137
 msgid "many"
-msgstr ""
+msgstr "много"
 
 #: gitk:7328
 msgid "Tags:"
-msgstr "Таги:"
+msgstr "Ð\9cеÑ\82ки:"
 
 #: gitk:7345 gitk:7351 gitk:8825
 msgid "Parent"
@@ -826,7 +803,7 @@ msgstr "Потомок"
 
 #: gitk:7365
 msgid "Branch"
-msgstr "Ð\92еÑ\82вÑ\8c"
+msgstr "Ð\92еÑ\82ка"
 
 #: gitk:7368
 msgid "Follows"
@@ -851,9 +828,9 @@ msgid "Short SHA1 id %s is ambiguous"
 msgstr "Сокращённый SHA1 идентификатор %s неоднозначен"
 
 #: gitk:8678
-#, fuzzy, tcl-format
+#, tcl-format
 msgid "Revision %s is not known"
-msgstr "SHA1 идентификатор %s не найден"
+msgstr "Редакция %s не найдена"
 
 #: gitk:8688
 #, tcl-format
@@ -863,7 +840,7 @@ msgstr "SHA1 идентификатор %s не найден"
 #: gitk:8690
 #, tcl-format
 msgid "Revision %s is not in the current view"
-msgstr ""
+msgstr "Редакция %s не найдена в текущем представлении"
 
 #: gitk:8832 gitk:8847
 msgid "Date"
@@ -876,62 +853,60 @@ msgstr "Потомки"
 #: gitk:8898
 #, tcl-format
 msgid "Reset %s branch to here"
-msgstr "УÑ\81Ñ\82ановиÑ\82Ñ\8c Ð²ÐµÑ\82вÑ\8c %s Ð½Ð° Ñ\8dÑ\82о Ñ\81оÑ\81Ñ\82оÑ\8fние"
+msgstr "СбÑ\80оÑ\81иÑ\82Ñ\8c Ð²ÐµÑ\82кÑ\83 %s Ð½Ð° Ñ\8dÑ\82оÑ\82 ÐºÐ¾Ð¼Ð¼Ð¸Ñ\82"
 
 #: gitk:8900
 msgid "Detached head: can't reset"
-msgstr "СоÑ\81Ñ\82оÑ\8fние Ð½Ðµ Ð¿Ñ\80инадлежиÑ\82 Ð½Ð¸ Ð¾Ð´Ð½Ð¾Ð¹ Ð²ÐµÑ\82ви, Ð¿ÐµÑ\80еÑ\85од Ð½ÐµÐ²Ð¾Ð·Ð¼Ð¾Ð¶ÐµÐ½"
+msgstr "Ð\9aоммиÑ\82 Ð½Ðµ Ð¿Ñ\80инадлежиÑ\82 Ð½Ð¸ Ð¾Ð´Ð½Ð¾Ð¹ Ð²ÐµÑ\82ке, Ñ\81бÑ\80оÑ\81иÑ\82Ñ\8c Ð½ÐµÐ²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ð¾"
 
 #: gitk:9005 gitk:9011
 msgid "Skipping merge commit "
-msgstr ""
+msgstr "Пропускаю коммит-слияние"
 
 #: gitk:9020 gitk:9025
-#, fuzzy
 msgid "Error getting patch ID for "
-msgstr "Ð\9eÑ\88ибка Ñ\81озданиÑ\8f Ð¿Ð°Ñ\82Ñ\87а:"
+msgstr "Ð\9dе Ñ\83далоÑ\81Ñ\8c Ð¿Ð¾Ð»Ñ\83Ñ\87иÑ\82Ñ\8c Ð¸Ð´ÐµÐ½Ñ\82иÑ\84икаÑ\82оÑ\80 Ð¿Ð°Ñ\82Ñ\87а Ð´Ð»Ñ\8f "
 
 #: gitk:9021 gitk:9026
 msgid " - stopping\n"
-msgstr ""
+msgstr " — останов\n"
 
 #: gitk:9031 gitk:9034 gitk:9042 gitk:9056 gitk:9065
-#, fuzzy
 msgid "Commit "
-msgstr "состояние"
+msgstr "Коммит"
 
 #: gitk:9035
 msgid ""
 " is the same patch as\n"
 "       "
-msgstr ""
+msgstr " такой же патч, как и\n       "
 
 #: gitk:9043
 msgid ""
 " differs from\n"
 "       "
-msgstr ""
+msgstr " отличается от\n       "
 
 #: gitk:9045
 msgid ""
 "Diff of commits:\n"
 "\n"
-msgstr ""
+msgstr "Различия коммитов:\n\n"
 
 #: gitk:9057 gitk:9066
 #, tcl-format
 msgid " has %s children - stopping\n"
-msgstr ""
+msgstr " является %s потомком — останов\n"
 
 #: gitk:9085
-#, fuzzy, tcl-format
+#, tcl-format
 msgid "Error writing commit to file: %s"
-msgstr "Ð\9eÑ\88ибка Ñ\81оÑ\85Ñ\80анениÑ\8f Ñ\81оÑ\81Ñ\82оÑ\8fниÑ\8f:"
+msgstr "Ð\9fÑ\80оизоÑ\88ла Ð¾Ñ\88ибка Ð¿Ñ\80и Ð·Ð°Ð¿Ð¸Ñ\81и ÐºÐ¾Ð¼Ð¼Ð¸Ñ\82а Ð² Ñ\84айл: %s"
 
 #: gitk:9091
-#, fuzzy, tcl-format
+#, tcl-format
 msgid "Error diffing commits: %s"
-msgstr "Ð\9eÑ\88ибка Ñ\81оÑ\85Ñ\80анениÑ\8f Ñ\81оÑ\81Ñ\82оÑ\8fниÑ\8f:"
+msgstr "Ð\9fÑ\80оизоÑ\88ла Ð¾Ñ\88ибка Ð¿Ñ\80и Ð²Ñ\8bводе Ñ\80азлиÑ\87ий ÐºÐ¾Ð¼Ð¼Ð¸Ñ\82ов: %s"
 
 #: gitk:9137
 msgid "Top"
@@ -983,12 +958,11 @@ msgstr "Имя метки:"
 
 #: gitk:9268
 msgid "Tag message is optional"
-msgstr ""
+msgstr "Описание метки указывать не обязательно"
 
 #: gitk:9270
-#, fuzzy
 msgid "Tag message:"
-msgstr "Ð\98мÑ\8f метки:"
+msgstr "Ð\9eпиÑ\81ание метки:"
 
 #: gitk:9274 gitk:9439
 msgid "Create"
@@ -1001,7 +975,7 @@ msgstr "Не задано имя метки"
 #: gitk:9296
 #, tcl-format
 msgid "Tag \"%s\" already exists"
-msgstr "Метка \"%s\" уже существует"
+msgstr "Метка «%s» уже существует"
 
 #: gitk:9306
 msgid "Error creating tag:"
@@ -1017,7 +991,7 @@ msgstr "Запись"
 
 #: gitk:9408
 msgid "Error writing commit:"
-msgstr "Ð\9eÑ\88ибка Ñ\81оÑ\85Ñ\80анениÑ\8f Ñ\81оÑ\81Ñ\82оÑ\8fниÑ\8f:"
+msgstr "Ð\9fÑ\80оизоÑ\88ла Ð¾Ñ\88ибка Ð¿Ñ\80и Ð·Ð°Ð¿Ð¸Ñ\81и ÐºÐ¾Ð¼Ð¼Ð¸Ñ\82а:"
 
 #: gitk:9435
 msgid "Name:"
@@ -1025,17 +999,17 @@ msgstr "Имя:"
 
 #: gitk:9458
 msgid "Please specify a name for the new branch"
-msgstr "УкажиÑ\82е Ð¸Ð¼Ñ\8f Ð´Ð»Ñ\8f Ð½Ð¾Ð²Ð¾Ð¹ Ð²ÐµÑ\82ви"
+msgstr "УкажиÑ\82е Ð¸Ð¼Ñ\8f Ð´Ð»Ñ\8f Ð½Ð¾Ð²Ð¾Ð¹ Ð²ÐµÑ\82ки"
 
 #: gitk:9463
 #, tcl-format
 msgid "Branch '%s' already exists. Overwrite?"
-msgstr "Ð\92еÑ\82вÑ\8c '%s' уже существует. Переписать?"
+msgstr "Ð\92еÑ\82ка Â«%s» уже существует. Переписать?"
 
 #: gitk:9530
 #, tcl-format
 msgid "Commit %s is already included in branch %s -- really re-apply it?"
-msgstr "СоÑ\81Ñ\82оÑ\8fние %s Ñ\83же Ð¿Ñ\80инадлежиÑ\82 Ð²ÐµÑ\82ви %s. Продолжить операцию?"
+msgstr "Ð\9aоммиÑ\82 %s Ñ\83же Ð²ÐºÐ»Ñ\8eÑ\87Ñ\91н Ð² Ð²ÐµÑ\82кÑ\83 %s. Продолжить операцию?"
 
 #: gitk:9535
 msgid "Cherry-picking"
@@ -1046,49 +1020,39 @@ msgstr "Копирование изменений"
 msgid ""
 "Cherry-pick failed because of local changes to file '%s'.\n"
 "Please commit, reset or stash your changes and try again."
-msgstr ""
-"Копирование невозможно из-за изменений в файле '%s'.\n"
-"Сохраните или отмените изменения и повторите операцию."
+msgstr "Отбор лучшего невозможен из-за изменений в файле «%s».\nЗакомитьте, сбросьте или спрячьте изменения и повторите операцию."
 
 #: gitk:9550
 msgid ""
 "Cherry-pick failed because of merge conflict.\n"
 "Do you wish to run git citool to resolve it?"
-msgstr ""
-"Копирование изменений невозможно из-за незавершённой операции слияния.\n"
-"Запустить git citool для завершения этой операции?"
+msgstr "Копирование изменений невозможно из-за незавершённой операции слияния.\nЗапустить git citool для завершения этой операции?"
 
 #: gitk:9566 gitk:9624
 msgid "No changes committed"
-msgstr "Изменения не сохранены"
+msgstr "Изменения не закоммичены"
 
 #: gitk:9593
-#, fuzzy, tcl-format
+#, tcl-format
 msgid "Commit %s is not included in branch %s -- really revert it?"
-msgstr "СоÑ\81Ñ\82оÑ\8fние %s Ñ\83же Ð¿Ñ\80инадлежиÑ\82 Ð²ÐµÑ\82ви %s. Продолжить операцию?"
+msgstr "Ð\9aоммиÑ\82 %s Ð½Ðµ Ð²ÐºÐ»Ñ\8eÑ\87Ñ\91н Ð² Ð²ÐµÑ\82кÑ\83 %s. Продолжить операцию?"
 
 #: gitk:9598
-#, fuzzy
 msgid "Reverting"
-msgstr "УÑ\81Ñ\82ановка"
+msgstr "Ð\92озвÑ\80аÑ\82 Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ð¹"
 
 #: gitk:9606
-#, fuzzy, tcl-format
+#, tcl-format
 msgid ""
 "Revert failed because of local changes to the following files:%s Please "
 "commit, reset or stash  your changes and try again."
-msgstr ""
-"Копирование невозможно из-за изменений в файле '%s'.\n"
-"Сохраните или отмените изменения и повторите операцию."
+msgstr "Возврат изменений коммита не удался из-за локальных изменений в указанных файлах: %s\nЗакомитьте, сбросьте или спрячьте изменения и повторите операцию."
 
 #: gitk:9610
-#, fuzzy
 msgid ""
 "Revert failed because of merge conflict.\n"
 " Do you wish to run git citool to resolve it?"
-msgstr ""
-"Копирование изменений невозможно из-за незавершённой операции слияния.\n"
-"Запустить git citool для завершения этой операции?"
+msgstr "Возврат изменений невозможен из-за незавершённой операции слияния.\nЗапустить git citool для завершения этой операции?"
 
 #: gitk:9653
 msgid "Confirm reset"
@@ -1097,7 +1061,7 @@ msgstr "Подтвердите операцию перехода"
 #: gitk:9655
 #, tcl-format
 msgid "Reset branch %s to %s?"
-msgstr "УÑ\81Ñ\82ановиÑ\82Ñ\8c Ð²ÐµÑ\82вÑ\8c %s Ð½Ð° Ñ\81оÑ\81Ñ\82оÑ\8fние %s?"
+msgstr "СбÑ\80оÑ\81иÑ\82Ñ\8c Ð²ÐµÑ\82кÑ\83 %s Ð½Ð° ÐºÐ¾Ð¼Ð¼Ð¸Ñ\82 %s?"
 
 #: gitk:9657
 msgid "Reset type:"
@@ -1115,13 +1079,11 @@ msgstr "Смешанный: оставить рабочий каталог не
 msgid ""
 "Hard: Reset working tree and index\n"
 "(discard ALL local changes)"
-msgstr ""
-"Жесткий: переписать индекс и рабочий каталог\n"
-"(все изменения в рабочем каталоге будут потеряны)"
+msgstr "Жесткий: переписать индекс и рабочий каталог\n(все изменения в рабочем каталоге будут потеряны)"
 
 #: gitk:9683
 msgid "Resetting"
-msgstr "УÑ\81Ñ\82ановка"
+msgstr "СбÑ\80оÑ\81"
 
 #: gitk:9743
 msgid "Checking out"
@@ -1129,21 +1091,19 @@ msgstr "Переход"
 
 #: gitk:9796
 msgid "Cannot delete the currently checked-out branch"
-msgstr "Ð\90кÑ\82ивнаÑ\8f Ð²ÐµÑ\82вÑ\8c не может быть удалена"
+msgstr "Ð\90кÑ\82ивнаÑ\8f Ð²ÐµÑ\82ка не может быть удалена"
 
 #: gitk:9802
 #, tcl-format
 msgid ""
 "The commits on branch %s aren't on any other branch.\n"
 "Really delete branch %s?"
-msgstr ""
-"Состояния ветви %s больше не принадлежат никакой другой ветви.\n"
-"Действительно удалить ветвь %s?"
+msgstr "Коммиты из ветки %s не принадлежат больше никакой другой ветке.\nДействительно удалить ветку %s?"
 
 #: gitk:9833
 #, tcl-format
 msgid "Tags and heads: %s"
-msgstr "Ð\9cеÑ\82ки Ð¸ Ð²ÐµÑ\82ви: %s"
+msgstr "Ð\9cеÑ\82ки Ð¸ Ð²ÐµÑ\82ки: %s"
 
 #: gitk:9850
 msgid "Filter"
@@ -1153,9 +1113,7 @@ msgstr "Фильтровать"
 msgid ""
 "Error reading commit topology information; branch and preceding/following "
 "tag information will be incomplete."
-msgstr ""
-"Ошибка чтения истории проекта; информация о ветвях и состояниях вокруг меток "
-"(до/после) может быть неполной."
+msgstr "Ошибка чтения истории проекта; информация о ветках и коммитах вокруг меток (до/после) может быть неполной."
 
 #: gitk:11123
 msgid "Tag"
@@ -1179,7 +1137,7 @@ msgstr "К"
 
 #: gitk:11348
 msgid "Commit list display options"
-msgstr "Параметры показа списка состояний"
+msgstr "Параметры показа списка коммитов"
 
 #: gitk:11351
 msgid "Maximum graph width (lines)"
@@ -1195,13 +1153,12 @@ msgid "Show local changes"
 msgstr "Показывать изменения в рабочем каталоге"
 
 #: gitk:11361
-#, fuzzy
 msgid "Auto-select SHA1 (length)"
-msgstr "Ð\92Ñ\8bделиÑ\82Ñ\8c SHA1"
+msgstr "Ð\90вÑ\82омаÑ\82иÑ\87еÑ\81ки Ð²Ñ\8bделиÑ\82Ñ\8c SHA1 (длинна)"
 
 #: gitk:11365
 msgid "Hide remote refs"
-msgstr ""
+msgstr "Скрыть внешние ссылки"
 
 #: gitk:11369
 msgid "Diff display options"
@@ -1212,13 +1169,12 @@ msgid "Tab spacing"
 msgstr "Ширина табуляции"
 
 #: gitk:11374
-#, fuzzy
 msgid "Display nearby tags/heads"
-msgstr "Показывать близкие метки"
+msgstr "Показывать близкие метки/ветки"
 
 #: gitk:11377
 msgid "Maximum # tags/heads to show"
-msgstr ""
+msgstr "Показывать максимальное количество меток/веток"
 
 #: gitk:11380
 msgid "Limit diffs to listed paths"
@@ -1237,21 +1193,20 @@ msgid "Choose..."
 msgstr "Выберите..."
 
 #: gitk:11395
-#, fuzzy
 msgid "General options"
-msgstr "СоздаÑ\82Ñ\8c Ð¿Ð°Ñ\82Ñ\87"
+msgstr "Ð\9eбÑ\89ие Ð¾Ð¿Ñ\86ии"
 
 #: gitk:11398
 msgid "Use themed widgets"
-msgstr ""
+msgstr "Использовать стили виджетов"
 
 #: gitk:11400
 msgid "(change requires restart)"
-msgstr ""
+msgstr "(изменение потребует перезапуск)"
 
 #: gitk:11402
 msgid "(currently unavailable)"
-msgstr ""
+msgstr "(недоступно в данный момент)"
 
 #: gitk:11413
 msgid "Colors: press to choose"
@@ -1259,12 +1214,11 @@ msgstr "Цвета: нажмите для выбора"
 
 #: gitk:11416
 msgid "Interface"
-msgstr ""
+msgstr "Интерфейс"
 
 #: gitk:11417
-#, fuzzy
 msgid "interface"
-msgstr "ШÑ\80иÑ\84Ñ\82 Ð¸Ð½Ñ\82еÑ\80Ñ\84ейÑ\81а"
+msgstr "инÑ\82еÑ\80Ñ\84ейÑ\81"
 
 #: gitk:11420
 msgid "Background"
@@ -1339,17 +1293,16 @@ msgid "Gitk preferences"
 msgstr "Настройки Gitk"
 
 #: gitk:11494
-#, fuzzy
 msgid "General"
-msgstr "СоздаÑ\82Ñ\8c"
+msgstr "Ð\9eбÑ\89ие"
 
 #: gitk:11495
 msgid "Colors"
-msgstr ""
+msgstr "Цвета"
 
 #: gitk:11496
 msgid "Fonts"
-msgstr ""
+msgstr "Шрифты"
 
 #: gitk:11546
 #, tcl-format
@@ -1360,9 +1313,7 @@ msgstr "Gitk: выберите цвет для %s"
 msgid ""
 "Sorry, gitk cannot run with this version of Tcl/Tk.\n"
 " Gitk requires at least Tcl/Tk 8.4."
-msgstr ""
-"К сожалению gitk не может работать с этой версий Tcl/Tk.\n"
-"Требуется как минимум Tcl/Tk 8.4."
+msgstr "К сожалению gitk не может работать с этой версий Tcl/Tk.\nТребуется как минимум Tcl/Tk 8.4."
 
 #: gitk:12269
 msgid "Cannot find a git repository here."
@@ -1371,38 +1322,8 @@ msgstr "Git-репозитарий не найден в текущем ката
 #: gitk:12316
 #, tcl-format
 msgid "Ambiguous argument '%s': both revision and filename"
-msgstr "Неоднозначный аргумент '%s': существует как версия и имя файла"
+msgstr "Неоднозначный аргумент «%s»: существует как редакция и как имя файла"
 
 #: gitk:12328
 msgid "Bad arguments to gitk:"
 msgstr "Неправильные аргументы для gitk:"
-
-#~ msgid "SHA1 ID: "
-#~ msgstr "SHA1:"
-
-#~ msgid "next"
-#~ msgstr "След."
-
-#~ msgid "prev"
-#~ msgstr "Пред."
-
-#~ msgid "Use all refs"
-#~ msgstr "Использовать все ветви"
-
-#~ msgid "Max count:"
-#~ msgstr "Макс. количество:"
-
-#~ msgid "Skip:"
-#~ msgstr "Пропустить:"
-
-#~ msgid "Name"
-#~ msgstr "Имя"
-
-#~ msgid "CDate"
-#~ msgstr "Дата ввода"
-
-#~ msgid "Tag/Head %s is not known"
-#~ msgstr "Метка или ветвь %s не найдена"
-
-#~ msgid "Cannot find the git directory \"%s\"."
-#~ msgstr "Git-репозитарий \"%s\" не найден."
index 0bd46d7881017506e1d5e8afc4a4225b0d840e2e..75317f43965ba4a6f9b173f476e13851ca069e52 100644 (file)
@@ -92,71 +92,71 @@ msgid "Cancel"
 msgstr "Avbryt"
 
 #: gitk:2069
-msgid "Update"
+msgid "&Update"
 msgstr "Uppdatera"
 
 #: gitk:2070
-msgid "Reload"
+msgid "&Reload"
 msgstr "Ladda om"
 
 #: gitk:2071
-msgid "Reread references"
+msgid "Reread re&ferences"
 msgstr "Läs om referenser"
 
 #: gitk:2072
-msgid "List references"
+msgid "&List references"
 msgstr "Visa referenser"
 
 #: gitk:2074
-msgid "Start git gui"
+msgid "Start git &gui"
 msgstr "Starta git gui"
 
 #: gitk:2076
-msgid "Quit"
+msgid "&Quit"
 msgstr "Avsluta"
 
 #: gitk:2068
-msgid "File"
+msgid "&File"
 msgstr "Arkiv"
 
 #: gitk:2080
-msgid "Preferences"
+msgid "&Preferences"
 msgstr "Inställningar"
 
 #: gitk:2079
-msgid "Edit"
+msgid "&Edit"
 msgstr "Redigera"
 
 #: gitk:2084
-msgid "New view..."
+msgid "&New view..."
 msgstr "Ny vy..."
 
 #: gitk:2085
-msgid "Edit view..."
+msgid "&Edit view..."
 msgstr "Ändra vy..."
 
 #: gitk:2086
-msgid "Delete view"
+msgid "&Delete view"
 msgstr "Ta bort vy"
 
 #: gitk:2088 gitk:4043
-msgid "All files"
+msgid "&All files"
 msgstr "Alla filer"
 
 #: gitk:2083 gitk:4067
-msgid "View"
+msgid "&View"
 msgstr "Visa"
 
 #: gitk:2093 gitk:2103 gitk:3012
-msgid "About gitk"
+msgid "&About gitk"
 msgstr "Om gitk"
 
 #: gitk:2094 gitk:2108
-msgid "Key bindings"
+msgid "&Key bindings"
 msgstr "Tangentbordsbindningar"
 
 #: gitk:2092 gitk:2107
-msgid "Help"
+msgid "&Help"
 msgstr "Hjälp"
 
 #: gitk:2185 gitk:8652
index 7133c422b7363b08fbb49fd6526c8db9af81b0eb..8966812368a626e8da9f42012b69f233b4181642 100644 (file)
@@ -1,14 +1,14 @@
 # Vietnamese translations for gitk package.
 # Bản dịch tiếng Việt cho gói gitk.
 # This file is distributed under the same license as the gitk package.
-# Trần Ngọc Quân <vnwildman@gmail.com>, 2013.
+# Trần Ngọc Quân <vnwildman@gmail.com>, 2013, 2015.
 #
 msgid ""
 msgstr ""
 "Project-Id-Version: gitk @@GIT_VERSION@@\n"
 "Report-Msgid-Bugs-To: \n"
 "POT-Creation-Date: 2015-05-17 14:32+1000\n"
-"PO-Revision-Date: 2013-12-14 14:40+0700\n"
+"PO-Revision-Date: 2015-09-15 07:33+0700\n"
 "Last-Translator: Trần Ngọc Quân <vnwildman@gmail.com>\n"
 "Language-Team: Vietnamese <translation-team-vi@lists.sourceforge.net>\n"
 "Language: vi\n"
@@ -16,6 +16,7 @@ msgstr ""
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=1; plural=0;\n"
+"X-Generator: Gtranslator 2.91.7\n"
 
 #: gitk:140
 msgid "Couldn't get list of unmerged files:"
@@ -60,7 +61,7 @@ msgstr "Đang đọc"
 
 #: gitk:496 gitk:4525
 msgid "Reading commits..."
-msgstr "Đang đọc các lần chuyển giao..."
+msgstr "Đang đọc các lần chuyển giao"
 
 #: gitk:499 gitk:1637 gitk:4528
 msgid "No commits selected"
@@ -88,71 +89,71 @@ msgid "Cancel"
 msgstr "Thôi"
 
 #: gitk:2069
-msgid "Update"
+msgid "&Update"
 msgstr "Cập nhật"
 
 #: gitk:2070
-msgid "Reload"
+msgid "&Reload"
 msgstr "Tải lại"
 
 #: gitk:2071
-msgid "Reread references"
+msgid "Reread re&ferences"
 msgstr "Đọc lại tham chiếu"
 
 #: gitk:2072
-msgid "List references"
+msgid "&List references"
 msgstr "Liệt kê các tham chiếu"
 
 #: gitk:2074
-msgid "Start git gui"
+msgid "Start git &gui"
 msgstr "Khởi chạy git gui"
 
 #: gitk:2076
-msgid "Quit"
+msgid "&Quit"
 msgstr "Thoát"
 
 #: gitk:2068
-msgid "File"
+msgid "&File"
 msgstr "Chính"
 
 #: gitk:2080
-msgid "Preferences"
-msgstr "Cá nhân hóa"
+msgid "&Preferences"
+msgstr "Tùy thích"
 
 #: gitk:2079
-msgid "Edit"
+msgid "&Edit"
 msgstr "Chỉnh sửa"
 
 #: gitk:2084
-msgid "New view..."
-msgstr "Thêm trình bày mới..."
+msgid "&New view..."
+msgstr "Thêm trình bày mới"
 
 #: gitk:2085
-msgid "Edit view..."
-msgstr "Sửa cách trình bày..."
+msgid "&Edit view..."
+msgstr "Sửa cách trình bày"
 
 #: gitk:2086
-msgid "Delete view"
+msgid "&Delete view"
 msgstr "Xóa cách trình bày"
 
 #: gitk:2088 gitk:4043
-msgid "All files"
+msgid "&All files"
 msgstr "Mọi tập tin"
 
 #: gitk:2083 gitk:4067
-msgid "View"
+msgid "&View"
 msgstr "Trình bày"
 
 #: gitk:2093 gitk:2103 gitk:3012
-msgid "About gitk"
+msgid "&About gitk"
 msgstr "Giới thiệu về gitk"
 
 #: gitk:2094 gitk:2108
-msgid "Key bindings"
+msgid "&Key bindings"
 msgstr "Tổ hợp phím"
 
 #: gitk:2092 gitk:2107
-msgid "Help"
+msgid "&Help"
 msgstr "Trợ giúp"
 
 #: gitk:2185 gitk:8652
@@ -319,7 +320,7 @@ msgstr "Hoàn lại lần chuyển giao này"
 
 #: gitk:2647
 msgid "Check out this branch"
-msgstr "Checkout nhánh này"
+msgstr "Lấy ra nhánh này"
 
 #: gitk:2648
 msgid "Remove this branch"
@@ -327,7 +328,7 @@ msgstr "Gỡ bỏ nhánh này"
 
 #: gitk:2649
 msgid "Copy branch name"
-msgstr ""
+msgstr "Chép tên nhánh"
 
 #: gitk:2656
 msgid "Highlight this too"
@@ -347,7 +348,7 @@ msgstr "Xem công trạng lần chuyển giao cha mẹ"
 
 #: gitk:2660
 msgid "Copy path"
-msgstr ""
+msgstr "Chép đường dẫn"
 
 #: gitk:2667
 msgid "Show origin of this line"
@@ -358,7 +359,6 @@ msgid "Run git gui blame on this line"
 msgstr "Chạy lệnh git gui blame cho dòng này"
 
 #: gitk:3014
-#, fuzzy
 msgid ""
 "\n"
 "Gitk - a commit viewer for git\n"
@@ -368,9 +368,9 @@ msgid ""
 "Use and redistribute under the terms of the GNU General Public License"
 msgstr ""
 "\n"
-"Gitk - phần mềm xem các lần chuyển giao dành cho git\n"
+"Gitk - ứng dụng để xem các lần chuyển giao dành cho git\n"
 "\n"
-"Bản quyền © 2005-2011 Paul Mackerras\n"
+"Bản quyền © 2005-2014 Paul Mackerras\n"
 "\n"
 "Dùng và phân phối lại phần mềm này theo các điều khoản của Giấy Phép Công GNU"
 
@@ -424,6 +424,7 @@ msgstr "<Right>, x, l\tDi chuyển tiếp trong danh sách lịch sử"
 #, tcl-format
 msgid "<%s-n>\tGo to n-th parent of current commit in history list"
 msgstr ""
+"<%s-n>\tĐến cha thứ n của lần chuyển giao hiện tại trong danh sách lịch sử"
 
 #: gitk:3057
 msgid "<PageUp>\tMove up one page in commit list"
@@ -507,9 +508,8 @@ msgid "<Return>\tMove to next find hit"
 msgstr "<Return>\t\tDi chuyển đến chỗ gặp kế tiếp"
 
 #: gitk:3075
-#, fuzzy
 msgid "g\t\tGo to commit"
-msgstr "<End>\t\tChuyển đến lần chuyển giao cuối"
+msgstr "g\t\tChuyển đến lần chuyển giao"
 
 #: gitk:3076
 msgid "/\t\tFocus the search box"
@@ -666,9 +666,8 @@ msgid "Matches all Commit Info criteria"
 msgstr "Khớp mọi điều kiện Thông tin Chuyển giao"
 
 #: gitk:4086
-#, fuzzy
 msgid "Matches no Commit Info criteria"
-msgstr "Khớp mọi điều kiện Thông tin Chuyển giao"
+msgstr "Khớp không điều kiện Thông tin Chuyển giao"
 
 #: gitk:4087
 msgid "Changes to Files:"
@@ -716,7 +715,7 @@ msgstr "Số lượng sẽ bỏ qua:"
 
 #: gitk:4097
 msgid "Miscellaneous options:"
-msgstr "Tuỳ chọn hỗn hợp:"
+msgstr "Tùy chọn hỗn hợp:"
 
 #: gitk:4098
 msgid "Strictly sort by date"
@@ -971,7 +970,7 @@ msgstr "Gặp lỗi khi tạo miếng vá:"
 
 #: gitk:9256 gitk:9373 gitk:9430
 msgid "ID:"
-msgstr "ID:"
+msgstr "Mã số:"
 
 #: gitk:9265
 msgid "Tag name:"
@@ -1186,7 +1185,7 @@ msgstr "Độ rộng biểu đồ tối đa (dòng)"
 #: gitk:11355
 #, no-tcl-format
 msgid "Maximum graph width (% of pane)"
-msgstr "Độ rộng biểu đồ tối đa (% của bảng)"
+msgstr "Độ rộng đồ thị tối đa (% của bảng)"
 
 #: gitk:11358
 msgid "Show local changes"
@@ -1194,7 +1193,7 @@ msgstr "Hiển thị các thay đổi nội bộ"
 
 #: gitk:11361
 msgid "Auto-select SHA1 (length)"
-msgstr "Tự chọn SHA1 (độ dài)"
+msgstr "Tự chọn (độ dài) SHA1"
 
 #: gitk:11365
 msgid "Hide remote refs"
@@ -1230,7 +1229,7 @@ msgstr "Công cụ so sánh từ bên ngoài"
 
 #: gitk:11390
 msgid "Choose..."
-msgstr "Chọn..."
+msgstr "Chọn"
 
 #: gitk:11395
 msgid "General options"
@@ -1354,6 +1353,8 @@ msgid ""
 "Sorry, gitk cannot run with this version of Tcl/Tk.\n"
 " Gitk requires at least Tcl/Tk 8.4."
 msgstr ""
+"Rất tiếc, gitk không thể chạy Tcl/Tk phiên bản này.\n"
+" Gitk cần ít nhất là Tcl/Tk 8.4."
 
 #: gitk:12269
 msgid "Cannot find a git repository here."
@@ -1366,7 +1367,7 @@ msgstr "Đối số “%s” chưa rõ ràng: vừa là điểm xét duyệt v
 
 #: gitk:12328
 msgid "Bad arguments to gitk:"
-msgstr "Đối số không hợp lệ cho gitk:"
+msgstr "Đối số cho gitk không hợp lệ:"
 
 #~ msgid "mc"
 #~ msgstr "mc"
diff --git a/mailinfo.c b/mailinfo.c
new file mode 100644 (file)
index 0000000..e157ca6
--- /dev/null
@@ -0,0 +1,1037 @@
+#include "cache.h"
+#include "utf8.h"
+#include "strbuf.h"
+#include "mailinfo.h"
+
+static void cleanup_space(struct strbuf *sb)
+{
+       size_t pos, cnt;
+       for (pos = 0; pos < sb->len; pos++) {
+               if (isspace(sb->buf[pos])) {
+                       sb->buf[pos] = ' ';
+                       for (cnt = 0; isspace(sb->buf[pos + cnt + 1]); cnt++);
+                       strbuf_remove(sb, pos + 1, cnt);
+               }
+       }
+}
+
+static void get_sane_name(struct strbuf *out, struct strbuf *name, struct strbuf *email)
+{
+       struct strbuf *src = name;
+       if (name->len < 3 || 60 < name->len || strchr(name->buf, '@') ||
+               strchr(name->buf, '<') || strchr(name->buf, '>'))
+               src = email;
+       else if (name == out)
+               return;
+       strbuf_reset(out);
+       strbuf_addbuf(out, src);
+}
+
+static void parse_bogus_from(struct mailinfo *mi, const struct strbuf *line)
+{
+       /* John Doe <johndoe> */
+
+       char *bra, *ket;
+       /* This is fallback, so do not bother if we already have an
+        * e-mail address.
+        */
+       if (mi->email.len)
+               return;
+
+       bra = strchr(line->buf, '<');
+       if (!bra)
+               return;
+       ket = strchr(bra, '>');
+       if (!ket)
+               return;
+
+       strbuf_reset(&mi->email);
+       strbuf_add(&mi->email, bra + 1, ket - bra - 1);
+
+       strbuf_reset(&mi->name);
+       strbuf_add(&mi->name, line->buf, bra - line->buf);
+       strbuf_trim(&mi->name);
+       get_sane_name(&mi->name, &mi->name, &mi->email);
+}
+
+static void handle_from(struct mailinfo *mi, const struct strbuf *from)
+{
+       char *at;
+       size_t el;
+       struct strbuf f;
+
+       strbuf_init(&f, from->len);
+       strbuf_addbuf(&f, from);
+
+       at = strchr(f.buf, '@');
+       if (!at) {
+               parse_bogus_from(mi, from);
+               return;
+       }
+
+       /*
+        * If we already have one email, don't take any confusing lines
+        */
+       if (mi->email.len && strchr(at + 1, '@')) {
+               strbuf_release(&f);
+               return;
+       }
+
+       /* Pick up the string around '@', possibly delimited with <>
+        * pair; that is the email part.
+        */
+       while (at > f.buf) {
+               char c = at[-1];
+               if (isspace(c))
+                       break;
+               if (c == '<') {
+                       at[-1] = ' ';
+                       break;
+               }
+               at--;
+       }
+       el = strcspn(at, " \n\t\r\v\f>");
+       strbuf_reset(&mi->email);
+       strbuf_add(&mi->email, at, el);
+       strbuf_remove(&f, at - f.buf, el + (at[el] ? 1 : 0));
+
+       /* The remainder is name.  It could be
+        *
+        * - "John Doe <john.doe@xz>"                   (a), or
+        * - "john.doe@xz (John Doe)"                   (b), or
+        * - "John (zzz) Doe <john.doe@xz> (Comment)"   (c)
+        *
+        * but we have removed the email part, so
+        *
+        * - remove extra spaces which could stay after email (case 'c'), and
+        * - trim from both ends, possibly removing the () pair at the end
+        *   (cases 'a' and 'b').
+        */
+       cleanup_space(&f);
+       strbuf_trim(&f);
+       if (f.buf[0] == '(' && f.len && f.buf[f.len - 1] == ')') {
+               strbuf_remove(&f, 0, 1);
+               strbuf_setlen(&f, f.len - 1);
+       }
+
+       get_sane_name(&mi->name, &f, &mi->email);
+       strbuf_release(&f);
+}
+
+static void handle_header(struct strbuf **out, const struct strbuf *line)
+{
+       if (!*out) {
+               *out = xmalloc(sizeof(struct strbuf));
+               strbuf_init(*out, line->len);
+       } else
+               strbuf_reset(*out);
+
+       strbuf_addbuf(*out, line);
+}
+
+/* NOTE NOTE NOTE.  We do not claim we do full MIME.  We just attempt
+ * to have enough heuristics to grok MIME encoded patches often found
+ * on our mailing lists.  For example, we do not even treat header lines
+ * case insensitively.
+ */
+
+static int slurp_attr(const char *line, const char *name, struct strbuf *attr)
+{
+       const char *ends, *ap = strcasestr(line, name);
+       size_t sz;
+
+       strbuf_setlen(attr, 0);
+       if (!ap)
+               return 0;
+       ap += strlen(name);
+       if (*ap == '"') {
+               ap++;
+               ends = "\"";
+       }
+       else
+               ends = "; \t";
+       sz = strcspn(ap, ends);
+       strbuf_add(attr, ap, sz);
+       return 1;
+}
+
+static void handle_content_type(struct mailinfo *mi, struct strbuf *line)
+{
+       struct strbuf *boundary = xmalloc(sizeof(struct strbuf));
+       strbuf_init(boundary, line->len);
+
+       if (slurp_attr(line->buf, "boundary=", boundary)) {
+               strbuf_insert(boundary, 0, "--", 2);
+               if (++mi->content_top >= &mi->content[MAX_BOUNDARIES]) {
+                       error("Too many boundaries to handle");
+                       mi->input_error = -1;
+                       mi->content_top = &mi->content[MAX_BOUNDARIES] - 1;
+                       return;
+               }
+               *(mi->content_top) = boundary;
+               boundary = NULL;
+       }
+       slurp_attr(line->buf, "charset=", &mi->charset);
+
+       if (boundary) {
+               strbuf_release(boundary);
+               free(boundary);
+       }
+}
+
+static void handle_message_id(struct mailinfo *mi, const struct strbuf *line)
+{
+       if (mi->add_message_id)
+               mi->message_id = strdup(line->buf);
+}
+
+static void handle_content_transfer_encoding(struct mailinfo *mi,
+                                            const struct strbuf *line)
+{
+       if (strcasestr(line->buf, "base64"))
+               mi->transfer_encoding = TE_BASE64;
+       else if (strcasestr(line->buf, "quoted-printable"))
+               mi->transfer_encoding = TE_QP;
+       else
+               mi->transfer_encoding = TE_DONTCARE;
+}
+
+static int is_multipart_boundary(struct mailinfo *mi, const struct strbuf *line)
+{
+       struct strbuf *content_top = *(mi->content_top);
+
+       return ((content_top->len <= line->len) &&
+               !memcmp(line->buf, content_top->buf, content_top->len));
+}
+
+static void cleanup_subject(struct mailinfo *mi, struct strbuf *subject)
+{
+       size_t at = 0;
+
+       while (at < subject->len) {
+               char *pos;
+               size_t remove;
+
+               switch (subject->buf[at]) {
+               case 'r': case 'R':
+                       if (subject->len <= at + 3)
+                               break;
+                       if ((subject->buf[at + 1] == 'e' ||
+                            subject->buf[at + 1] == 'E') &&
+                           subject->buf[at + 2] == ':') {
+                               strbuf_remove(subject, at, 3);
+                               continue;
+                       }
+                       at++;
+                       break;
+               case ' ': case '\t': case ':':
+                       strbuf_remove(subject, at, 1);
+                       continue;
+               case '[':
+                       pos = strchr(subject->buf + at, ']');
+                       if (!pos)
+                               break;
+                       remove = pos - subject->buf + at + 1;
+                       if (!mi->keep_non_patch_brackets_in_subject ||
+                           (7 <= remove &&
+                            memmem(subject->buf + at, remove, "PATCH", 5)))
+                               strbuf_remove(subject, at, remove);
+                       else {
+                               at += remove;
+                               /*
+                                * If the input had a space after the ], keep
+                                * it.  We don't bother with finding the end of
+                                * the space, since we later normalize it
+                                * anyway.
+                                */
+                               if (isspace(subject->buf[at]))
+                                       at += 1;
+                       }
+                       continue;
+               }
+               break;
+       }
+       strbuf_trim(subject);
+}
+
+#define MAX_HDR_PARSED 10
+static const char *header[MAX_HDR_PARSED] = {
+       "From","Subject","Date",
+};
+
+static inline int cmp_header(const struct strbuf *line, const char *hdr)
+{
+       int len = strlen(hdr);
+       return !strncasecmp(line->buf, hdr, len) && line->len > len &&
+                       line->buf[len] == ':' && isspace(line->buf[len + 1]);
+}
+
+static int is_format_patch_separator(const char *line, int len)
+{
+       static const char SAMPLE[] =
+               "From e6807f3efca28b30decfecb1732a56c7db1137ee Mon Sep 17 00:00:00 2001\n";
+       const char *cp;
+
+       if (len != strlen(SAMPLE))
+               return 0;
+       if (!skip_prefix(line, "From ", &cp))
+               return 0;
+       if (strspn(cp, "0123456789abcdef") != 40)
+               return 0;
+       cp += 40;
+       return !memcmp(SAMPLE + (cp - line), cp, strlen(SAMPLE) - (cp - line));
+}
+
+static struct strbuf *decode_q_segment(const struct strbuf *q_seg, int rfc2047)
+{
+       const char *in = q_seg->buf;
+       int c;
+       struct strbuf *out = xmalloc(sizeof(struct strbuf));
+       strbuf_init(out, q_seg->len);
+
+       while ((c = *in++) != 0) {
+               if (c == '=') {
+                       int d = *in++;
+                       if (d == '\n' || !d)
+                               break; /* drop trailing newline */
+                       strbuf_addch(out, (hexval(d) << 4) | hexval(*in++));
+                       continue;
+               }
+               if (rfc2047 && c == '_') /* rfc2047 4.2 (2) */
+                       c = 0x20;
+               strbuf_addch(out, c);
+       }
+       return out;
+}
+
+static struct strbuf *decode_b_segment(const struct strbuf *b_seg)
+{
+       /* Decode in..ep, possibly in-place to ot */
+       int c, pos = 0, acc = 0;
+       const char *in = b_seg->buf;
+       struct strbuf *out = xmalloc(sizeof(struct strbuf));
+       strbuf_init(out, b_seg->len);
+
+       while ((c = *in++) != 0) {
+               if (c == '+')
+                       c = 62;
+               else if (c == '/')
+                       c = 63;
+               else if ('A' <= c && c <= 'Z')
+                       c -= 'A';
+               else if ('a' <= c && c <= 'z')
+                       c -= 'a' - 26;
+               else if ('0' <= c && c <= '9')
+                       c -= '0' - 52;
+               else
+                       continue; /* garbage */
+               switch (pos++) {
+               case 0:
+                       acc = (c << 2);
+                       break;
+               case 1:
+                       strbuf_addch(out, (acc | (c >> 4)));
+                       acc = (c & 15) << 4;
+                       break;
+               case 2:
+                       strbuf_addch(out, (acc | (c >> 2)));
+                       acc = (c & 3) << 6;
+                       break;
+               case 3:
+                       strbuf_addch(out, (acc | c));
+                       acc = pos = 0;
+                       break;
+               }
+       }
+       return out;
+}
+
+static int convert_to_utf8(struct mailinfo *mi,
+                          struct strbuf *line, const char *charset)
+{
+       char *out;
+
+       if (!mi->metainfo_charset || !charset || !*charset)
+               return 0;
+
+       if (same_encoding(mi->metainfo_charset, charset))
+               return 0;
+       out = reencode_string(line->buf, mi->metainfo_charset, charset);
+       if (!out) {
+               mi->input_error = -1;
+               return error("cannot convert from %s to %s",
+                            charset, mi->metainfo_charset);
+       }
+       strbuf_attach(line, out, strlen(out), strlen(out));
+       return 0;
+}
+
+static void decode_header(struct mailinfo *mi, struct strbuf *it)
+{
+       char *in, *ep, *cp;
+       struct strbuf outbuf = STRBUF_INIT, *dec;
+       struct strbuf charset_q = STRBUF_INIT, piecebuf = STRBUF_INIT;
+       int found_error = 1; /* pessimism */
+
+       in = it->buf;
+       while (in - it->buf <= it->len && (ep = strstr(in, "=?")) != NULL) {
+               int encoding;
+               strbuf_reset(&charset_q);
+               strbuf_reset(&piecebuf);
+
+               if (in != ep) {
+                       /*
+                        * We are about to process an encoded-word
+                        * that begins at ep, but there is something
+                        * before the encoded word.
+                        */
+                       char *scan;
+                       for (scan = in; scan < ep; scan++)
+                               if (!isspace(*scan))
+                                       break;
+
+                       if (scan != ep || in == it->buf) {
+                               /*
+                                * We should not lose that "something",
+                                * unless we have just processed an
+                                * encoded-word, and there is only LWS
+                                * before the one we are about to process.
+                                */
+                               strbuf_add(&outbuf, in, ep - in);
+                       }
+               }
+               /* E.g.
+                * ep : "=?iso-2022-jp?B?GyR...?= foo"
+                * ep : "=?ISO-8859-1?Q?Foo=FCbar?= baz"
+                */
+               ep += 2;
+
+               if (ep - it->buf >= it->len || !(cp = strchr(ep, '?')))
+                       goto release_return;
+
+               if (cp + 3 - it->buf > it->len)
+                       goto release_return;
+               strbuf_add(&charset_q, ep, cp - ep);
+
+               encoding = cp[1];
+               if (!encoding || cp[2] != '?')
+                       goto release_return;
+               ep = strstr(cp + 3, "?=");
+               if (!ep)
+                       goto release_return;
+               strbuf_add(&piecebuf, cp + 3, ep - cp - 3);
+               switch (tolower(encoding)) {
+               default:
+                       goto release_return;
+               case 'b':
+                       dec = decode_b_segment(&piecebuf);
+                       break;
+               case 'q':
+                       dec = decode_q_segment(&piecebuf, 1);
+                       break;
+               }
+               if (convert_to_utf8(mi, dec, charset_q.buf))
+                       goto release_return;
+
+               strbuf_addbuf(&outbuf, dec);
+               strbuf_release(dec);
+               free(dec);
+               in = ep + 2;
+       }
+       strbuf_addstr(&outbuf, in);
+       strbuf_reset(it);
+       strbuf_addbuf(it, &outbuf);
+       found_error = 0;
+release_return:
+       strbuf_release(&outbuf);
+       strbuf_release(&charset_q);
+       strbuf_release(&piecebuf);
+
+       if (found_error)
+               mi->input_error = -1;
+}
+
+static int check_header(struct mailinfo *mi,
+                       const struct strbuf *line,
+                       struct strbuf *hdr_data[], int overwrite)
+{
+       int i, ret = 0, len;
+       struct strbuf sb = STRBUF_INIT;
+
+       /* search for the interesting parts */
+       for (i = 0; header[i]; i++) {
+               int len = strlen(header[i]);
+               if ((!hdr_data[i] || overwrite) && cmp_header(line, header[i])) {
+                       /* Unwrap inline B and Q encoding, and optionally
+                        * normalize the meta information to utf8.
+                        */
+                       strbuf_add(&sb, line->buf + len + 2, line->len - len - 2);
+                       decode_header(mi, &sb);
+                       handle_header(&hdr_data[i], &sb);
+                       ret = 1;
+                       goto check_header_out;
+               }
+       }
+
+       /* Content stuff */
+       if (cmp_header(line, "Content-Type")) {
+               len = strlen("Content-Type: ");
+               strbuf_add(&sb, line->buf + len, line->len - len);
+               decode_header(mi, &sb);
+               strbuf_insert(&sb, 0, "Content-Type: ", len);
+               handle_content_type(mi, &sb);
+               ret = 1;
+               goto check_header_out;
+       }
+       if (cmp_header(line, "Content-Transfer-Encoding")) {
+               len = strlen("Content-Transfer-Encoding: ");
+               strbuf_add(&sb, line->buf + len, line->len - len);
+               decode_header(mi, &sb);
+               handle_content_transfer_encoding(mi, &sb);
+               ret = 1;
+               goto check_header_out;
+       }
+       if (cmp_header(line, "Message-Id")) {
+               len = strlen("Message-Id: ");
+               strbuf_add(&sb, line->buf + len, line->len - len);
+               decode_header(mi, &sb);
+               handle_message_id(mi, &sb);
+               ret = 1;
+               goto check_header_out;
+       }
+
+       /* for inbody stuff */
+       if (starts_with(line->buf, ">From") && isspace(line->buf[5])) {
+               ret = is_format_patch_separator(line->buf + 1, line->len - 1);
+               goto check_header_out;
+       }
+       if (starts_with(line->buf, "[PATCH]") && isspace(line->buf[7])) {
+               for (i = 0; header[i]; i++) {
+                       if (!strcmp("Subject", header[i])) {
+                               handle_header(&hdr_data[i], line);
+                               ret = 1;
+                               goto check_header_out;
+                       }
+               }
+       }
+
+check_header_out:
+       strbuf_release(&sb);
+       return ret;
+}
+
+static void decode_transfer_encoding(struct mailinfo *mi, struct strbuf *line)
+{
+       struct strbuf *ret;
+
+       switch (mi->transfer_encoding) {
+       case TE_QP:
+               ret = decode_q_segment(line, 0);
+               break;
+       case TE_BASE64:
+               ret = decode_b_segment(line);
+               break;
+       case TE_DONTCARE:
+       default:
+               return;
+       }
+       strbuf_reset(line);
+       strbuf_addbuf(line, ret);
+       strbuf_release(ret);
+       free(ret);
+}
+
+static inline int patchbreak(const struct strbuf *line)
+{
+       size_t i;
+
+       /* Beginning of a "diff -" header? */
+       if (starts_with(line->buf, "diff -"))
+               return 1;
+
+       /* CVS "Index: " line? */
+       if (starts_with(line->buf, "Index: "))
+               return 1;
+
+       /*
+        * "--- <filename>" starts patches without headers
+        * "---<sp>*" is a manual separator
+        */
+       if (line->len < 4)
+               return 0;
+
+       if (starts_with(line->buf, "---")) {
+               /* space followed by a filename? */
+               if (line->buf[3] == ' ' && !isspace(line->buf[4]))
+                       return 1;
+               /* Just whitespace? */
+               for (i = 3; i < line->len; i++) {
+                       unsigned char c = line->buf[i];
+                       if (c == '\n')
+                               return 1;
+                       if (!isspace(c))
+                               break;
+               }
+               return 0;
+       }
+       return 0;
+}
+
+static int is_scissors_line(const struct strbuf *line)
+{
+       size_t i, len = line->len;
+       int scissors = 0, gap = 0;
+       int first_nonblank = -1;
+       int last_nonblank = 0, visible, perforation = 0, in_perforation = 0;
+       const char *buf = line->buf;
+
+       for (i = 0; i < len; i++) {
+               if (isspace(buf[i])) {
+                       if (in_perforation) {
+                               perforation++;
+                               gap++;
+                       }
+                       continue;
+               }
+               last_nonblank = i;
+               if (first_nonblank < 0)
+                       first_nonblank = i;
+               if (buf[i] == '-') {
+                       in_perforation = 1;
+                       perforation++;
+                       continue;
+               }
+               if (i + 1 < len &&
+                   (!memcmp(buf + i, ">8", 2) || !memcmp(buf + i, "8<", 2) ||
+                    !memcmp(buf + i, ">%", 2) || !memcmp(buf + i, "%<", 2))) {
+                       in_perforation = 1;
+                       perforation += 2;
+                       scissors += 2;
+                       i++;
+                       continue;
+               }
+               in_perforation = 0;
+       }
+
+       /*
+        * The mark must be at least 8 bytes long (e.g. "-- >8 --").
+        * Even though there can be arbitrary cruft on the same line
+        * (e.g. "cut here"), in order to avoid misidentification, the
+        * perforation must occupy more than a third of the visible
+        * width of the line, and dashes and scissors must occupy more
+        * than half of the perforation.
+        */
+
+       visible = last_nonblank - first_nonblank + 1;
+       return (scissors && 8 <= visible &&
+               visible < perforation * 3 &&
+               gap * 2 < perforation);
+}
+
+static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
+{
+       assert(!mi->filter_stage);
+
+       if (mi->header_stage) {
+               if (!line->len || (line->len == 1 && line->buf[0] == '\n'))
+                       return 0;
+       }
+
+       if (mi->use_inbody_headers && mi->header_stage) {
+               mi->header_stage = check_header(mi, line, mi->s_hdr_data, 0);
+               if (mi->header_stage)
+                       return 0;
+       } else
+               /* Only trim the first (blank) line of the commit message
+                * when ignoring in-body headers.
+                */
+               mi->header_stage = 0;
+
+       /* normalize the log message to UTF-8. */
+       if (convert_to_utf8(mi, line, mi->charset.buf))
+               return 0; /* mi->input_error already set */
+
+       if (mi->use_scissors && is_scissors_line(line)) {
+               int i;
+
+               strbuf_setlen(&mi->log_message, 0);
+               mi->header_stage = 1;
+
+               /*
+                * We may have already read "secondary headers"; purge
+                * them to give ourselves a clean restart.
+                */
+               for (i = 0; header[i]; i++) {
+                       if (mi->s_hdr_data[i])
+                               strbuf_release(mi->s_hdr_data[i]);
+                       mi->s_hdr_data[i] = NULL;
+               }
+               return 0;
+       }
+
+       if (patchbreak(line)) {
+               if (mi->message_id)
+                       strbuf_addf(&mi->log_message,
+                                   "Message-Id: %s\n", mi->message_id);
+               return 1;
+       }
+
+       strbuf_addbuf(&mi->log_message, line);
+       return 0;
+}
+
+static void handle_patch(struct mailinfo *mi, const struct strbuf *line)
+{
+       fwrite(line->buf, 1, line->len, mi->patchfile);
+       mi->patch_lines++;
+}
+
+static void handle_filter(struct mailinfo *mi, struct strbuf *line)
+{
+       switch (mi->filter_stage) {
+       case 0:
+               if (!handle_commit_msg(mi, line))
+                       break;
+               mi->filter_stage++;
+       case 1:
+               handle_patch(mi, line);
+               break;
+       }
+}
+
+static int is_rfc2822_header(const struct strbuf *line)
+{
+       /*
+        * The section that defines the loosest possible
+        * field name is "3.6.8 Optional fields".
+        *
+        * optional-field = field-name ":" unstructured CRLF
+        * field-name = 1*ftext
+        * ftext = %d33-57 / %59-126
+        */
+       int ch;
+       char *cp = line->buf;
+
+       /* Count mbox From headers as headers */
+       if (starts_with(cp, "From ") || starts_with(cp, ">From "))
+               return 1;
+
+       while ((ch = *cp++)) {
+               if (ch == ':')
+                       return 1;
+               if ((33 <= ch && ch <= 57) ||
+                   (59 <= ch && ch <= 126))
+                       continue;
+               break;
+       }
+       return 0;
+}
+
+static int read_one_header_line(struct strbuf *line, FILE *in)
+{
+       struct strbuf continuation = STRBUF_INIT;
+
+       /* Get the first part of the line. */
+       if (strbuf_getline(line, in, '\n'))
+               return 0;
+
+       /*
+        * Is it an empty line or not a valid rfc2822 header?
+        * If so, stop here, and return false ("not a header")
+        */
+       strbuf_rtrim(line);
+       if (!line->len || !is_rfc2822_header(line)) {
+               /* Re-add the newline */
+               strbuf_addch(line, '\n');
+               return 0;
+       }
+
+       /*
+        * Now we need to eat all the continuation lines..
+        * Yuck, 2822 header "folding"
+        */
+       for (;;) {
+               int peek;
+
+               peek = fgetc(in); ungetc(peek, in);
+               if (peek != ' ' && peek != '\t')
+                       break;
+               if (strbuf_getline(&continuation, in, '\n'))
+                       break;
+               continuation.buf[0] = ' ';
+               strbuf_rtrim(&continuation);
+               strbuf_addbuf(line, &continuation);
+       }
+       strbuf_release(&continuation);
+
+       return 1;
+}
+
+static int find_boundary(struct mailinfo *mi, struct strbuf *line)
+{
+       while (!strbuf_getline(line, mi->input, '\n')) {
+               if (*(mi->content_top) && is_multipart_boundary(mi, line))
+                       return 1;
+       }
+       return 0;
+}
+
+static int handle_boundary(struct mailinfo *mi, struct strbuf *line)
+{
+       struct strbuf newline = STRBUF_INIT;
+
+       strbuf_addch(&newline, '\n');
+again:
+       if (line->len >= (*(mi->content_top))->len + 2 &&
+           !memcmp(line->buf + (*(mi->content_top))->len, "--", 2)) {
+               /* we hit an end boundary */
+               /* pop the current boundary off the stack */
+               strbuf_release(*(mi->content_top));
+               free(*(mi->content_top));
+               *(mi->content_top) = NULL;
+
+               /* technically won't happen as is_multipart_boundary()
+                  will fail first.  But just in case..
+                */
+               if (--mi->content_top < mi->content) {
+                       error("Detected mismatched boundaries, can't recover");
+                       mi->input_error = -1;
+                       mi->content_top = mi->content;
+                       return 0;
+               }
+               handle_filter(mi, &newline);
+               strbuf_release(&newline);
+               if (mi->input_error)
+                       return 0;
+
+               /* skip to the next boundary */
+               if (!find_boundary(mi, line))
+                       return 0;
+               goto again;
+       }
+
+       /* set some defaults */
+       mi->transfer_encoding = TE_DONTCARE;
+       strbuf_reset(&mi->charset);
+
+       /* slurp in this section's info */
+       while (read_one_header_line(line, mi->input))
+               check_header(mi, line, mi->p_hdr_data, 0);
+
+       strbuf_release(&newline);
+       /* replenish line */
+       if (strbuf_getline(line, mi->input, '\n'))
+               return 0;
+       strbuf_addch(line, '\n');
+       return 1;
+}
+
+static void handle_body(struct mailinfo *mi, struct strbuf *line)
+{
+       struct strbuf prev = STRBUF_INIT;
+
+       /* Skip up to the first boundary */
+       if (*(mi->content_top)) {
+               if (!find_boundary(mi, line))
+                       goto handle_body_out;
+       }
+
+       do {
+               /* process any boundary lines */
+               if (*(mi->content_top) && is_multipart_boundary(mi, line)) {
+                       /* flush any leftover */
+                       if (prev.len) {
+                               handle_filter(mi, &prev);
+                               strbuf_reset(&prev);
+                       }
+                       if (!handle_boundary(mi, line))
+                               goto handle_body_out;
+               }
+
+               /* Unwrap transfer encoding */
+               decode_transfer_encoding(mi, line);
+
+               switch (mi->transfer_encoding) {
+               case TE_BASE64:
+               case TE_QP:
+               {
+                       struct strbuf **lines, **it, *sb;
+
+                       /* Prepend any previous partial lines */
+                       strbuf_insert(line, 0, prev.buf, prev.len);
+                       strbuf_reset(&prev);
+
+                       /*
+                        * This is a decoded line that may contain
+                        * multiple new lines.  Pass only one chunk
+                        * at a time to handle_filter()
+                        */
+                       lines = strbuf_split(line, '\n');
+                       for (it = lines; (sb = *it); it++) {
+                               if (*(it + 1) == NULL) /* The last line */
+                                       if (sb->buf[sb->len - 1] != '\n') {
+                                               /* Partial line, save it for later. */
+                                               strbuf_addbuf(&prev, sb);
+                                               break;
+                                       }
+                               handle_filter(mi, sb);
+                       }
+                       /*
+                        * The partial chunk is saved in "prev" and will be
+                        * appended by the next iteration of read_line_with_nul().
+                        */
+                       strbuf_list_free(lines);
+                       break;
+               }
+               default:
+                       handle_filter(mi, line);
+               }
+
+               if (mi->input_error)
+                       break;
+       } while (!strbuf_getwholeline(line, mi->input, '\n'));
+
+handle_body_out:
+       strbuf_release(&prev);
+}
+
+static void output_header_lines(FILE *fout, const char *hdr, const struct strbuf *data)
+{
+       const char *sp = data->buf;
+       while (1) {
+               char *ep = strchr(sp, '\n');
+               int len;
+               if (!ep)
+                       len = strlen(sp);
+               else
+                       len = ep - sp;
+               fprintf(fout, "%s: %.*s\n", hdr, len, sp);
+               if (!ep)
+                       break;
+               sp = ep + 1;
+       }
+}
+
+static void handle_info(struct mailinfo *mi)
+{
+       struct strbuf *hdr;
+       int i;
+
+       for (i = 0; header[i]; i++) {
+               /* only print inbody headers if we output a patch file */
+               if (mi->patch_lines && mi->s_hdr_data[i])
+                       hdr = mi->s_hdr_data[i];
+               else if (mi->p_hdr_data[i])
+                       hdr = mi->p_hdr_data[i];
+               else
+                       continue;
+
+               if (!strcmp(header[i], "Subject")) {
+                       if (!mi->keep_subject) {
+                               cleanup_subject(mi, hdr);
+                               cleanup_space(hdr);
+                       }
+                       output_header_lines(mi->output, "Subject", hdr);
+               } else if (!strcmp(header[i], "From")) {
+                       cleanup_space(hdr);
+                       handle_from(mi, hdr);
+                       fprintf(mi->output, "Author: %s\n", mi->name.buf);
+                       fprintf(mi->output, "Email: %s\n", mi->email.buf);
+               } else {
+                       cleanup_space(hdr);
+                       fprintf(mi->output, "%s: %s\n", header[i], hdr->buf);
+               }
+       }
+       fprintf(mi->output, "\n");
+}
+
+int mailinfo(struct mailinfo *mi, const char *msg, const char *patch)
+{
+       FILE *cmitmsg;
+       int peek;
+       struct strbuf line = STRBUF_INIT;
+
+       cmitmsg = fopen(msg, "w");
+       if (!cmitmsg) {
+               perror(msg);
+               return -1;
+       }
+       mi->patchfile = fopen(patch, "w");
+       if (!mi->patchfile) {
+               perror(patch);
+               fclose(cmitmsg);
+               return -1;
+       }
+
+       mi->p_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(*(mi->p_hdr_data)));
+       mi->s_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(*(mi->s_hdr_data)));
+
+       do {
+               peek = fgetc(mi->input);
+       } while (isspace(peek));
+       ungetc(peek, mi->input);
+
+       /* process the email header */
+       while (read_one_header_line(&line, mi->input))
+               check_header(mi, &line, mi->p_hdr_data, 1);
+
+       handle_body(mi, &line);
+       fwrite(mi->log_message.buf, 1, mi->log_message.len, cmitmsg);
+       fclose(cmitmsg);
+       fclose(mi->patchfile);
+
+       handle_info(mi);
+       strbuf_release(&line);
+       return mi->input_error;
+}
+
+static int git_mailinfo_config(const char *var, const char *value, void *mi_)
+{
+       struct mailinfo *mi = mi_;
+
+       if (!starts_with(var, "mailinfo."))
+               return git_default_config(var, value, NULL);
+       if (!strcmp(var, "mailinfo.scissors")) {
+               mi->use_scissors = git_config_bool(var, value);
+               return 0;
+       }
+       /* perhaps others here */
+       return 0;
+}
+
+void setup_mailinfo(struct mailinfo *mi)
+{
+       memset(mi, 0, sizeof(*mi));
+       strbuf_init(&mi->name, 0);
+       strbuf_init(&mi->email, 0);
+       strbuf_init(&mi->charset, 0);
+       strbuf_init(&mi->log_message, 0);
+       mi->header_stage = 1;
+       mi->use_inbody_headers = 1;
+       mi->content_top = mi->content;
+       git_config(git_mailinfo_config, &mi);
+}
+
+void clear_mailinfo(struct mailinfo *mi)
+{
+       int i;
+
+       strbuf_release(&mi->name);
+       strbuf_release(&mi->email);
+       strbuf_release(&mi->charset);
+       free(mi->message_id);
+
+       for (i = 0; mi->p_hdr_data[i]; i++)
+               strbuf_release(mi->p_hdr_data[i]);
+       free(mi->p_hdr_data);
+       for (i = 0; mi->s_hdr_data[i]; i++)
+               strbuf_release(mi->s_hdr_data[i]);
+       free(mi->s_hdr_data);
+
+       while (mi->content < mi->content_top) {
+               free(*(mi->content_top));
+               mi->content_top--;
+       }
+
+       strbuf_release(&mi->log_message);
+}
diff --git a/mailinfo.h b/mailinfo.h
new file mode 100644 (file)
index 0000000..93776a7
--- /dev/null
@@ -0,0 +1,41 @@
+#ifndef MAILINFO_H
+#define MAILINFO_H
+
+#define MAX_BOUNDARIES 5
+
+struct mailinfo {
+       FILE *input;
+       FILE *output;
+       FILE *patchfile;
+
+       struct strbuf name;
+       struct strbuf email;
+       int keep_subject;
+       int keep_non_patch_brackets_in_subject;
+       int add_message_id;
+       int use_scissors;
+       int use_inbody_headers;
+       const char *metainfo_charset;
+
+       struct strbuf *content[MAX_BOUNDARIES];
+       struct strbuf **content_top;
+       struct strbuf charset;
+       char *message_id;
+       enum  {
+               TE_DONTCARE, TE_QP, TE_BASE64
+       } transfer_encoding;
+       int patch_lines;
+       int filter_stage; /* still reading log or are we copying patch? */
+       int header_stage; /* still checking in-body headers? */
+       struct strbuf **p_hdr_data;
+       struct strbuf **s_hdr_data;
+
+       struct strbuf log_message;
+       int input_error;
+};
+
+extern void setup_mailinfo(struct mailinfo *);
+extern int mailinfo(struct mailinfo *, const char *msg, const char *patch);
+extern void clear_mailinfo(struct mailinfo *);
+
+#endif /* MAILINFO_H */
index 702cd0518fca67a84417de8268ed70dfa29392e4..332ba956e7edec6c6fbe10f346ec08fc469b846f 100644 (file)
 struct dir_entry {
        struct hashmap_entry ent;
        struct dir_entry *parent;
-       struct cache_entry *ce;
        int nr;
        unsigned int namelen;
+       char name[FLEX_ARRAY];
 };
 
 static int dir_entry_cmp(const struct dir_entry *e1,
                const struct dir_entry *e2, const char *name)
 {
-       return e1->namelen != e2->namelen || strncasecmp(e1->ce->name,
-                       name ? name : e2->ce->name, e1->namelen);
+       return e1->namelen != e2->namelen || strncasecmp(e1->name,
+                       name ? name : e2->name, e1->namelen);
 }
 
 static struct dir_entry *find_dir_entry(struct index_state *istate,
@@ -41,14 +41,6 @@ static struct dir_entry *hash_dir_entry(struct index_state *istate,
         * closing slash.  Despite submodules being a directory, they never
         * reach this point, because they are stored
         * in index_state.name_hash (as ordinary cache_entries).
-        *
-        * Note that the cache_entry stored with the dir_entry merely
-        * supplies the name of the directory (up to dir_entry.namelen). We
-        * track the number of 'active' files in a directory in dir_entry.nr,
-        * so we can tell if the directory is still relevant, e.g. for git
-        * status. However, if cache_entries are removed, we cannot pinpoint
-        * an exact cache_entry that's still active. It is very possible that
-        * multiple dir_entries point to the same cache_entry.
         */
        struct dir_entry *dir;
 
@@ -63,10 +55,10 @@ static struct dir_entry *hash_dir_entry(struct index_state *istate,
        dir = find_dir_entry(istate, ce->name, namelen);
        if (!dir) {
                /* not found, create it and add to hash table */
-               dir = xcalloc(1, sizeof(struct dir_entry));
+               dir = xcalloc(1, sizeof(struct dir_entry) + namelen + 1);
                hashmap_entry_init(dir, memihash(ce->name, namelen));
                dir->namelen = namelen;
-               dir->ce = ce;
+               strncpy(dir->name, ce->name, namelen);
                hashmap_add(&istate->dir_hash, dir);
 
                /* recursively add missing parent directories */
@@ -188,26 +180,36 @@ static int same_name(const struct cache_entry *ce, const char *name, int namelen
        return slow_same_name(name, namelen, ce->name, len);
 }
 
-struct cache_entry *index_dir_exists(struct index_state *istate, const char *name, int namelen)
+int index_dir_exists(struct index_state *istate, const char *name, int namelen)
 {
-       struct cache_entry *ce;
        struct dir_entry *dir;
 
        lazy_init_name_hash(istate);
        dir = find_dir_entry(istate, name, namelen);
-       if (dir && dir->nr)
-               return dir->ce;
+       return dir && dir->nr;
+}
 
-       /*
-        * It might be a submodule. Unlike plain directories, which are stored
-        * in the dir-hash, submodules are stored in the name-hash, so check
-        * there, as well.
-        */
-       ce = index_file_exists(istate, name, namelen, 1);
-       if (ce && S_ISGITLINK(ce->ce_mode))
-               return ce;
+void adjust_dirname_case(struct index_state *istate, char *name)
+{
+       const char *startPtr = name;
+       const char *ptr = startPtr;
 
-       return NULL;
+       lazy_init_name_hash(istate);
+       while (*ptr) {
+               while (*ptr && *ptr != '/')
+                       ptr++;
+
+               if (*ptr == '/') {
+                       struct dir_entry *dir;
+
+                       ptr++;
+                       dir = find_dir_entry(istate, name, ptr - name + 1);
+                       if (dir) {
+                               memcpy((void *)startPtr, dir->name + (startPtr - name), ptr - startPtr);
+                               startPtr = ptr;
+                       }
+               }
+       }
 }
 
 struct cache_entry *index_file_exists(struct index_state *istate, const char *name, int namelen, int icase)
index 87204a50a5cfe2acbdb0563a68f88303a5eb7ae7..84616c8e219bf3cf902643a8da62a2060d335c36 100644 (file)
@@ -17,7 +17,6 @@
 #include "strbuf.h"
 #include "varint.h"
 #include "split-index.h"
-#include "sigchain.h"
 #include "utf8.h"
 
 static struct cache_entry *refresh_cache_entry(struct cache_entry *ce,
@@ -679,21 +678,7 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
         * entry's directory case.
         */
        if (ignore_case) {
-               const char *startPtr = ce->name;
-               const char *ptr = startPtr;
-               while (*ptr) {
-                       while (*ptr && *ptr != '/')
-                               ++ptr;
-                       if (*ptr == '/') {
-                               struct cache_entry *foundce;
-                               ++ptr;
-                               foundce = index_dir_exists(istate, ce->name, ptr - ce->name - 1);
-                               if (foundce) {
-                                       memcpy((void *)startPtr, foundce->name + (startPtr - ce->name), ptr - startPtr);
-                                       startPtr = ptr;
-                               }
-                       }
-               }
+               adjust_dirname_case(istate, ce->name);
        }
 
        alias = index_file_exists(istate, ce->name, ce_namelen(ce), ignore_case);
index 1101f82eaf34b936d51e0d61837c568b3691a468..fb161530cdd5e286716d45532c1d58da26250e22 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -282,6 +282,7 @@ static void read_branches_file(struct remote *remote)
                return;
 
        strbuf_getline(&buf, f, '\n');
+       fclose(f);
        strbuf_trim(&buf);
        if (!buf.len) {
                strbuf_release(&buf);
diff --git a/setup.c b/setup.c
index b2644716911c3ee9594ca1e72d5fb30900d9331e..d34372520b05a2acee36f02ddc2ef68dde4b65fd 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -5,6 +5,7 @@
 static int inside_git_dir = -1;
 static int inside_work_tree = -1;
 static int work_tree_config_is_bogus;
+static struct string_list unknown_extensions = STRING_LIST_INIT_DUP;
 
 /*
  * The input parameter must contain an absolute path, and it must already be
@@ -356,10 +357,25 @@ void setup_work_tree(void)
 
 static int check_repo_format(const char *var, const char *value, void *cb)
 {
+       const char *ext;
+
        if (strcmp(var, "core.repositoryformatversion") == 0)
                repository_format_version = git_config_int(var, value);
        else if (strcmp(var, "core.sharedrepository") == 0)
                shared_repository = git_config_perm(var, value);
+       else if (skip_prefix(var, "extensions.", &ext)) {
+               /*
+                * record any known extensions here; otherwise,
+                * we fall through to recording it as unknown, and
+                * check_repository_format will complain
+                */
+               if (!strcmp(ext, "noop"))
+                       ;
+               else if (!strcmp(ext, "preciousobjects"))
+                       repository_format_precious_objects = git_config_bool(var, value);
+               else
+                       string_list_append(&unknown_extensions, ext);
+       }
        return 0;
 }
 
@@ -370,6 +386,8 @@ static int check_repository_format_gently(const char *gitdir, int *nongit_ok)
        config_fn_t fn;
        int ret = 0;
 
+       string_list_clear(&unknown_extensions, 0);
+
        if (get_common_dir(&sb, gitdir))
                fn = check_repo_format;
        else
@@ -387,16 +405,31 @@ static int check_repository_format_gently(const char *gitdir, int *nongit_ok)
         * is a good one.
         */
        git_config_early(fn, NULL, repo_config);
-       if (GIT_REPO_VERSION < repository_format_version) {
+       if (GIT_REPO_VERSION_READ < repository_format_version) {
                if (!nongit_ok)
                        die ("Expected git repo version <= %d, found %d",
-                            GIT_REPO_VERSION, repository_format_version);
+                            GIT_REPO_VERSION_READ, repository_format_version);
                warning("Expected git repo version <= %d, found %d",
-                       GIT_REPO_VERSION, repository_format_version);
+                       GIT_REPO_VERSION_READ, repository_format_version);
                warning("Please upgrade Git");
                *nongit_ok = -1;
                ret = -1;
        }
+
+       if (repository_format_version >= 1 && unknown_extensions.nr) {
+               int i;
+
+               if (!nongit_ok)
+                       die("unknown repository extension: %s",
+                           unknown_extensions.items[0].string);
+
+               for (i = 0; i < unknown_extensions.nr; i++)
+                       warning("unknown repository extension: %s",
+                               unknown_extensions.items[i].string);
+               *nongit_ok = -1;
+               ret = -1;
+       }
+
        strbuf_release(&sb);
        return ret;
 }
index d49a3d6e9f02e292a04981e34f0a2d0b1ffa00d0..4f9d66723670664fd8a15e271ed0cfc309324059 100644 (file)
--- a/shallow.c
+++ b/shallow.c
@@ -10,7 +10,6 @@
 #include "diff.h"
 #include "revision.h"
 #include "commit-slab.h"
-#include "sigchain.h"
 
 static int is_shallow = -1;
 static struct stat_validity shallow_stat;
index 5a9eed7fd858b6f2e454421d68e884a21acb7a23..d9e4903fed24f5916962f8d315eb680f9b4013ff 100644 (file)
@@ -2,7 +2,7 @@
 #include "pack.h"
 
 static const char show_index_usage[] =
-"git show-index < <packed archive index>";
+"git show-index";
 
 int main(int argc, char **argv)
 {
index 107c45d29111a011a04daaa8fd0323ed9d973398..d76f0aed85c4ec6eafdcfd1a8ee2b22d3d20df96 100644 (file)
--- a/strbuf.c
+++ b/strbuf.c
@@ -752,3 +752,69 @@ void strbuf_add_unique_abbrev(struct strbuf *sb, const unsigned char *sha1,
        r = find_unique_abbrev_r(sb->buf + sb->len, sha1, abbrev_len);
        strbuf_setlen(sb, sb->len + r);
 }
+
+/*
+ * Returns the length of a line, without trailing spaces.
+ *
+ * If the line ends with newline, it will be removed too.
+ */
+static size_t cleanup(char *line, size_t len)
+{
+       while (len) {
+               unsigned char c = line[len - 1];
+               if (!isspace(c))
+                       break;
+               len--;
+       }
+
+       return len;
+}
+
+/*
+ * Remove empty lines from the beginning and end
+ * and also trailing spaces from every line.
+ *
+ * Turn multiple consecutive empty lines between paragraphs
+ * into just one empty line.
+ *
+ * If the input has only empty lines and spaces,
+ * no output will be produced.
+ *
+ * If last line does not have a newline at the end, one is added.
+ *
+ * Enable skip_comments to skip every line starting with comment
+ * character.
+ */
+void strbuf_stripspace(struct strbuf *sb, int skip_comments)
+{
+       int empties = 0;
+       size_t i, j, len, newlen;
+       char *eol;
+
+       /* We may have to add a newline. */
+       strbuf_grow(sb, 1);
+
+       for (i = j = 0; i < sb->len; i += len, j += newlen) {
+               eol = memchr(sb->buf + i, '\n', sb->len - i);
+               len = eol ? eol - (sb->buf + i) + 1 : sb->len - i;
+
+               if (skip_comments && len && sb->buf[i] == comment_line_char) {
+                       newlen = 0;
+                       continue;
+               }
+               newlen = cleanup(sb->buf + i, len);
+
+               /* Not just an empty line? */
+               if (newlen) {
+                       if (empties > 0 && j > 0)
+                               sb->buf[j++] = '\n';
+                       empties = 0;
+                       memmove(sb->buf + j, sb->buf + i, newlen);
+                       sb->buf[newlen + j++] = '\n';
+               } else {
+                       empties++;
+               }
+       }
+
+       strbuf_setlen(sb, j);
+}
index 0f9c8a72ba7cdb7adb34c96a8e04db776c18d743..7123fca7aff5182b66af6d76991552081139467c 100644 (file)
--- a/strbuf.h
+++ b/strbuf.h
@@ -418,7 +418,16 @@ extern void strbuf_add_absolute_path(struct strbuf *sb, const char *path);
  * Strip whitespace from a buffer. The second parameter controls if
  * comments are considered contents to be removed or not.
  */
-extern void stripspace(struct strbuf *buf, int skip_comments);
+extern void strbuf_stripspace(struct strbuf *buf, int skip_comments);
+
+/**
+ * Temporary alias until all topic branches have switched to use
+ * strbuf_stripspace directly.
+ */
+static inline void stripspace(struct strbuf *buf, int skip_comments)
+{
+       strbuf_stripspace(buf, skip_comments);
+}
 
 static inline int strbuf_strip_suffix(struct strbuf *sb, const char *suffix)
 {
index 0d9388afc4e20a5c536d214bfe25e630242ed667..9bcd34969f56038d3933471bc32641a277d413ba 100755 (executable)
@@ -67,4 +67,64 @@ test_expect_success 'gitdir required mode' '
        )
 '
 
+check_allow () {
+       git rev-parse --git-dir >actual &&
+       echo .git >expect &&
+       test_cmp expect actual
+}
+
+check_abort () {
+       test_must_fail git rev-parse --git-dir
+}
+
+# avoid git-config, since it cannot be trusted to run
+# in a repository with a broken version
+mkconfig () {
+       echo '[core]' &&
+       echo "repositoryformatversion = $1" &&
+       shift &&
+
+       if test $# -gt 0; then
+               echo '[extensions]' &&
+               for i in "$@"; do
+                       echo "$i"
+               done
+       fi
+}
+
+while read outcome version extensions; do
+       test_expect_success "$outcome version=$version $extensions" "
+               mkconfig $version $extensions >.git/config &&
+               check_${outcome}
+       "
+done <<\EOF
+allow 0
+allow 1
+allow 1 noop
+abort 1 no-such-extension
+allow 0 no-such-extension
+EOF
+
+test_expect_success 'precious-objects allowed' '
+       mkconfig 1 preciousObjects >.git/config &&
+       check_allow
+'
+
+test_expect_success 'precious-objects blocks destructive repack' '
+       test_must_fail git repack -ad
+'
+
+test_expect_success 'other repacks are OK' '
+       test_commit foo &&
+       git repack
+'
+
+test_expect_success 'precious-objects blocks prune' '
+       test_must_fail git prune
+'
+
+test_expect_success 'gc runs without complaint' '
+       git gc
+'
+
 test_done
diff --git a/t/t2027-worktree-list.sh b/t/t2027-worktree-list.sh
new file mode 100755 (executable)
index 0000000..75ebb1b
--- /dev/null
@@ -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
index 37a24c131240c46d0694e0a148ec7c15b89e17fe..0e8d0d42f2f5b1edbc18d46da77c956cd99dadec 100755 (executable)
@@ -412,7 +412,9 @@ test_expect_success 'create/modify files, some of which are gitignored' '
        echo two bis >done/two &&
        echo three >done/three && # three is gitignored
        echo four >done/four && # four is gitignored at a higher level
-       echo five >done/five # five is not gitignored
+       echo five >done/five && # five is not gitignored
+       echo test >base && #we need to ensure that the root dir is touched
+       rm base
 '
 
 test_expect_success 'test sparse status with untracked cache' '
diff --git a/worktree.c b/worktree.c
new file mode 100644 (file)
index 0000000..981f810
--- /dev/null
@@ -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 (file)
index 0000000..b4b3dda
--- /dev/null
@@ -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