Merge branch 'sb/cd-then-git-can-be-written-as-git-c'
authorJunio C Hamano <gitster@pobox.com>
Mon, 23 Jan 2017 23:59:22 +0000 (15:59 -0800)
committerJunio C Hamano <gitster@pobox.com>
Mon, 23 Jan 2017 23:59:22 +0000 (15:59 -0800)
Test clean-up.

* sb/cd-then-git-can-be-written-as-git-c:
lib-submodule-update.sh: reduce use of subshell by using "git -C"

101 files changed:
.mailmap
Documentation/Makefile
Documentation/RelNotes/2.11.1.txt [new file with mode: 0644]
Documentation/RelNotes/2.12.0.txt
Documentation/config.txt
Documentation/diff-config.txt
Documentation/diff-options.txt
Documentation/git-bisect.txt
Documentation/git-grep.txt
Documentation/git-gui.txt
Documentation/git-p4.txt
Documentation/git-rev-parse.txt
Documentation/git-tag.txt
Documentation/git.txt
Documentation/giteveryday.txt
Documentation/gitk.txt
Documentation/technical/api-setup.txt
Documentation/user-manual.txt
Makefile
abspath.c
archive-zip.c
builtin/blame.c
builtin/gc.c
builtin/grep.c
builtin/init-db.c
builtin/ls-tree.c
builtin/mv.c
builtin/repack.c
builtin/rm.c
builtin/submodule--helper.c
cache.h
config.c
contrib/gitview/gitview [deleted file]
contrib/gitview/gitview.txt [deleted file]
diff.c
dir.c
environment.c
git-mergetool.sh
git-p4.py
git-rebase--interactive.sh
git-submodule.sh
git.c
gitk-git/Makefile
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/pt_pt.po [new file with mode: 0644]
gitk-git/po/ru.po
gitk-git/po/sv.po
gitk-git/po/vi.po
grep.c
grep.h
pathspec.c
pathspec.h
read-cache.c
remote.c
run-command.c
run-command.h
setup.c
sha1_file.c
submodule-config.c
submodule-config.h
submodule.c
submodule.h
t/t1514-rev-parse-push.sh
t/t3510-cherry-pick-sequence.sh
t/t3600-rm.sh
t/t4032-diff-inter-hunk-context.sh
t/t5003-archive-zip.sh
t/t5310-pack-bitmaps.sh
t/t5504-fetch-receive-strict.sh
t/t5516-fetch-push.sh
t/t5580-clone-push-unc.sh [new file with mode: 0755]
t/t5601-clone.sh
t/t6030-bisect-porcelain.sh
t/t6134-pathspec-in-submodule.sh [new file with mode: 0755]
t/t6500-gc.sh
t/t7004-tag.sh
t/t7406-submodule-update.sh
t/t7411-submodule-config.sh
t/t7610-mergetool.sh
t/t7810-grep.sh
t/t7814-grep-recurse-submodules.sh [new file with mode: 0755]
t/t8002-blame.sh
t/t8011-blame-split-file.sh [new file with mode: 0755]
t/t9001-send-email.sh
t/t9117-git-svn-init-clone.sh
t/t9813-git-p4-preserve-users.sh
t/t9814-git-p4-rename.sh
transport.c
tree-walk.c
unpack-trees.c
versioncmp.c
worktree.c
index 9cc33e925de8adc562daf9b176135aba7b5e4d9b..9c87a3840b240e9ac356eed1039b000b391ef19c 100644 (file)
--- a/.mailmap
+++ b/.mailmap
@@ -192,6 +192,8 @@ Philippe Bruhat <book@cpan.org>
 Ralf Thielow <ralf.thielow@gmail.com> <ralf.thielow@googlemail.com>
 Ramsay Jones <ramsay@ramsayjones.plus.com> <ramsay@ramsay1.demon.co.uk>
 René Scharfe <l.s.r@web.de> <rene.scharfe@lsrfire.ath.cx>
+Richard Hansen <rhansen@rhansen.org> <hansenr@google.com>
+Richard Hansen <rhansen@rhansen.org> <rhansen@bbn.com>
 Robert Fitzsimons <robfitz@273k.net>
 Robert Shearman <robertshearman@gmail.com> <rob@codeweavers.com>
 Robert Zeh <robert.a.zeh@gmail.com>
index b43d66eae6e154b229aa1477b8454ce2918ba8b9..a9fb497b837dd39673ab7105774ad61ecbf67a6f 100644 (file)
@@ -337,7 +337,7 @@ manpage-base-url.xsl: manpage-base-url.xsl.in
 
 user-manual.xml: user-manual.txt user-manual.conf
        $(QUIET_ASCIIDOC)$(RM) $@+ $@ && \
-       $(TXT_TO_XML) -d article -o $@+ $< && \
+       $(TXT_TO_XML) -d book -o $@+ $< && \
        mv $@+ $@
 
 technical/api-index.txt: technical/api-index-skel.txt \
diff --git a/Documentation/RelNotes/2.11.1.txt b/Documentation/RelNotes/2.11.1.txt
new file mode 100644 (file)
index 0000000..74b193f
--- /dev/null
@@ -0,0 +1,120 @@
+Git v2.11.1 Release Notes
+=========================
+
+Fixes since v2.11
+-----------------
+
+ * The default Travis-CI configuration specifies newer P4 and GitLFS.
+
+ * The character width table has been updated to match Unicode 9.0
+
+ * Update the isatty() emulation for Windows by updating the previous
+   hack that depended on internals of (older) MSVC runtime.
+
+ * "git rev-parse --symbolic" failed with a more recent notation like
+   "HEAD^-1" and "HEAD^!".
+
+ * An empty directory in a working tree that can simply be nuked used
+   to interfere while merging or cherry-picking a change to create a
+   submodule directory there, which has been fixed..
+
+ * The code in "git push" to compute if any commit being pushed in the
+   superproject binds a commit in a submodule that hasn't been pushed
+   out was overly inefficient, making it unusable even for a small
+   project that does not have any submodule but have a reasonable
+   number of refs.
+
+ * "git push --dry-run --recurse-submodule=on-demand" wasn't
+   "--dry-run" in the submodules.
+
+ * The output from "git worktree list" was made in readdir() order,
+   and was unstable.
+
+ * mergetool.<tool>.trustExitCode configuration variable did not apply
+   to built-in tools, but now it does.
+
+ * "git p4" LFS support was broken when LFS stores an empty blob.
+
+ * Fix a corner case in merge-recursive regression that crept in
+   during 2.10 development cycle.
+
+ * Update the error messages from the dumb-http client when it fails
+   to obtain loose objects; we used to give sensible error message
+   only upon 404 but we now forbid unexpected redirects that needs to
+   be reported with something sensible.
+
+ * When diff.renames configuration is on (and with Git 2.9 and later,
+   it is enabled by default, which made it worse), "git stash"
+   misbehaved if a file is removed and another file with a very
+   similar content is added.
+
+ * "git diff --no-index" did not take "--no-abbrev" option.
+
+ * "git difftool --dir-diff" had a minor regression when started from
+   a subdirectory, which has been fixed.
+
+ * "git commit --allow-empty --only" (no pathspec) with dirty index
+   ought to be an acceptable way to create a new commit that does not
+   change any paths, but it was forbidden, perhaps because nobody
+   needed it so far.
+
+ * A pathname that begins with "//" or "\\" on Windows is special but
+   path normalization logic was unaware of it.
+
+ * "git pull --rebase", when there is no new commits on our side since
+   we forked from the upstream, should be able to fast-forward without
+   invoking "git rebase", but it didn't.
+
+ * The way to specify hotkeys to "xxdiff" that is used by "git
+   mergetool" has been modernized to match recent versions of xxdiff.
+
+ * Unlike "git am --abort", "git cherry-pick --abort" moved HEAD back
+   to where cherry-pick started while picking multiple changes, when
+   the cherry-pick stopped to ask for help from the user, and the user
+   did "git reset --hard" to a different commit in order to re-attempt
+   the operation.
+
+ * Code cleanup in shallow boundary computation.
+
+ * A recent update to receive-pack to make it easier to drop garbage
+   objects made it clear that GIT_ALTERNATE_OBJECT_DIRECTORIES cannot
+   have a pathname with a colon in it (no surprise!), and this in turn
+   made it impossible to push into a repository at such a path.  This
+   has been fixed by introducing a quoting mechanism used when
+   appending such a path to the colon-separated list.
+
+ * The function usage_msg_opt() has been updated to say "fatal:"
+   before the custom message programs give, when they want to die
+   with a message about wrong command line options followed by the
+   standard usage string.
+
+ * "git index-pack --stdin" needs an access to an existing repository,
+   but "git index-pack file.pack" to generate an .idx file that
+   corresponds to a packfile does not.
+
+ * Fix for NDEBUG builds.
+
+ * A lazy "git push" without refspec did not internally use a fully
+   specified refspec to perform 'current', 'simple', or 'upstream'
+   push, causing unnecessary "ambiguous ref" errors.
+
+ * "git p4" misbehaved when swapping a directory and a symbolic link.
+
+ * Even though an fix was attempted in Git 2.9.3 days, but running
+   "git difftool --dir-diff" from a subdirectory never worked. This
+   has been fixed.
+
+ * "git p4" that tracks multile p4 paths imported a single changelist
+   that touches files in these multiple paths as one commit, followed
+   by many empty commits.  This has been fixed.
+
+ * A potential but unlikely buffer overflow in Windows port has been
+   fixed.
+
+ * When the http server gives an incomplete response to a smart-http
+   rpc call, it could lead to client waiting for a full response that
+   will never come.  Teach the client side to notice this condition
+   and abort the transfer.
+
+
+Also contains various documentation updates and code clean-ups.
index 2a19064f6e4b1e5484d9dbc1dc7e4f527a13c726..efff5264d883ad14ab036dbc7cc04f8d48e8d71e 100644 (file)
@@ -66,6 +66,17 @@ UI, Workflows & Features
    more widely known when conversion fails from/to it.
    (merge df3755888b jc/latin-1 later to maint).
 
+ * "git grep" has been taught to optionally recurse into submodules.
+
+ * "git rm" used to refuse to remove a submodule when it has its own
+   git repository embedded in its working tree.  It learned to move
+   the repository away to $GIT_DIR/modules/ of the superproject
+   instead, and allow the submodule to be deleted (as long as there
+   will be no loss of local modifications, that is).
+
+ * A recent updates to "git p4" was not usable for older p4 but it
+   could be made to work with minimum changes.  Do so.
+
 
 Performance, Internal Implementation, Development Support etc.
 
@@ -75,13 +86,11 @@ Performance, Internal Implementation, Development Support etc.
    code with "git interpret-trailer".
 
  * The default Travis-CI configuration specifies newer P4 and GitLFS.
-   (merge 5f703e8f02 ls/travis-update-p4-and-lfs later to maint).
 
  * The "fast hash" that had disastrous performance issues in some
    corner cases has been retired from the internal diff.
 
  * The character width table has been updated to match Unicode 9.0
-   (merge 9e6e9aefdf bb/unicode-9.0 later to maint).
 
  * Update the procedure to generate "tags" for developer support.
    (merge 046e4c1c09 jk/make-tags-find-sources-tweak later to maint).
@@ -101,6 +110,19 @@ Performance, Internal Implementation, Development Support etc.
    superproject to .git/modules/ (and point the latter with the former
    that is turned into a "gitdir:" file) has been added.
 
+ * "git push \\server\share\dir" has recently regressed and then
+   fixed.  A test has retroactively been added for this breakage.
+
+ * Build updates for Cygwin.
+
+ * The implementation of "real_path()" was to go there with chdir(2)
+   and call getcwd(3), but this obviously wouldn't be usable in a
+   threaded environment.  Rewrite it to manually resolve relative
+   paths including symbolic links in path components.
+
+ * Adjust documentation to help AsciiDoctor render better while not
+   breaking the rendering done by AsciiDoc.
+
 
 Also contains various documentation updates and code clean-ups.
 
@@ -115,7 +137,6 @@ notes for details).
    standard I/O streams are connected to a TTY, but isatty() that
    comes with Windows incorrectly returned true if it is used on NUL
    (i.e. an equivalent to /dev/null).  This has been fixed.
-   (merge cbb3f3c9b1 js/mingw-isatty later to maint).
 
  * "git svn" did not work well with path components that are "0", and
    some configuration variable it uses were not documented.
@@ -123,70 +144,56 @@ notes for details).
 
  * "git rev-parse --symbolic" failed with a more recent notation like
    "HEAD^-1" and "HEAD^!".
-   (merge a2e7b04c44 jk/rev-parse-symbolic-parents-fix later to maint).
 
  * An empty directory in a working tree that can simply be nuked used
    to interfere while merging or cherry-picking a change to create a
    submodule directory there, which has been fixed..
-   (merge 5423d2e700 dt/empty-submodule-in-merge later to maint).
 
  * The code in "git push" to compute if any commit being pushed in the
    superproject binds a commit in a submodule that hasn't been pushed
    out was overly inefficient, making it unusable even for a small
    project that does not have any submodule but have a reasonable
    number of refs.
-   (merge 250ab24ab3 hv/submodule-not-yet-pushed-fix later to maint).
 
  * "git push --dry-run --recurse-submodule=on-demand" wasn't
    "--dry-run" in the submodules.
-   (merge 0301c821c5 bw/push-dry-run later to maint).
 
  * The output from "git worktree list" was made in readdir() order,
    and was unstable.
-   (merge 4df1d4d466 nd/worktree-list-fixup later to maint).
 
  * mergetool.<tool>.trustExitCode configuration variable did not apply
    to built-in tools, but now it does.
-   (merge 2967284456 da/mergetool-trust-exit-code later to maint).
 
  * "git p4" LFS support was broken when LFS stores an empty blob.
-   (merge d5eb3cf5e7 ls/p4-empty-file-on-lfs later to maint).
 
  * A corner case in merge-recursive regression that crept in
    during 2.10 development cycle has been fixed.
-   (merge 1c25d2d8ed jc/renormalize-merge-kill-safer-crlf later to maint).
 
  * Transport with dumb http can be fooled into following foreign URLs
    that the end user does not intend to, especially with the server
    side redirects and http-alternates mechanism, which can lead to
    security issues.  Tighten the redirection and make it more obvious
    to the end user when it happens.
-   (merge cb4d2d35c4 jk/http-walker-limit-redirect-2.9 later to maint).
 
  * Update the error messages from the dumb-http client when it fails
    to obtain loose objects; we used to give sensible error message
    only upon 404 but we now forbid unexpected redirects that needs to
    be reported with something sensible.
-   (merge 3680f16f9d jk/http-walker-limit-redirect later to maint).
 
  * When diff.renames configuration is on (and with Git 2.9 and later,
    it is enabled by default, which made it worse), "git stash"
    misbehaved if a file is removed and another file with a very
    similar content is added.
-   (merge 9d4e28ead5 jk/stash-disable-renames-internally later to maint).
 
  * "git diff --no-index" did not take "--no-abbrev" option.
-   (merge 43d1948b7b jb/diff-no-index-no-abbrev later to maint).
 
  * "git difftool --dir-diff" had a minor regression when started from
    a subdirectory, which has been fixed.
-   (merge 853e10c197 da/difftool-dir-diff-fix later to maint).
 
  * "git commit --allow-empty --only" (no pathspec) with dirty index
    ought to be an acceptable way to create a new commit that does not
    change any paths, but it was forbidden, perhaps because nobody
    needed it so far.
-   (merge beb635ca9c ak/commit-only-allow-empty later to maint).
 
  * Git 2.11 had a minor regression in "merge --ff-only" that competed
    with another process that simultanously attempted to update the
@@ -196,26 +203,21 @@ notes for details).
 
  * A pathname that begins with "//" or "\\" on Windows is special but
    path normalization logic was unaware of it.
-   (merge 7814fbe3f1 js/normalize-path-copy-ceil later to maint).
 
  * "git pull --rebase", when there is no new commits on our side since
    we forked from the upstream, should be able to fast-forward without
    invoking "git rebase", but it didn't.
-   (merge 33b842a1e9 jc/pull-rebase-ff later to maint).
 
  * The way to specify hotkeys to "xxdiff" that is used by "git
    mergetool" has been modernized to match recent versions of xxdiff.
-   (merge 6cf5f6cef7 da/mergetool-xxdiff-hotkey later to maint).
 
  * Unlike "git am --abort", "git cherry-pick --abort" moved HEAD back
    to where cherry-pick started while picking multiple changes, when
    the cherry-pick stopped to ask for help from the user, and the user
    did "git reset --hard" to a different commit in order to re-attempt
    the operation.
-   (merge ce73bb22d8 sb/sequencer-abort-safety later to maint).
 
  * Code cleanup in shallow boundary computation.
-   (merge 649b0c316a nd/shallow-fixup later to maint).
 
  * A recent update to receive-pack to make it easier to drop garbage
    objects made it clear that GIT_ALTERNATE_OBJECT_DIRECTORIES cannot
@@ -223,49 +225,39 @@ notes for details).
    made it impossible to push into a repository at such a path.  This
    has been fixed by introducing a quoting mechanism used when
    appending such a path to the colon-separated list.
-   (merge 5e74824fac jk/quote-env-path-list-component later to maint).
 
  * The function usage_msg_opt() has been updated to say "fatal:"
    before the custom message programs give, when they want to die
    with a message about wrong command line options followed by the
    standard usage string.
-   (merge 87433261a4 jk/parseopt-usage-msg-opt later to maint).
 
  * "git index-pack --stdin" needs an access to an existing repository,
    but "git index-pack file.pack" to generate an .idx file that
    corresponds to a packfile does not.
-   (merge 29401e1575 jk/index-pack-wo-repo-from-stdin later to maint).
 
  * Fix for NDEBUG builds.
-   (merge 08414938a2 jt/mailinfo-fold-in-body-headers later to maint).
 
  * A lazy "git push" without refspec did not internally use a fully
    specified refspec to perform 'current', 'simple', or 'upstream'
    push, causing unnecessary "ambiguous ref" errors.
-   (merge b284495e93 jc/push-default-explicit later to maint).
 
  * "git p4" misbehaved when swapping a directory and a symbolic link.
-   (merge df8a9e86db ld/p4-compare-dir-vs-symlink later to maint).
 
  * Even though an fix was attempted in Git 2.9.3 days, but running
    "git difftool --dir-diff" from a subdirectory never worked. This
    has been fixed.
-   (merge ce6926974e jk/difftool-in-subdir later to maint).
 
  * "git p4" that tracks multile p4 paths imported a single changelist
    that touches files in these multiple paths as one commit, followed
    by many empty commits.  This has been fixed.
-   (merge 9943e5b979 gv/p4-multi-path-commit-fix later to maint).
 
  * A potential but unlikely buffer overflow in Windows port has been
    fixed.
-   (merge c46458e82f mk/mingw-winansi-ttyname-termination-fix later to maint).
 
  * When the http server gives an incomplete response to a smart-http
    rpc call, it could lead to client waiting for a full response that
    will never come.  Teach the client side to notice this condition
    and abort the transfer.
-   (merge f8edeaa05d dt/smart-http-detect-server-going-away later to maint).
 
  * Compression setting for producing packfiles were spread across
    three codepaths, one of which did not honor any configuration.
@@ -284,17 +276,45 @@ notes for details).
  * Leakage of lockfiles in the config subsystem has been fixed.
    (merge c06fa62dfc nd/config-misc-fixes later to maint).
 
+ * It is natural that "git gc --auto" may not attempt to pack
+   everything into a single pack, and there is no point in warning
+   when the user has configured the system to use the pack bitmap,
+   leading to disabling further "gc".
+   (merge 1c409a705c dt/disable-bitmap-in-auto-gc later to maint).
+
+ * "git archive" did not read the standard configuration files, and
+   failed to notice a file that is marked as binary via the userdiff
+   driver configuration.
+   (merge 965cba2e7e jk/archive-zip-userdiff-config later to maint).
+
+ * "git blame --porcelain" misidentified the "previous" <commit, path>
+   pair (aka "source") when contents came from two or more files.
+   (merge 4e76832984 jk/blame-fixes later to maint).
+
+ * "git rebase -i" with a recent update started showing an incorrect
+   count when squashing more than 10 commits.
+   (merge 356b8ecff1 jk/rebase-i-squash-count-fix later to maint).
+
+ * "git <cmd> @{push}" on a detached HEAD used to segfault; it has
+   been corrected to error out with a message.
+   (merge b10731f43d km/branch-get-push-while-detached later to maint).
+
+ * Running "git add a/b" when "a" is a submodule correctly errored
+   out, but without a meaningful error message.
+   (merge 2d81c48fa7 sb/pathspec-errors later to maint).
+
+ * Typing ^C to pager, which usually does not kill it, killed Git and
+   took the pager down as a collateral damage in certain process-tree
+   structure.  This has been fixed.
+   (merge 46df6906f3 jk/execv-dashed-external later to maint).
+
+ * "git mergetool" without any pathspec on the command line that is
+   run from a subdirectory became no-op in Git v2.11 by mistake, which
+   has been fixed.
+
+ * Retire long unused/unmaintained gitview from the contrib/ area.
+   (merge 3120925c25 sb/remove-gitview later to maint).
+
  * Other minor doc, test and build updates and code cleanups.
-   (merge fa6ca11105 nd/qsort-in-merge-recursive later to maint).
-   (merge fa3142c919 ak/lazy-prereq-mktemp later to maint).
-   (merge 9c48b4fb23 ls/t0021-fixup later to maint).
-   (merge 584f99c87b sb/unpack-trees-grammofix later to maint).
-   (merge 54471fdcc3 jk/readme-gmane-is-no-more later to maint).
-   (merge 9e189f1a5c sb/t3600-cleanup later to maint).
-   (merge e2c20be57c lr/doc-fix-cet later to maint).
-   (merge 47437fd3bd kh/tutorial-grammofix later to maint).
    (merge f2627d9b19 sb/submodule-config-cleanup later to maint).
-   (merge 7eeda8b821 ls/filter-process later to maint).
-   (merge 6cc823c5c1 jt/fetch-no-redundant-tag-fetch-map later to maint).
-   (merge 235ec24352 mm/push-social-engineering-attack-doc later to maint).
-   (merge f1350d0c12 mm/gc-safety-doc later to maint).
+   (merge 384f1a167b sb/unpack-trees-cleanup later to maint).
index 506431267ed0ef15bce2570d3f1bf69e7268e8f0..af2ae4cc02af75c5cf9395e228ff8bbc6e390181 100644 (file)
@@ -3113,17 +3113,39 @@ user.signingKey::
        This option is passed unchanged to gpg's --local-user parameter,
        so you may specify a key using any method that gpg supports.
 
-versionsort.prereleaseSuffix::
-       When version sort is used in linkgit:git-tag[1], prerelease
-       tags (e.g. "1.0-rc1") may appear after the main release
-       "1.0". By specifying the suffix "-rc" in this variable,
-       "1.0-rc1" will appear before "1.0".
-+
-This variable can be specified multiple times, once per suffix. The
-order of suffixes in the config file determines the sorting order
-(e.g. if "-pre" appears before "-rc" in the config file then 1.0-preXX
-is sorted before 1.0-rcXX). The sorting order between different
-suffixes is undefined if they are in multiple config files.
+versionsort.prereleaseSuffix (deprecated)::
+       Deprecated alias for `versionsort.suffix`.  Ignored if
+       `versionsort.suffix` is set.
+
+versionsort.suffix::
+       Even when version sort is used in linkgit:git-tag[1], tagnames
+       with the same base version but different suffixes are still sorted
+       lexicographically, resulting e.g. in prerelease tags appearing
+       after the main release (e.g. "1.0-rc1" after "1.0").  This
+       variable can be specified to determine the sorting order of tags
+       with different suffixes.
++
+By specifying a single suffix in this variable, any tagname containing
+that suffix will appear before the corresponding main release.  E.g. if
+the variable is set to "-rc", then all "1.0-rcX" tags will appear before
+"1.0".  If specified multiple times, once per suffix, then the order of
+suffixes in the configuration will determine the sorting order of tagnames
+with those suffixes.  E.g. if "-pre" appears before "-rc" in the
+configuration, then all "1.0-preX" tags will be listed before any
+"1.0-rcX" tags.  The placement of the main release tag relative to tags
+with various suffixes can be determined by specifying the empty suffix
+among those other suffixes.  E.g. if the suffixes "-rc", "", "-ck" and
+"-bfs" appear in the configuration in this order, then all "v4.8-rcX" tags
+are listed first, followed by "v4.8", then "v4.8-ckX" and finally
+"v4.8-bfsX".
++
+If more than one suffixes match the same tagname, then that tagname will
+be sorted according to the suffix which starts at the earliest position in
+the tagname.  If more than one different matching suffixes start at
+that earliest position, then that tagname will be sorted according to the
+longest of those suffixes.
+The sorting order between different suffixes is undefined if they are
+in multiple config files.
 
 web.browser::
        Specify a web browser that may be used by some commands.
index d8570f2a75096c07d7045ddfba7f1e2e6396d000..15521f5191b6fdba66c2d3d4f92491dd8c4524f3 100644 (file)
@@ -60,6 +60,12 @@ diff.context::
        Generate diffs with <n> lines of context instead of the default
        of 3. This value is overridden by the -U option.
 
+diff.interHunkContext::
+       Show the context between diff hunks, up to the specified number
+       of lines, thereby fusing the hunks that are close to each other.
+       This value serves as the default for the `--inter-hunk-context`
+       command line option.
+
 diff.external::
        If this config variable is set, diff generation is not
        performed using the internal diff machinery, but using the
index e6215c372c3c3c876125c8ac363a446fd840e112..a219aa290726d504e7b4c7ec0a3051e091b5445b 100644 (file)
@@ -511,6 +511,8 @@ endif::git-format-patch[]
 --inter-hunk-context=<lines>::
        Show the context between diff hunks, up to the specified number
        of lines, thereby fusing hunks that are close to each other.
+       Defaults to `diff.interHunkContext` or 0 if the config option
+       is unset.
 
 -W::
 --function-context::
index 2bb9a577a2b4a4e95dcf950239c2ead8d424319f..bdd915a66b481dccf9ae6d601b5497ceb6b3b3d7 100644 (file)
@@ -18,8 +18,8 @@ on the subcommand:
 
  git bisect start [--term-{old,good}=<term> --term-{new,bad}=<term>]
                  [--no-checkout] [<bad> [<good>...]] [--] [<paths>...]
- git bisect (bad|new) [<rev>]
- git bisect (good|old) [<rev>...]
+ git bisect (bad|new|<term-new>) [<rev>]
+ git bisect (good|old|<term-old>) [<rev>...]
  git bisect terms [--term-good | --term-bad]
  git bisect skip [(<rev>|<range>)...]
  git bisect reset [<commit>]
index 0ecea6e4912f635e2825e8c02a14aef191cd91f1..71f32f35089241bc452cfdccdc6b3e9a30f95366 100644 (file)
@@ -26,6 +26,7 @@ SYNOPSIS
           [--threads <num>]
           [-f <file>] [-e] <pattern>
           [--and|--or|--not|(|)|-e <pattern>...]
+          [--recurse-submodules] [--parent-basename <basename>]
           [ [--[no-]exclude-standard] [--cached | --no-index | --untracked] | <tree>...]
           [--] [<pathspec>...]
 
@@ -88,6 +89,19 @@ OPTIONS
        mechanism.  Only useful when searching files in the current directory
        with `--no-index`.
 
+--recurse-submodules::
+       Recursively search in each submodule that has been initialized and
+       checked out in the repository.  When used in combination with the
+       <tree> option the prefix of all submodule output will be the name of
+       the parent project's <tree> object.
+
+--parent-basename <basename>::
+       For internal use only.  In order to produce uniform output with the
+       --recurse-submodules option, this option can be used to provide the
+       basename of a parent's <tree> object to a submodule so the submodule
+       can prefix its output with the parent's name rather than the SHA1 of
+       the submodule.
+
 -a::
 --text::
        Process binary files as if they were text.
index c1a3e8bf073f7aef89989356e985861644530a86..5f93f8003d1dfa43b6fab00253a206e47839efda 100644 (file)
@@ -35,7 +35,7 @@ blame::
 
 browser::
        Start a tree browser showing all files in the specified
-       commit (or `HEAD` by default).  Files selected through the
+       commit.  Files selected through the
        browser are opened in the blame viewer.
 
 citool::
index bae862ddcb9fe85765ea5bb126ced4fad8e98268..7436c64a95616d84af9922958e4ccacd0c28519a 100644 (file)
@@ -479,6 +479,8 @@ git-p4.client::
 git-p4.retries::
        Specifies the number of times to retry a p4 command (notably,
        'p4 sync') if the network times out. The default value is 3.
+       Set the value to 0 to disable retries or if your p4 version
+       does not support retries (pre 2012.2).
 
 Clone and sync variables
 ~~~~~~~~~~~~~~~~~~~~~~~~
index b6c6326cdc7bb42993cf16fcae0d492944720e51..7241e968935505cdb9d507ab910b35178fcbf3bd 100644 (file)
@@ -91,7 +91,8 @@ repository.  For example:
 ----
 prefix=$(git rev-parse --show-prefix)
 cd "$(git rev-parse --show-toplevel)"
-eval "set -- $(git rev-parse --sq --prefix "$prefix" "$@")"
+# rev-parse provides the -- needed for 'set'
+eval "set $(git rev-parse --sq --prefix "$prefix" -- "$@")"
 ----
 
 --verify::
index 76cfe40d969bc5f37dce674f43857ec89f25ae67..5055a9682393409c1ed07e1e417015f46e46a5bc 100644 (file)
@@ -101,8 +101,8 @@ OPTIONS
        multiple times, in which case the last key becomes the primary
        key. Also supports "version:refname" or "v:refname" (tag
        names are treated as versions). The "version:refname" sort
-       order can also be affected by the
-       "versionsort.prereleaseSuffix" configuration variable.
+       order can also be affected by the "versionsort.suffix"
+       configuration variable.
        The keys supported are the same as those in `git for-each-ref`.
        Sort order defaults to the value configured for the `tag.sort`
        variable if it exists, or lexicographic order otherwise. See
index ba222f68cc1a9642de326e816d5b7a8831afb82a..4f208fab925cc00535c23c40d38ad6e3d56eeaab 100644 (file)
@@ -44,9 +44,10 @@ unreleased) version of Git, that is available from the 'master'
 branch of the `git.git` repository.
 Documentation for older releases are available here:
 
-* link:v2.11.0/git.html[documentation for release 2.11]
+* link:v2.11.1/git.html[documentation for release 2.11.1]
 
 * release notes for
+  link:RelNotes/2.11.1.txt[2.11.1],
   link:RelNotes/2.11.0.txt[2.11].
 
 * link:v2.10.2/git.html[documentation for release 2.10.2]
index 35473ad02fb1dcce2e03bca30bdace4dea0232d4..10c8ff93c0b9f55ff19a8b54cdeed2fdad93b92b 100644 (file)
@@ -307,9 +307,16 @@ master or exposed as a part of a stable branch.
 <9> backport a critical fix.
 <10> create a signed tag.
 <11> make sure master was not accidentally rewound beyond that
-already pushed out.  `ko` shorthand points at the Git maintainer's
+already pushed out.
+<12> In the output from `git show-branch`, `master` should have
+everything `ko/master` has, and `next` should have
+everything `ko/next` has, etc.
+<13> push out the bleeding edge, together with new tags that point
+into the pushed history.
+
+In this example, the `ko` shorthand points at the Git maintainer's
 repository at kernel.org, and looks like this:
-+
+
 ------------
 (in .git/config)
 [remote "ko"]
@@ -320,12 +327,6 @@ repository at kernel.org, and looks like this:
        push = +refs/heads/pu
        push = refs/heads/maint
 ------------
-+
-<12> In the output from `git show-branch`, `master` should have
-everything `ko/master` has, and `next` should have
-everything `ko/next` has, etc.
-<13> push out the bleeding edge, together with new tags that point
-into the pushed history.
 
 
 Repository Administration[[ADMINISTRATION]]
index e382dd96dfded769a27c2295ebc096c15214713b..ca96c281d1f3abbf71cdac112a019d2849e7ad0b 100644 (file)
@@ -178,19 +178,21 @@ used by default. If '$XDG_CONFIG_HOME' is not set it defaults to
 History
 -------
 Gitk was the first graphical repository browser. It's written in
-tcl/tk and started off in a separate repository but was later merged
-into the main Git repository.
+tcl/tk.
 
+'gitk' is actually maintained as an independent project, but stable
+versions are distributed as part of the Git suite for the convenience
+of end users.
+
+gitk-git/ comes from Paul Mackerras's gitk project:
+
+       git://ozlabs.org/~paulus/gitk
 
 SEE ALSO
 --------
 'qgit(1)'::
        A repository browser written in C++ using Qt.
 
-'gitview(1)'::
-       A repository browser written in Python using Gtk. It's based on
-       'bzrk(1)' and distributed in the contrib area of the Git repository.
-
 'tig(1)'::
        A minimal repository browser and Git tool output highlighter written
        in C using Ncurses.
index 540e45568990f89fceb50ad619ca8402e9925983..eb1fa9853ef6fd3ba9ab29342cf55747efaa2a69 100644 (file)
@@ -27,8 +27,6 @@ parse_pathspec(). This function takes several arguments:
 
 - prefix and args come from cmd_* functions
 
-get_pathspec() is obsolete and should never be used in new code.
-
 parse_pathspec() helps catch unsupported features and reject them
 politely. At a lower level, different pathspec-related functions may
 not support the same set of features. Such pathspec-sensitive
index 5e074545729fbd938efaa65311f8437b2ca1bcdf..bc29298678a6d2d9f6b2927f97c40ba8978bd93b 100644 (file)
@@ -4395,6 +4395,10 @@ itself!
 Git Glossary
 ============
 
+[[git-explained]]
+Git explained
+-------------
+
 include::glossary-content.txt[]
 
 [[git-quick-start]]
@@ -4636,6 +4640,10 @@ $ git gc
 Appendix B: Notes and todo list for this manual
 ===============================================
 
+[[todo-list]]
+Todo list
+---------
+
 This is a work in progress.
 
 The basic requirements:
index d861bd9985dd41c24d5ff08a985c9d280d9dc07c..27afd0f378619c1960861d72df74a4b7cba7514f 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1816,7 +1816,7 @@ $(SCRIPT_LIB) : % : %.sh GIT-SCRIPT-DEFINES
 git.res: git.rc GIT-VERSION-FILE
        $(QUIET_RC)$(RC) \
          $(join -DMAJOR= -DMINOR=, $(wordlist 1,2,$(subst -, ,$(subst ., ,$(GIT_VERSION))))) \
-         -DGIT_VERSION="\\\"$(GIT_VERSION)\\\"" $< -o $@
+         -DGIT_VERSION="\\\"$(GIT_VERSION)\\\"" -i $< -o $@
 
 # This makes sure we depend on the NO_PERL setting itself.
 $(SCRIPT_PERL_GEN): GIT-BUILD-OPTIONS
@@ -2046,7 +2046,7 @@ git-%$X: %.o GIT-LDFLAGS $(GITLIBS)
 
 git-imap-send$X: imap-send.o $(IMAP_SEND_BUILDDEPS) GIT-LDFLAGS $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
-               $(LIBS) $(IMAP_SEND_LDFLAGS)
+               $(IMAP_SEND_LDFLAGS) $(LIBS)
 
 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,$^) \
index 2825de85912fc730d9a40fa66f4f83b6250a4171..fce40fddcc3b68a644fb93a698da8164a1d9b9cf 100644 (file)
--- a/abspath.c
+++ b/abspath.c
@@ -11,46 +11,83 @@ int is_directory(const char *path)
        return (!stat(path, &st) && S_ISDIR(st.st_mode));
 }
 
+/* removes the last path component from 'path' except if 'path' is root */
+static void strip_last_component(struct strbuf *path)
+{
+       size_t offset = offset_1st_component(path->buf);
+       size_t len = path->len;
+
+       /* Find start of the last component */
+       while (offset < len && !is_dir_sep(path->buf[len - 1]))
+               len--;
+       /* Skip sequences of multiple path-separators */
+       while (offset < len && is_dir_sep(path->buf[len - 1]))
+               len--;
+
+       strbuf_setlen(path, len);
+}
+
+/* get (and remove) the next component in 'remaining' and place it in 'next' */
+static void get_next_component(struct strbuf *next, struct strbuf *remaining)
+{
+       char *start = NULL;
+       char *end = NULL;
+
+       strbuf_reset(next);
+
+       /* look for the next component */
+       /* Skip sequences of multiple path-separators */
+       for (start = remaining->buf; is_dir_sep(*start); start++)
+               ; /* nothing */
+       /* Find end of the path component */
+       for (end = start; *end && !is_dir_sep(*end); end++)
+               ; /* nothing */
+
+       strbuf_add(next, start, end - start);
+       /* remove the component from 'remaining' */
+       strbuf_remove(remaining, 0, end - remaining->buf);
+}
+
+/* copies root part from remaining to resolved, canonicalizing it on the way */
+static void get_root_part(struct strbuf *resolved, struct strbuf *remaining)
+{
+       int offset = offset_1st_component(remaining->buf);
+
+       strbuf_reset(resolved);
+       strbuf_add(resolved, remaining->buf, offset);
+#ifdef GIT_WINDOWS_NATIVE
+       convert_slashes(resolved->buf);
+#endif
+       strbuf_remove(remaining, 0, offset);
+}
+
 /* We allow "recursive" symbolic links. Only within reason, though. */
-#define MAXDEPTH 5
+#ifndef MAXSYMLINKS
+#define MAXSYMLINKS 32
+#endif
 
 /*
  * Return the real path (i.e., absolute path, with symlinks resolved
  * and extra slashes removed) equivalent to the specified path.  (If
  * you want an absolute path but don't mind links, use
- * absolute_path().)  The return value is a pointer to a static
- * buffer.
+ * absolute_path().)  Places the resolved realpath in the provided strbuf.
  *
- * The input and all intermediate paths must be shorter than MAX_PATH.
  * The directory part of path (i.e., everything up to the last
  * dir_sep) must denote a valid, existing directory, but the last
  * component need not exist.  If die_on_error is set, then die with an
  * informative error message if there is a problem.  Otherwise, return
  * NULL on errors (without generating any output).
- *
- * If path is our buffer, then return path, as it's already what the
- * user wants.
  */
-static const char *real_path_internal(const char *path, int die_on_error)
+char *strbuf_realpath(struct strbuf *resolved, const char *path,
+                     int die_on_error)
 {
-       static struct strbuf sb = STRBUF_INIT;
+       struct strbuf remaining = STRBUF_INIT;
+       struct strbuf next = STRBUF_INIT;
+       struct strbuf symlink = STRBUF_INIT;
        char *retval = NULL;
-
-       /*
-        * If we have to temporarily chdir(), store the original CWD
-        * here so that we can chdir() back to it at the end of the
-        * function:
-        */
-       struct strbuf cwd = STRBUF_INIT;
-
-       int depth = MAXDEPTH;
-       char *last_elem = NULL;
+       int num_symlinks = 0;
        struct stat st;
 
-       /* We've already done it */
-       if (path == sb.buf)
-               return path;
-
        if (!*path) {
                if (die_on_error)
                        die("The empty string is not a valid path");
@@ -58,86 +95,136 @@ static const char *real_path_internal(const char *path, int die_on_error)
                        goto error_out;
        }
 
-       strbuf_reset(&sb);
-       strbuf_addstr(&sb, path);
-
-       while (depth--) {
-               if (!is_directory(sb.buf)) {
-                       char *last_slash = find_last_dir_sep(sb.buf);
-                       if (last_slash) {
-                               last_elem = xstrdup(last_slash + 1);
-                               strbuf_setlen(&sb, last_slash - sb.buf + 1);
-                       } else {
-                               last_elem = xmemdupz(sb.buf, sb.len);
-                               strbuf_reset(&sb);
-                       }
+       strbuf_addstr(&remaining, path);
+       get_root_part(resolved, &remaining);
+
+       if (!resolved->len) {
+               /* relative path; can use CWD as the initial resolved path */
+               if (strbuf_getcwd(resolved)) {
+                       if (die_on_error)
+                               die_errno("unable to get current working directory");
+                       else
+                               goto error_out;
                }
+       }
 
-               if (sb.len) {
-                       if (!cwd.len && strbuf_getcwd(&cwd)) {
+       /* Iterate over the remaining path components */
+       while (remaining.len > 0) {
+               get_next_component(&next, &remaining);
+
+               if (next.len == 0) {
+                       continue; /* empty component */
+               } else if (next.len == 1 && !strcmp(next.buf, ".")) {
+                       continue; /* '.' component */
+               } else if (next.len == 2 && !strcmp(next.buf, "..")) {
+                       /* '..' component; strip the last path component */
+                       strip_last_component(resolved);
+                       continue;
+               }
+
+               /* append the next component and resolve resultant path */
+               if (!is_dir_sep(resolved->buf[resolved->len - 1]))
+                       strbuf_addch(resolved, '/');
+               strbuf_addbuf(resolved, &next);
+
+               if (lstat(resolved->buf, &st)) {
+                       /* error out unless this was the last component */
+                       if (errno != ENOENT || remaining.len) {
                                if (die_on_error)
-                                       die_errno("Could not get current working directory");
+                                       die_errno("Invalid path '%s'",
+                                                 resolved->buf);
                                else
                                        goto error_out;
                        }
+               } else if (S_ISLNK(st.st_mode)) {
+                       ssize_t len;
+                       strbuf_reset(&symlink);
+
+                       if (num_symlinks++ > MAXSYMLINKS) {
+                               errno = ELOOP;
 
-                       if (chdir(sb.buf)) {
                                if (die_on_error)
-                                       die_errno("Could not switch to '%s'",
-                                                 sb.buf);
+                                       die("More than %d nested symlinks "
+                                           "on path '%s'", MAXSYMLINKS, path);
                                else
                                        goto error_out;
                        }
-               }
-               if (strbuf_getcwd(&sb)) {
-                       if (die_on_error)
-                               die_errno("Could not get current working directory");
-                       else
-                               goto error_out;
-               }
-
-               if (last_elem) {
-                       if (sb.len && !is_dir_sep(sb.buf[sb.len - 1]))
-                               strbuf_addch(&sb, '/');
-                       strbuf_addstr(&sb, last_elem);
-                       free(last_elem);
-                       last_elem = NULL;
-               }
 
-               if (!lstat(sb.buf, &st) && S_ISLNK(st.st_mode)) {
-                       struct strbuf next_sb = STRBUF_INIT;
-                       ssize_t len = strbuf_readlink(&next_sb, sb.buf, 0);
+                       len = strbuf_readlink(&symlink, resolved->buf,
+                                             st.st_size);
                        if (len < 0) {
                                if (die_on_error)
                                        die_errno("Invalid symlink '%s'",
-                                                 sb.buf);
+                                                 resolved->buf);
                                else
                                        goto error_out;
                        }
-                       strbuf_swap(&sb, &next_sb);
-                       strbuf_release(&next_sb);
-               } else
-                       break;
+
+                       if (is_absolute_path(symlink.buf)) {
+                               /* absolute symlink; set resolved to root */
+                               get_root_part(resolved, &symlink);
+                       } else {
+                               /*
+                                * relative symlink
+                                * strip off the last component since it will
+                                * be replaced with the contents of the symlink
+                                */
+                               strip_last_component(resolved);
+                       }
+
+                       /*
+                        * if there are still remaining components to resolve
+                        * then append them to symlink
+                        */
+                       if (remaining.len) {
+                               strbuf_addch(&symlink, '/');
+                               strbuf_addbuf(&symlink, &remaining);
+                       }
+
+                       /*
+                        * use the symlink as the remaining components that
+                        * need to be resloved
+                        */
+                       strbuf_swap(&symlink, &remaining);
+               }
        }
 
-       retval = sb.buf;
+       retval = resolved->buf;
+
 error_out:
-       free(last_elem);
-       if (cwd.len && chdir(cwd.buf))
-               die_errno("Could not change back to '%s'", cwd.buf);
-       strbuf_release(&cwd);
+       strbuf_release(&remaining);
+       strbuf_release(&next);
+       strbuf_release(&symlink);
+
+       if (!retval)
+               strbuf_reset(resolved);
 
        return retval;
 }
 
 const char *real_path(const char *path)
 {
-       return real_path_internal(path, 1);
+       static struct strbuf realpath = STRBUF_INIT;
+       return strbuf_realpath(&realpath, path, 1);
 }
 
 const char *real_path_if_valid(const char *path)
 {
-       return real_path_internal(path, 0);
+       static struct strbuf realpath = STRBUF_INIT;
+       return strbuf_realpath(&realpath, path, 0);
+}
+
+char *real_pathdup(const char *path)
+{
+       struct strbuf realpath = STRBUF_INIT;
+       char *retval = NULL;
+
+       if (strbuf_realpath(&realpath, path, 0))
+               retval = strbuf_detach(&realpath, NULL);
+
+       strbuf_release(&realpath);
+
+       return retval;
 }
 
 /*
index 9db47357b02d4c33fc4e2d1edb8d948b2b3a496f..b429a8d974a02b06c9c6bc46850f4c9fae4a6d01 100644 (file)
@@ -554,11 +554,18 @@ static void dos_time(time_t *time, int *dos_date, int *dos_time)
        *dos_time = t->tm_sec / 2 + t->tm_min * 32 + t->tm_hour * 2048;
 }
 
+static int archive_zip_config(const char *var, const char *value, void *data)
+{
+       return userdiff_config(var, value);
+}
+
 static int write_zip_archive(const struct archiver *ar,
                             struct archiver_args *args)
 {
        int err;
 
+       git_config(archive_zip_config, NULL);
+
        dos_time(&args->time, &zip_date, &zip_time);
 
        zip_dir = xmalloc(ZIP_DIRECTORY_MIN_SIZE);
index ab54a5c1f48ffcb87193eafb69d45a0f8d378c5e..126b8c9e5ba627ee5514f5a472bd7ae5cb9f9f67 100644 (file)
@@ -1700,13 +1700,23 @@ static void get_commit_info(struct commit *commit,
 }
 
 /*
+ * Write out any suspect information which depends on the path. This must be
+ * handled separately from emit_one_suspect_detail(), because a given commit
+ * may have changes in multiple paths. So this needs to appear each time
+ * we mention a new group.
+ *
  * To allow LF and other nonportable characters in pathnames,
  * they are c-style quoted as needed.
  */
-static void write_filename_info(const char *path)
+static void write_filename_info(struct origin *suspect)
 {
+       if (suspect->previous) {
+               struct origin *prev = suspect->previous;
+               printf("previous %s ", oid_to_hex(&prev->commit->object.oid));
+               write_name_quoted(prev->path, stdout, '\n');
+       }
        printf("filename ");
-       write_name_quoted(path, stdout, '\n');
+       write_name_quoted(suspect->path, stdout, '\n');
 }
 
 /*
@@ -1735,11 +1745,6 @@ static int emit_one_suspect_detail(struct origin *suspect, int repeat)
        printf("summary %s\n", ci.summary.buf);
        if (suspect->commit->object.flags & UNINTERESTING)
                printf("boundary\n");
-       if (suspect->previous) {
-               struct origin *prev = suspect->previous;
-               printf("previous %s ", oid_to_hex(&prev->commit->object.oid));
-               write_name_quoted(prev->path, stdout, '\n');
-       }
 
        commit_info_destroy(&ci);
 
@@ -1760,7 +1765,7 @@ static void found_guilty_entry(struct blame_entry *ent,
                       oid_to_hex(&suspect->commit->object.oid),
                       ent->s_lno + 1, ent->lno + 1, ent->num_lines);
                emit_one_suspect_detail(suspect, 0);
-               write_filename_info(suspect->path);
+               write_filename_info(suspect);
                maybe_flush_or_die(stdout, "stdout");
        }
        pi->blamed_lines += ent->num_lines;
@@ -1884,7 +1889,7 @@ static void emit_porcelain_details(struct origin *suspect, int repeat)
 {
        if (emit_one_suspect_detail(suspect, repeat) ||
            (suspect->commit->object.flags & MORE_THAN_ONE_PATH))
-               write_filename_info(suspect->path);
+               write_filename_info(suspect);
 }
 
 static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent,
@@ -2655,9 +2660,11 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
        } else if (show_progress < 0)
                show_progress = isatty(2);
 
-       if (0 < abbrev)
+       if (0 < abbrev && abbrev < GIT_SHA1_HEXSZ)
                /* one more abbrev length is needed for the boundary commit */
                abbrev++;
+       else if (!abbrev)
+               abbrev = GIT_SHA1_HEXSZ;
 
        if (revs_file && read_ancestry(revs_file))
                die_errno("reading graft file '%s' failed", revs_file);
index 069950d0b417f5eb97cd756ae32df7a2fb13abce..331f2192607a4f7657750f4592193e6c0b8cea55 100644 (file)
@@ -191,6 +191,11 @@ static void add_repack_all_option(void)
        }
 }
 
+static void add_repack_incremental_option(void)
+{
+       argv_array_push(&repack, "--no-write-bitmap-index");
+}
+
 static int need_to_gc(void)
 {
        /*
@@ -208,7 +213,9 @@ static int need_to_gc(void)
         */
        if (too_many_packs())
                add_repack_all_option();
-       else if (!too_many_loose_objects())
+       else if (too_many_loose_objects())
+               add_repack_incremental_option();
+       else
                return 0;
 
        if (run_hook_le(NULL, "pre-auto-gc", NULL))
index 8887b6addb5f9085dd9ed46a9e5ad81768c551cb..2c727ef499c0fa7b44c0cf1890cc44427dc8921f 100644 (file)
 #include "quote.h"
 #include "dir.h"
 #include "pathspec.h"
+#include "submodule.h"
+#include "submodule-config.h"
 
 static char const * const grep_usage[] = {
        N_("git grep [<options>] [-e] <pattern> [<rev>...] [[--] <path>...]"),
        NULL
 };
 
+static const char *super_prefix;
+static int recurse_submodules;
+static struct argv_array submodule_options = ARGV_ARRAY_INIT;
+static const char *parent_basename;
+
+static int grep_submodule_launch(struct grep_opt *opt,
+                                const struct grep_source *gs);
+
 #define GREP_NUM_THREADS_DEFAULT 8
 static int num_threads;
 
@@ -174,7 +184,10 @@ static void *run(void *arg)
                        break;
 
                opt->output_priv = w;
-               hit |= grep_source(opt, &w->source);
+               if (w->source.type == GREP_SOURCE_SUBMODULE)
+                       hit |= grep_submodule_launch(opt, &w->source);
+               else
+                       hit |= grep_source(opt, &w->source);
                grep_source_clear_data(&w->source);
                work_done(w);
        }
@@ -300,6 +313,10 @@ static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1,
        if (opt->relative && opt->prefix_length) {
                quote_path_relative(filename + tree_name_len, opt->prefix, &pathbuf);
                strbuf_insert(&pathbuf, 0, filename, tree_name_len);
+       } else if (super_prefix) {
+               strbuf_add(&pathbuf, filename, tree_name_len);
+               strbuf_addstr(&pathbuf, super_prefix);
+               strbuf_addstr(&pathbuf, filename + tree_name_len);
        } else {
                strbuf_addstr(&pathbuf, filename);
        }
@@ -328,10 +345,13 @@ static int grep_file(struct grep_opt *opt, const char *filename)
 {
        struct strbuf buf = STRBUF_INIT;
 
-       if (opt->relative && opt->prefix_length)
+       if (opt->relative && opt->prefix_length) {
                quote_path_relative(filename, opt->prefix, &buf);
-       else
+       } else {
+               if (super_prefix)
+                       strbuf_addstr(&buf, super_prefix);
                strbuf_addstr(&buf, filename);
+       }
 
 #ifndef NO_PTHREADS
        if (num_threads) {
@@ -378,31 +398,310 @@ static void run_pager(struct grep_opt *opt, const char *prefix)
                exit(status);
 }
 
-static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec, int cached)
+static void compile_submodule_options(const struct grep_opt *opt,
+                                     const struct pathspec *pathspec,
+                                     int cached, int untracked,
+                                     int opt_exclude, int use_index,
+                                     int pattern_type_arg)
+{
+       struct grep_pat *pattern;
+       int i;
+
+       if (recurse_submodules)
+               argv_array_push(&submodule_options, "--recurse-submodules");
+
+       if (cached)
+               argv_array_push(&submodule_options, "--cached");
+       if (!use_index)
+               argv_array_push(&submodule_options, "--no-index");
+       if (untracked)
+               argv_array_push(&submodule_options, "--untracked");
+       if (opt_exclude > 0)
+               argv_array_push(&submodule_options, "--exclude-standard");
+
+       if (opt->invert)
+               argv_array_push(&submodule_options, "-v");
+       if (opt->ignore_case)
+               argv_array_push(&submodule_options, "-i");
+       if (opt->word_regexp)
+               argv_array_push(&submodule_options, "-w");
+       switch (opt->binary) {
+       case GREP_BINARY_NOMATCH:
+               argv_array_push(&submodule_options, "-I");
+               break;
+       case GREP_BINARY_TEXT:
+               argv_array_push(&submodule_options, "-a");
+               break;
+       default:
+               break;
+       }
+       if (opt->allow_textconv)
+               argv_array_push(&submodule_options, "--textconv");
+       if (opt->max_depth != -1)
+               argv_array_pushf(&submodule_options, "--max-depth=%d",
+                                opt->max_depth);
+       if (opt->linenum)
+               argv_array_push(&submodule_options, "-n");
+       if (!opt->pathname)
+               argv_array_push(&submodule_options, "-h");
+       if (!opt->relative)
+               argv_array_push(&submodule_options, "--full-name");
+       if (opt->name_only)
+               argv_array_push(&submodule_options, "-l");
+       if (opt->unmatch_name_only)
+               argv_array_push(&submodule_options, "-L");
+       if (opt->null_following_name)
+               argv_array_push(&submodule_options, "-z");
+       if (opt->count)
+               argv_array_push(&submodule_options, "-c");
+       if (opt->file_break)
+               argv_array_push(&submodule_options, "--break");
+       if (opt->heading)
+               argv_array_push(&submodule_options, "--heading");
+       if (opt->pre_context)
+               argv_array_pushf(&submodule_options, "--before-context=%d",
+                                opt->pre_context);
+       if (opt->post_context)
+               argv_array_pushf(&submodule_options, "--after-context=%d",
+                                opt->post_context);
+       if (opt->funcname)
+               argv_array_push(&submodule_options, "-p");
+       if (opt->funcbody)
+               argv_array_push(&submodule_options, "-W");
+       if (opt->all_match)
+               argv_array_push(&submodule_options, "--all-match");
+       if (opt->debug)
+               argv_array_push(&submodule_options, "--debug");
+       if (opt->status_only)
+               argv_array_push(&submodule_options, "-q");
+
+       switch (pattern_type_arg) {
+       case GREP_PATTERN_TYPE_BRE:
+               argv_array_push(&submodule_options, "-G");
+               break;
+       case GREP_PATTERN_TYPE_ERE:
+               argv_array_push(&submodule_options, "-E");
+               break;
+       case GREP_PATTERN_TYPE_FIXED:
+               argv_array_push(&submodule_options, "-F");
+               break;
+       case GREP_PATTERN_TYPE_PCRE:
+               argv_array_push(&submodule_options, "-P");
+               break;
+       case GREP_PATTERN_TYPE_UNSPECIFIED:
+               break;
+       }
+
+       for (pattern = opt->pattern_list; pattern != NULL;
+            pattern = pattern->next) {
+               switch (pattern->token) {
+               case GREP_PATTERN:
+                       argv_array_pushf(&submodule_options, "-e%s",
+                                        pattern->pattern);
+                       break;
+               case GREP_AND:
+               case GREP_OPEN_PAREN:
+               case GREP_CLOSE_PAREN:
+               case GREP_NOT:
+               case GREP_OR:
+                       argv_array_push(&submodule_options, pattern->pattern);
+                       break;
+               /* BODY and HEAD are not used by git-grep */
+               case GREP_PATTERN_BODY:
+               case GREP_PATTERN_HEAD:
+                       break;
+               }
+       }
+
+       /*
+        * Limit number of threads for child process to use.
+        * This is to prevent potential fork-bomb behavior of git-grep as each
+        * submodule process has its own thread pool.
+        */
+       argv_array_pushf(&submodule_options, "--threads=%d",
+                        (num_threads + 1) / 2);
+
+       /* Add Pathspecs */
+       argv_array_push(&submodule_options, "--");
+       for (i = 0; i < pathspec->nr; i++)
+               argv_array_push(&submodule_options,
+                               pathspec->items[i].original);
+}
+
+/*
+ * Launch child process to grep contents of a submodule
+ */
+static int grep_submodule_launch(struct grep_opt *opt,
+                                const struct grep_source *gs)
+{
+       struct child_process cp = CHILD_PROCESS_INIT;
+       int status, i;
+       const char *end_of_base;
+       const char *name;
+       struct work_item *w = opt->output_priv;
+
+       end_of_base = strchr(gs->name, ':');
+       if (gs->identifier && end_of_base)
+               name = end_of_base + 1;
+       else
+               name = gs->name;
+
+       prepare_submodule_repo_env(&cp.env_array);
+       argv_array_push(&cp.env_array, GIT_DIR_ENVIRONMENT);
+
+       /* Add super prefix */
+       argv_array_pushf(&cp.args, "--super-prefix=%s%s/",
+                        super_prefix ? super_prefix : "",
+                        name);
+       argv_array_push(&cp.args, "grep");
+
+       /*
+        * Add basename of parent project
+        * When performing grep on a tree object the filename is prefixed
+        * with the object's name: 'tree-name:filename'.  In order to
+        * provide uniformity of output we want to pass the name of the
+        * parent project's object name to the submodule so the submodule can
+        * prefix its output with the parent's name and not its own SHA1.
+        */
+       if (gs->identifier && end_of_base)
+               argv_array_pushf(&cp.args, "--parent-basename=%.*s",
+                                (int) (end_of_base - gs->name),
+                                gs->name);
+
+       /* Add options */
+       for (i = 0; i < submodule_options.argc; i++) {
+               /*
+                * If there is a tree identifier for the submodule, add the
+                * rev after adding the submodule options but before the
+                * pathspecs.  To do this we listen for the '--' and insert the
+                * sha1 before pushing the '--' onto the child process argv
+                * array.
+                */
+               if (gs->identifier &&
+                   !strcmp("--", submodule_options.argv[i])) {
+                       argv_array_push(&cp.args, sha1_to_hex(gs->identifier));
+               }
+
+               argv_array_push(&cp.args, submodule_options.argv[i]);
+       }
+
+       cp.git_cmd = 1;
+       cp.dir = gs->path;
+
+       /*
+        * Capture output to output buffer and check the return code from the
+        * child process.  A '0' indicates a hit, a '1' indicates no hit and
+        * anything else is an error.
+        */
+       status = capture_command(&cp, &w->out, 0);
+       if (status && (status != 1)) {
+               /* flush the buffer */
+               write_or_die(1, w->out.buf, w->out.len);
+               die("process for submodule '%s' failed with exit code: %d",
+                   gs->name, status);
+       }
+
+       /* invert the return code to make a hit equal to 1 */
+       return !status;
+}
+
+/*
+ * Prep grep structures for a submodule grep
+ * sha1: the sha1 of the submodule or NULL if using the working tree
+ * filename: name of the submodule including tree name of parent
+ * path: location of the submodule
+ */
+static int grep_submodule(struct grep_opt *opt, const unsigned char *sha1,
+                         const char *filename, const char *path)
+{
+       if (!is_submodule_initialized(path))
+               return 0;
+       if (!is_submodule_populated(path)) {
+               /*
+                * If searching history, check for the presense of the
+                * submodule's gitdir before skipping the submodule.
+                */
+               if (sha1) {
+                       const struct submodule *sub =
+                                       submodule_from_path(null_sha1, path);
+                       if (sub)
+                               path = git_path("modules/%s", sub->name);
+
+                       if (!(is_directory(path) && is_git_directory(path)))
+                               return 0;
+               } else {
+                       return 0;
+               }
+       }
+
+#ifndef NO_PTHREADS
+       if (num_threads) {
+               add_work(opt, GREP_SOURCE_SUBMODULE, filename, path, sha1);
+               return 0;
+       } else
+#endif
+       {
+               struct work_item w;
+               int hit;
+
+               grep_source_init(&w.source, GREP_SOURCE_SUBMODULE,
+                                filename, path, sha1);
+               strbuf_init(&w.out, 0);
+               opt->output_priv = &w;
+               hit = grep_submodule_launch(opt, &w.source);
+
+               write_or_die(1, w.out.buf, w.out.len);
+
+               grep_source_clear(&w.source);
+               strbuf_release(&w.out);
+               return hit;
+       }
+}
+
+static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec,
+                     int cached)
 {
        int hit = 0;
        int nr;
+       struct strbuf name = STRBUF_INIT;
+       int name_base_len = 0;
+       if (super_prefix) {
+               name_base_len = strlen(super_prefix);
+               strbuf_addstr(&name, super_prefix);
+       }
+
        read_cache();
 
        for (nr = 0; nr < active_nr; nr++) {
                const struct cache_entry *ce = active_cache[nr];
-               if (!S_ISREG(ce->ce_mode))
-                       continue;
-               if (!ce_path_match(ce, pathspec, NULL))
+               strbuf_setlen(&name, name_base_len);
+               strbuf_addstr(&name, ce->name);
+
+               if (S_ISREG(ce->ce_mode) &&
+                   match_pathspec(pathspec, name.buf, name.len, 0, NULL,
+                                  S_ISDIR(ce->ce_mode) ||
+                                  S_ISGITLINK(ce->ce_mode))) {
+                       /*
+                        * If CE_VALID is on, we assume worktree file and its
+                        * cache entry are identical, even if worktree file has
+                        * been modified, so use cache version instead
+                        */
+                       if (cached || (ce->ce_flags & CE_VALID) ||
+                           ce_skip_worktree(ce)) {
+                               if (ce_stage(ce) || ce_intent_to_add(ce))
+                                       continue;
+                               hit |= grep_sha1(opt, ce->oid.hash, ce->name,
+                                                0, ce->name);
+                       } else {
+                               hit |= grep_file(opt, ce->name);
+                       }
+               } else if (recurse_submodules && S_ISGITLINK(ce->ce_mode) &&
+                          submodule_path_match(pathspec, name.buf, NULL)) {
+                       hit |= grep_submodule(opt, NULL, ce->name, ce->name);
+               } else {
                        continue;
-               /*
-                * If CE_VALID is on, we assume worktree file and its cache entry
-                * are identical, even if worktree file has been modified, so use
-                * cache version instead
-                */
-               if (cached || (ce->ce_flags & CE_VALID) || ce_skip_worktree(ce)) {
-                       if (ce_stage(ce) || ce_intent_to_add(ce))
-                               continue;
-                       hit |= grep_sha1(opt, ce->oid.hash, ce->name, 0,
-                                        ce->name);
                }
-               else
-                       hit |= grep_file(opt, ce->name);
+
                if (ce_stage(ce)) {
                        do {
                                nr++;
@@ -413,6 +712,8 @@ static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec, int
                if (hit && opt->status_only)
                        break;
        }
+
+       strbuf_release(&name);
        return hit;
 }
 
@@ -424,12 +725,22 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
        enum interesting match = entry_not_interesting;
        struct name_entry entry;
        int old_baselen = base->len;
+       struct strbuf name = STRBUF_INIT;
+       int name_base_len = 0;
+       if (super_prefix) {
+               strbuf_addstr(&name, super_prefix);
+               name_base_len = name.len;
+       }
 
        while (tree_entry(tree, &entry)) {
                int te_len = tree_entry_len(&entry);
 
                if (match != all_entries_interesting) {
-                       match = tree_entry_interesting(&entry, base, tn_len, pathspec);
+                       strbuf_addstr(&name, base->buf + tn_len);
+                       match = tree_entry_interesting(&entry, &name,
+                                                      0, pathspec);
+                       strbuf_setlen(&name, name_base_len);
+
                        if (match == all_entries_not_interesting)
                                break;
                        if (match == entry_not_interesting)
@@ -441,8 +752,7 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
                if (S_ISREG(entry.mode)) {
                        hit |= grep_sha1(opt, entry.oid->hash, base->buf, tn_len,
                                         check_attr ? base->buf + tn_len : NULL);
-               }
-               else if (S_ISDIR(entry.mode)) {
+               } else if (S_ISDIR(entry.mode)) {
                        enum object_type type;
                        struct tree_desc sub;
                        void *data;
@@ -458,12 +768,18 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
                        hit |= grep_tree(opt, pathspec, &sub, base, tn_len,
                                         check_attr);
                        free(data);
+               } else if (recurse_submodules && S_ISGITLINK(entry.mode)) {
+                       hit |= grep_submodule(opt, entry.oid->hash, base->buf,
+                                             base->buf + tn_len);
                }
+
                strbuf_setlen(base, old_baselen);
 
                if (hit && opt->status_only)
                        break;
        }
+
+       strbuf_release(&name);
        return hit;
 }
 
@@ -487,6 +803,10 @@ static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec,
                if (!data)
                        die(_("unable to read tree (%s)"), oid_to_hex(&obj->oid));
 
+               /* Use parent's name as base when recursing submodules */
+               if (recurse_submodules && parent_basename)
+                       name = parent_basename;
+
                len = name ? strlen(name) : 0;
                strbuf_init(&base, PATH_MAX + len + 1);
                if (len) {
@@ -513,6 +833,12 @@ static int grep_objects(struct grep_opt *opt, const struct pathspec *pathspec,
        for (i = 0; i < nr; i++) {
                struct object *real_obj;
                real_obj = deref_tag(list->objects[i].item, NULL, 0);
+
+               /* load the gitmodules file for this rev */
+               if (recurse_submodules) {
+                       submodule_free();
+                       gitmodules_config_sha1(real_obj->oid.hash);
+               }
                if (grep_object(opt, pathspec, real_obj, list->objects[i].name, list->objects[i].path)) {
                        hit = 1;
                        if (opt->status_only)
@@ -651,6 +977,11 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                        N_("search in both tracked and untracked files")),
                OPT_SET_INT(0, "exclude-standard", &opt_exclude,
                            N_("ignore files specified via '.gitignore'"), 1),
+               OPT_BOOL(0, "recurse-submodules", &recurse_submodules,
+                        N_("recursivley search in each submodule")),
+               OPT_STRING(0, "parent-basename", &parent_basename,
+                          N_("basename"),
+                          N_("prepend parent project's basename to output")),
                OPT_GROUP(""),
                OPT_BOOL('v', "invert-match", &opt.invert,
                        N_("show non-matching lines")),
@@ -755,6 +1086,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
        init_grep_defaults();
        git_config(grep_cmd_config, NULL);
        grep_init(&opt, prefix);
+       super_prefix = get_super_prefix();
 
        /*
         * If there is no -- then the paths must exist in the working
@@ -872,6 +1204,13 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
        pathspec.max_depth = opt.max_depth;
        pathspec.recursive = 1;
 
+       if (recurse_submodules) {
+               gitmodules_config();
+               compile_submodule_options(&opt, &pathspec, cached, untracked,
+                                         opt_exclude, use_index,
+                                         pattern_type_arg);
+       }
+
        if (show_in_pager && (cached || list.nr))
                die(_("--open-files-in-pager only works on the worktree"));
 
@@ -895,6 +1234,9 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                }
        }
 
+       if (recurse_submodules && (!use_index || untracked))
+               die(_("option not supported with --recurse-submodules."));
+
        if (!show_in_pager && !opt.status_only)
                setup_pager();
 
index 2399b97d902668a08c3c881d544f4d6a88f4e263..76d68fad001083d85800b937798529a1682959c9 100644 (file)
@@ -338,7 +338,7 @@ int init_db(const char *git_dir, const char *real_git_dir,
 {
        int reinit;
        int exist_ok = flags & INIT_DB_EXIST_OK;
-       char *original_git_dir = xstrdup(real_path(git_dir));
+       char *original_git_dir = real_pathdup(git_dir);
 
        if (real_git_dir) {
                struct stat st;
@@ -489,7 +489,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
        argc = parse_options(argc, argv, prefix, init_db_options, init_db_usage, 0);
 
        if (real_git_dir && !is_absolute_path(real_git_dir))
-               real_git_dir = xstrdup(real_path(real_git_dir));
+               real_git_dir = real_pathdup(real_git_dir);
 
        if (argc == 1) {
                int mkdir_tried = 0;
@@ -560,7 +560,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
                const char *git_dir_parent = strrchr(git_dir, '/');
                if (git_dir_parent) {
                        char *rel = xstrndup(git_dir, git_dir_parent - git_dir);
-                       git_work_tree_cfg = xstrdup(real_path(rel));
+                       git_work_tree_cfg = real_pathdup(rel);
                        free(rel);
                }
                if (!git_work_tree_cfg)
index 0e30d862303b67ace2c7accf2e7c9346d8f63264..d7ebeb4ce6b1f1a0491f8db8a66001968a41b641 100644 (file)
@@ -31,21 +31,18 @@ static const  char * const ls_tree_usage[] = {
 
 static int show_recursive(const char *base, int baselen, const char *pathname)
 {
-       const char **s;
+       int i;
 
        if (ls_options & LS_RECURSIVE)
                return 1;
 
-       s = pathspec._raw;
-       if (!s)
+       if (!pathspec.nr)
                return 0;
 
-       for (;;) {
-               const char *spec = *s++;
+       for (i = 0; i < pathspec.nr; i++) {
+               const char *spec = pathspec.items[i].match;
                int len, speclen;
 
-               if (!spec)
-                       return 0;
                if (strncmp(base, spec, baselen))
                        continue;
                len = strlen(pathname);
@@ -59,6 +56,7 @@ static int show_recursive(const char *base, int baselen, const char *pathname)
                        continue;
                return 1;
        }
+       return 0;
 }
 
 static int show_tree(const unsigned char *sha1, struct strbuf *base,
@@ -175,8 +173,8 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)
         * cannot be lifted until it is converted to use
         * match_pathspec() or tree_entry_interesting()
         */
-       parse_pathspec(&pathspec, PATHSPEC_GLOB | PATHSPEC_ICASE |
-                                 PATHSPEC_EXCLUDE,
+       parse_pathspec(&pathspec, PATHSPEC_ALL_MAGIC &
+                                 ~(PATHSPEC_FROMTOP | PATHSPEC_LITERAL),
                       PATHSPEC_PREFER_CWD,
                       prefix, argv + 1);
        for (i = 0; i < pathspec.nr; i++)
index 43adf92ba64426a2f312cd6133276a98b20855f1..61d20037add7cf56df721f8d69612b50412f94e9 100644 (file)
@@ -4,6 +4,7 @@
  * Copyright (C) 2006 Johannes Schindelin
  */
 #include "builtin.h"
+#include "pathspec.h"
 #include "lockfile.h"
 #include "dir.h"
 #include "cache-tree.h"
@@ -19,31 +20,42 @@ static const char * const builtin_mv_usage[] = {
 #define DUP_BASENAME 1
 #define KEEP_TRAILING_SLASH 2
 
-static const char **internal_copy_pathspec(const char *prefix,
-                                          const char **pathspec,
-                                          int count, unsigned flags)
+static const char **internal_prefix_pathspec(const char *prefix,
+                                            const char **pathspec,
+                                            int count, unsigned flags)
 {
        int i;
        const char **result;
+       int prefixlen = prefix ? strlen(prefix) : 0;
        ALLOC_ARRAY(result, count + 1);
-       COPY_ARRAY(result, pathspec, count);
-       result[count] = NULL;
+
+       /* Create an intermediate copy of the pathspec based on the flags */
        for (i = 0; i < count; i++) {
-               int length = strlen(result[i]);
+               int length = strlen(pathspec[i]);
                int to_copy = length;
+               char *it;
                while (!(flags & KEEP_TRAILING_SLASH) &&
-                      to_copy > 0 && is_dir_sep(result[i][to_copy - 1]))
+                      to_copy > 0 && is_dir_sep(pathspec[i][to_copy - 1]))
                        to_copy--;
-               if (to_copy != length || flags & DUP_BASENAME) {
-                       char *it = xmemdupz(result[i], to_copy);
-                       if (flags & DUP_BASENAME) {
-                               result[i] = xstrdup(basename(it));
-                               free(it);
-                       } else
-                               result[i] = it;
+
+               it = xmemdupz(pathspec[i], to_copy);
+               if (flags & DUP_BASENAME) {
+                       result[i] = xstrdup(basename(it));
+                       free(it);
+               } else {
+                       result[i] = it;
                }
        }
-       return get_pathspec(prefix, result);
+       result[count] = NULL;
+
+       /* Prefix the pathspec and free the old intermediate strings */
+       for (i = 0; i < count; i++) {
+               const char *match = prefix_path(prefix, prefixlen, result[i]);
+               free((char *) result[i]);
+               result[i] = match;
+       }
+
+       return result;
 }
 
 static const char *add_slash(const char *path)
@@ -130,7 +142,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
        if (read_cache() < 0)
                die(_("index file corrupt"));
 
-       source = internal_copy_pathspec(prefix, argv, argc, 0);
+       source = internal_prefix_pathspec(prefix, argv, argc, 0);
        modes = xcalloc(argc, sizeof(enum update_mode));
        /*
         * Keep trailing slash, needed to let
@@ -140,16 +152,16 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
        flags = KEEP_TRAILING_SLASH;
        if (argc == 1 && is_directory(argv[0]) && !is_directory(argv[1]))
                flags = 0;
-       dest_path = internal_copy_pathspec(prefix, argv + argc, 1, flags);
+       dest_path = internal_prefix_pathspec(prefix, argv + argc, 1, flags);
        submodule_gitfile = xcalloc(argc, sizeof(char *));
 
        if (dest_path[0][0] == '\0')
                /* special case: "." was normalized to "" */
-               destination = internal_copy_pathspec(dest_path[0], argv, argc, DUP_BASENAME);
+               destination = internal_prefix_pathspec(dest_path[0], argv, argc, DUP_BASENAME);
        else if (!lstat(dest_path[0], &st) &&
                        S_ISDIR(st.st_mode)) {
                dest_path[0] = add_slash(dest_path[0]);
-               destination = internal_copy_pathspec(dest_path[0], argv, argc, DUP_BASENAME);
+               destination = internal_prefix_pathspec(dest_path[0], argv, argc, DUP_BASENAME);
        } else {
                if (argc != 1)
                        die(_("destination '%s' is not a directory"), dest_path[0]);
index 80dd06b4a2a8b784ab96899cb150b13006d7e402..677bc7c81a2be11b287f3b05f91742216fbe1d51 100644 (file)
@@ -18,6 +18,12 @@ static const char *const git_repack_usage[] = {
        NULL
 };
 
+static const char incremental_bitmap_conflict_error[] = N_(
+"Incremental repacks are incompatible with bitmap indexes.  Use\n"
+"--no-write-bitmap-index or disable the pack.writebitmaps configuration."
+);
+
+
 static int repack_config(const char *var, const char *value, void *cb)
 {
        if (!strcmp(var, "repack.usedeltabaseoffset")) {
@@ -206,6 +212,9 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
        if (pack_kept_objects < 0)
                pack_kept_objects = write_bitmaps;
 
+       if (write_bitmaps && !(pack_everything & ALL_INTO_ONE))
+               die(_(incremental_bitmap_conflict_error));
+
        packdir = mkpathdup("%s/pack", get_object_directory());
        packtmp = mkpathdup("%s/.tmp-%d-pack", packdir, (int)getpid());
 
index 7f15a3d7f82a7b610dcb8c6c9f713b485614e2f7..452170a3ab45d24e03ab11965448572a411c349f 100644 (file)
@@ -59,27 +59,9 @@ static void print_error_files(struct string_list *files_list,
        }
 }
 
-static void error_removing_concrete_submodules(struct string_list *files, int *errs)
-{
-       print_error_files(files,
-                         Q_("the following submodule (or one of its nested "
-                            "submodules)\n"
-                            "uses a .git directory:",
-                            "the following submodules (or one of their nested "
-                            "submodules)\n"
-                            "use a .git directory:", files->nr),
-                         _("\n(use 'rm -rf' if you really want to remove "
-                           "it including all of its history)"),
-                         errs);
-       string_list_clear(files, 0);
-}
-
-static int check_submodules_use_gitfiles(void)
+static void submodules_absorb_gitdir_if_needed(const char *prefix)
 {
        int i;
-       int errs = 0;
-       struct string_list files = STRING_LIST_INIT_NODUP;
-
        for (i = 0; i < list.nr; i++) {
                const char *name = list.entry[i].name;
                int pos;
@@ -99,12 +81,9 @@ static int check_submodules_use_gitfiles(void)
                        continue;
 
                if (!submodule_uses_gitfile(name))
-                       string_list_append(&files, name);
+                       absorb_git_dir_into_superproject(prefix, name,
+                               ABSORB_GITDIR_RECURSE_SUBMODULES);
        }
-
-       error_removing_concrete_submodules(&files, &errs);
-
-       return errs;
 }
 
 static int check_local_mod(struct object_id *head, int index_only)
@@ -120,7 +99,6 @@ static int check_local_mod(struct object_id *head, int index_only)
        int errs = 0;
        struct string_list files_staged = STRING_LIST_INIT_NODUP;
        struct string_list files_cached = STRING_LIST_INIT_NODUP;
-       struct string_list files_submodule = STRING_LIST_INIT_NODUP;
        struct string_list files_local = STRING_LIST_INIT_NODUP;
 
        no_head = is_null_oid(head);
@@ -187,7 +165,9 @@ static int check_local_mod(struct object_id *head, int index_only)
                 */
                if (ce_match_stat(ce, &st, 0) ||
                    (S_ISGITLINK(ce->ce_mode) &&
-                    !ok_to_remove_submodule(ce->name)))
+                    bad_to_remove_submodule(ce->name,
+                               SUBMODULE_REMOVAL_DIE_ON_ERROR |
+                               SUBMODULE_REMOVAL_IGNORE_IGNORED_UNTRACKED)))
                        local_changes = 1;
 
                /*
@@ -217,13 +197,8 @@ static int check_local_mod(struct object_id *head, int index_only)
                else if (!index_only) {
                        if (staged_changes)
                                string_list_append(&files_cached, name);
-                       if (local_changes) {
-                               if (S_ISGITLINK(ce->ce_mode) &&
-                                   !submodule_uses_gitfile(name))
-                                       string_list_append(&files_submodule, name);
-                               else
-                                       string_list_append(&files_local, name);
-                       }
+                       if (local_changes)
+                               string_list_append(&files_local, name);
                }
        }
        print_error_files(&files_staged,
@@ -245,8 +220,6 @@ static int check_local_mod(struct object_id *head, int index_only)
                          &errs);
        string_list_clear(&files_cached, 0);
 
-       error_removing_concrete_submodules(&files_submodule, &errs);
-
        print_error_files(&files_local,
                          Q_("the following file has local modifications:",
                             "the following files have local modifications:",
@@ -340,6 +313,9 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
                        exit(0);
        }
 
+       if (!index_only)
+               submodules_absorb_gitdir_if_needed(prefix);
+
        /*
         * If not forced, the file, the index and the HEAD (if exists)
         * must match; but the file can already been removed, since
@@ -356,9 +332,6 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
                        oidclr(&oid);
                if (check_local_mod(&oid, index_only))
                        exit(1);
-       } else if (!index_only) {
-               if (check_submodules_use_gitfiles())
-                       exit(1);
        }
 
        /*
@@ -387,32 +360,20 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
         */
        if (!index_only) {
                int removed = 0, gitmodules_modified = 0;
-               struct strbuf buf = STRBUF_INIT;
                for (i = 0; i < list.nr; i++) {
                        const char *path = list.entry[i].name;
                        if (list.entry[i].is_submodule) {
-                               if (is_empty_dir(path)) {
-                                       if (!rmdir(path)) {
-                                               removed = 1;
-                                               if (!remove_path_from_gitmodules(path))
-                                                       gitmodules_modified = 1;
-                                               continue;
-                                       }
-                               } else {
-                                       strbuf_reset(&buf);
-                                       strbuf_addstr(&buf, path);
-                                       if (!remove_dir_recursively(&buf, 0)) {
-                                               removed = 1;
-                                               if (!remove_path_from_gitmodules(path))
-                                                       gitmodules_modified = 1;
-                                               strbuf_release(&buf);
-                                               continue;
-                                       } else if (!file_exists(path))
-                                               /* Submodule was removed by user */
-                                               if (!remove_path_from_gitmodules(path))
-                                                       gitmodules_modified = 1;
-                                       /* Fallthrough and let remove_path() fail. */
-                               }
+                               struct strbuf buf = STRBUF_INIT;
+
+                               strbuf_addstr(&buf, path);
+                               if (remove_dir_recursively(&buf, 0))
+                                       die(_("could not remove '%s'"), path);
+                               strbuf_release(&buf);
+
+                               removed = 1;
+                               if (!remove_path_from_gitmodules(path))
+                                       gitmodules_modified = 1;
+                               continue;
                        }
                        if (!remove_path(path)) {
                                removed = 1;
@@ -421,7 +382,6 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
                        if (!removed)
                                die_errno("git rm: '%s'", path);
                }
-               strbuf_release(&buf);
                if (gitmodules_modified)
                        stage_updated_gitmodules();
        }
index df0d9c166f05b27469fbd6c1ddd208ac763667d7..74614a951e8bb8aef4ed0ef07665f8ec0e43cc78 100644 (file)
@@ -317,8 +317,12 @@ static void init_submodule(const char *path, const char *prefix, int quiet)
        /* Only loads from .gitmodules, no overlay with .git/config */
        gitmodules_config();
 
-       if (prefix) {
-               strbuf_addf(&sb, "%s%s", prefix, path);
+       if (prefix && get_super_prefix())
+               die("BUG: cannot have prefix and superprefix");
+       else if (prefix)
+               displaypath = xstrdup(relative_path(path, prefix, &sb));
+       else if (get_super_prefix()) {
+               strbuf_addf(&sb, "%s%s", get_super_prefix(), path);
                displaypath = strbuf_detach(&sb, NULL);
        } else
                displaypath = xstrdup(path);
@@ -403,9 +407,6 @@ static int module_init(int argc, const char **argv, const char *prefix)
        int i;
 
        struct option module_init_options[] = {
-               OPT_STRING(0, "prefix", &prefix,
-                          N_("path"),
-                          N_("alternative anchor for relative paths")),
                OPT__QUIET(&quiet, N_("Suppress output for initializing a submodule")),
                OPT_END()
        };
@@ -1144,7 +1145,7 @@ static struct cmd_struct commands[] = {
        {"relative-path", resolve_relative_path, 0},
        {"resolve-relative-url", resolve_relative_url, 0},
        {"resolve-relative-url-test", resolve_relative_url_test, 0},
-       {"init", module_init, 0},
+       {"init", module_init, SUPPORT_SUPER_PREFIX},
        {"remote-branch", resolve_remote_submodule_branch, 0},
        {"absorb-git-dirs", absorb_git_dirs, SUPPORT_SUPER_PREFIX},
 };
diff --git a/cache.h b/cache.h
index 1b67f078ddf7473cbde1351dc11405ec239e1373..00a029af3657d319b7df987a36979a519c2dda94 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -514,7 +514,6 @@ extern void set_git_work_tree(const char *tree);
 
 #define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES"
 
-extern const char **get_pathspec(const char *prefix, const char **pathspec);
 extern void setup_work_tree(void);
 extern const char *setup_git_directory_gently(int *);
 extern const char *setup_git_directory(void);
@@ -599,7 +598,7 @@ extern int chmod_index_entry(struct index_state *, struct cache_entry *ce, char
 extern int ce_same_name(const struct cache_entry *a, const struct cache_entry *b);
 extern void set_object_name_for_intent_to_add_entry(struct cache_entry *ce);
 extern int index_name_is_other(const struct index_state *, const char *, int);
-extern void *read_blob_data_from_index(struct index_state *, const char *, unsigned long *);
+extern void *read_blob_data_from_index(const struct index_state *, const char *, unsigned long *);
 
 /* do stat comparison even if CE_VALID is true */
 #define CE_MATCH_IGNORE_VALID          01
@@ -1064,8 +1063,11 @@ static inline int is_absolute_path(const char *path)
        return is_dir_sep(path[0]) || has_dos_drive_prefix(path);
 }
 int is_directory(const char *);
+char *strbuf_realpath(struct strbuf *resolved, const char *path,
+                     int die_on_error);
 const char *real_path(const char *path);
 const char *real_path_if_valid(const char *path);
+char *real_pathdup(const char *path);
 const char *absolute_path(const char *path);
 const char *remove_leading_path(const char *in, const char *prefix);
 const char *relative_path(const char *in, const char *prefix, struct strbuf *sb);
@@ -1691,6 +1693,8 @@ extern int git_default_config(const char *, const char *, void *);
 extern int git_config_from_file(config_fn_t fn, const char *, void *);
 extern int git_config_from_mem(config_fn_t fn, const enum config_origin_type,
                                        const char *name, const char *buf, size_t len, void *data);
+extern int git_config_from_blob_sha1(config_fn_t fn, const char *name,
+                                    const unsigned char *sha1, void *data);
 extern void git_config_push_parameter(const char *text);
 extern int git_config_from_parameters(config_fn_t fn, void *data);
 extern void git_config(config_fn_t fn, void *);
index 617b2e3cf4a9dcb61283ea797166d2bda812de6f..b680f79732aa867d39a2e10ac889b5b2abfb1e7f 100644 (file)
--- a/config.c
+++ b/config.c
@@ -1236,10 +1236,10 @@ int git_config_from_mem(config_fn_t fn, const enum config_origin_type origin_typ
        return do_config_from(&top, fn, data);
 }
 
-static int git_config_from_blob_sha1(config_fn_t fn,
-                                    const char *name,
-                                    const unsigned char *sha1,
-                                    void *data)
+int git_config_from_blob_sha1(config_fn_t fn,
+                             const char *name,
+                             const unsigned char *sha1,
+                             void *data)
 {
        enum object_type type;
        char *buf;
diff --git a/contrib/gitview/gitview b/contrib/gitview/gitview
deleted file mode 100755 (executable)
index 4e23c65..0000000
+++ /dev/null
@@ -1,1305 +0,0 @@
-#! /usr/bin/env python
-
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-
-""" gitview
-GUI browser for git repository
-This program is based on bzrk by Scott James Remnant <scott@ubuntu.com>
-"""
-__copyright__ = "Copyright (C) 2006 Hewlett-Packard Development Company, L.P."
-__copyright__ = "Copyright (C) 2007 Aneesh Kumar K.V <aneesh.kumar@gmail.com"
-__author__    = "Aneesh Kumar K.V <aneesh.kumar@gmail.com>"
-
-
-import sys
-import os
-import gtk
-import pygtk
-import pango
-import re
-import time
-import gobject
-import cairo
-import math
-import string
-import fcntl
-
-have_gtksourceview2 = False
-have_gtksourceview = False
-try:
-    import gtksourceview2
-    have_gtksourceview2 = True
-except ImportError:
-    try:
-        import gtksourceview
-        have_gtksourceview = True
-    except ImportError:
-        print "Running without gtksourceview2 or gtksourceview module"
-
-re_ident = re.compile('(author|committer) (?P<ident>.*) (?P<epoch>\d+) (?P<tz>[+-]\d{4})')
-
-def list_to_string(args, skip):
-       count = len(args)
-       i = skip
-       str_arg=" "
-       while (i < count ):
-               str_arg = str_arg + args[i]
-               str_arg = str_arg + " "
-               i = i+1
-
-       return str_arg
-
-def show_date(epoch, tz):
-       secs = float(epoch)
-       tzsecs = float(tz[1:3]) * 3600
-       tzsecs += float(tz[3:5]) * 60
-       if (tz[0] == "+"):
-               secs += tzsecs
-       else:
-               secs -= tzsecs
-
-       return time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(secs))
-
-def get_source_buffer_and_view():
-       if have_gtksourceview2:
-               buffer = gtksourceview2.Buffer()
-               slm = gtksourceview2.LanguageManager()
-               gsl = slm.get_language("diff")
-               buffer.set_highlight_syntax(True)
-               buffer.set_language(gsl)
-               view = gtksourceview2.View(buffer)
-       elif have_gtksourceview:
-               buffer = gtksourceview.SourceBuffer()
-               slm = gtksourceview.SourceLanguagesManager()
-               gsl = slm.get_language_from_mime_type("text/x-patch")
-               buffer.set_highlight(True)
-               buffer.set_language(gsl)
-               view = gtksourceview.SourceView(buffer)
-       else:
-               buffer = gtk.TextBuffer()
-               view = gtk.TextView(buffer)
-       return (buffer, view)
-
-
-class CellRendererGraph(gtk.GenericCellRenderer):
-       """Cell renderer for directed graph.
-
-       This module contains the implementation of a custom GtkCellRenderer that
-       draws part of the directed graph based on the lines suggested by the code
-       in graph.py.
-
-       Because we're shiny, we use Cairo to do this, and because we're naughty
-       we cheat and draw over the bits of the TreeViewColumn that are supposed to
-       just be for the background.
-
-       Properties:
-       node              (column, colour, [ names ]) tuple to draw revision node,
-       in_lines          (start, end, colour) tuple list to draw inward lines,
-       out_lines         (start, end, colour) tuple list to draw outward lines.
-       """
-
-       __gproperties__ = {
-       "node":         ( gobject.TYPE_PYOBJECT, "node",
-                         "revision node instruction",
-                         gobject.PARAM_WRITABLE
-                       ),
-       "in-lines":     ( gobject.TYPE_PYOBJECT, "in-lines",
-                         "instructions to draw lines into the cell",
-                         gobject.PARAM_WRITABLE
-                       ),
-       "out-lines":    ( gobject.TYPE_PYOBJECT, "out-lines",
-                         "instructions to draw lines out of the cell",
-                         gobject.PARAM_WRITABLE
-                       ),
-       }
-
-       def do_set_property(self, property, value):
-               """Set properties from GObject properties."""
-               if property.name == "node":
-                       self.node = value
-               elif property.name == "in-lines":
-                       self.in_lines = value
-               elif property.name == "out-lines":
-                       self.out_lines = value
-               else:
-                       raise AttributeError, "no such property: '%s'" % property.name
-
-       def box_size(self, widget):
-               """Calculate box size based on widget's font.
-
-               Cache this as it's probably expensive to get.  It ensures that we
-               draw the graph at least as large as the text.
-               """
-               try:
-                       return self._box_size
-               except AttributeError:
-                       pango_ctx = widget.get_pango_context()
-                       font_desc = widget.get_style().font_desc
-                       metrics = pango_ctx.get_metrics(font_desc)
-
-                       ascent = pango.PIXELS(metrics.get_ascent())
-                       descent = pango.PIXELS(metrics.get_descent())
-
-                       self._box_size = ascent + descent + 6
-                       return self._box_size
-
-       def set_colour(self, ctx, colour, bg, fg):
-               """Set the context source colour.
-
-               Picks a distinct colour based on an internal wheel; the bg
-               parameter provides the value that should be assigned to the 'zero'
-               colours and the fg parameter provides the multiplier that should be
-               applied to the foreground colours.
-               """
-               colours = [
-                   ( 1.0, 0.0, 0.0 ),
-                   ( 1.0, 1.0, 0.0 ),
-                   ( 0.0, 1.0, 0.0 ),
-                   ( 0.0, 1.0, 1.0 ),
-                   ( 0.0, 0.0, 1.0 ),
-                   ( 1.0, 0.0, 1.0 ),
-                   ]
-
-               colour %= len(colours)
-               red   = (colours[colour][0] * fg) or bg
-               green = (colours[colour][1] * fg) or bg
-               blue  = (colours[colour][2] * fg) or bg
-
-               ctx.set_source_rgb(red, green, blue)
-
-       def on_get_size(self, widget, cell_area):
-               """Return the size we need for this cell.
-
-               Each cell is drawn individually and is only as wide as it needs
-               to be, we let the TreeViewColumn take care of making them all
-               line up.
-               """
-               box_size = self.box_size(widget)
-
-               cols = self.node[0]
-               for start, end, colour in self.in_lines + self.out_lines:
-                       cols = int(max(cols, start, end))
-
-               (column, colour, names) = self.node
-               names_len = 0
-               if (len(names) != 0):
-                       for item in names:
-                               names_len += len(item)
-
-               width = box_size * (cols + 1 ) + names_len
-               height = box_size
-
-               # FIXME I have no idea how to use cell_area properly
-               return (0, 0, width, height)
-
-       def on_render(self, window, widget, bg_area, cell_area, exp_area, flags):
-               """Render an individual cell.
-
-               Draws the cell contents using cairo, taking care to clip what we
-               do to within the background area so we don't draw over other cells.
-               Note that we're a bit naughty there and should really be drawing
-               in the cell_area (or even the exposed area), but we explicitly don't
-               want any gutter.
-
-               We try and be a little clever, if the line we need to draw is going
-               to cross other columns we actually draw it as in the .---' style
-               instead of a pure diagonal ... this reduces confusion by an
-               incredible amount.
-               """
-               ctx = window.cairo_create()
-               ctx.rectangle(bg_area.x, bg_area.y, bg_area.width, bg_area.height)
-               ctx.clip()
-
-               box_size = self.box_size(widget)
-
-               ctx.set_line_width(box_size / 8)
-               ctx.set_line_cap(cairo.LINE_CAP_SQUARE)
-
-               # Draw lines into the cell
-               for start, end, colour in self.in_lines:
-                       ctx.move_to(cell_area.x + box_size * start + box_size / 2,
-                                       bg_area.y - bg_area.height / 2)
-
-                       if start - end > 1:
-                               ctx.line_to(cell_area.x + box_size * start, bg_area.y)
-                               ctx.line_to(cell_area.x + box_size * end + box_size, bg_area.y)
-                       elif start - end < -1:
-                               ctx.line_to(cell_area.x + box_size * start + box_size,
-                                               bg_area.y)
-                               ctx.line_to(cell_area.x + box_size * end, bg_area.y)
-
-                       ctx.line_to(cell_area.x + box_size * end + box_size / 2,
-                                       bg_area.y + bg_area.height / 2)
-
-                       self.set_colour(ctx, colour, 0.0, 0.65)
-                       ctx.stroke()
-
-               # Draw lines out of the cell
-               for start, end, colour in self.out_lines:
-                       ctx.move_to(cell_area.x + box_size * start + box_size / 2,
-                                       bg_area.y + bg_area.height / 2)
-
-                       if start - end > 1:
-                               ctx.line_to(cell_area.x + box_size * start,
-                                               bg_area.y + bg_area.height)
-                               ctx.line_to(cell_area.x + box_size * end + box_size,
-                                               bg_area.y + bg_area.height)
-                       elif start - end < -1:
-                               ctx.line_to(cell_area.x + box_size * start + box_size,
-                                               bg_area.y + bg_area.height)
-                               ctx.line_to(cell_area.x + box_size * end,
-                                               bg_area.y + bg_area.height)
-
-                       ctx.line_to(cell_area.x + box_size * end + box_size / 2,
-                                       bg_area.y + bg_area.height / 2 + bg_area.height)
-
-                       self.set_colour(ctx, colour, 0.0, 0.65)
-                       ctx.stroke()
-
-               # Draw the revision node in the right column
-               (column, colour, names) = self.node
-               ctx.arc(cell_area.x + box_size * column + box_size / 2,
-                               cell_area.y + cell_area.height / 2,
-                               box_size / 4, 0, 2 * math.pi)
-
-
-               self.set_colour(ctx, colour, 0.0, 0.5)
-               ctx.stroke_preserve()
-
-               self.set_colour(ctx, colour, 0.5, 1.0)
-               ctx.fill_preserve()
-
-               if (len(names) != 0):
-                       name = " "
-                       for item in names:
-                               name = name + item + " "
-
-                       ctx.set_font_size(13)
-                       if (flags & 1):
-                               self.set_colour(ctx, colour, 0.5, 1.0)
-                       else:
-                               self.set_colour(ctx, colour, 0.0, 0.5)
-                       ctx.show_text(name)
-
-class Commit(object):
-       """ This represent a commit object obtained after parsing the git-rev-list
-       output """
-
-       __slots__ = ['children_sha1', 'message', 'author', 'date', 'committer',
-                                'commit_date', 'commit_sha1', 'parent_sha1']
-
-       children_sha1 = {}
-
-       def __init__(self, commit_lines):
-               self.message            = ""
-               self.author             = ""
-               self.date               = ""
-               self.committer          = ""
-               self.commit_date        = ""
-               self.commit_sha1        = ""
-               self.parent_sha1        = [ ]
-               self.parse_commit(commit_lines)
-
-
-       def parse_commit(self, commit_lines):
-
-               # First line is the sha1 lines
-               line = string.strip(commit_lines[0])
-               sha1 = re.split(" ", line)
-               self.commit_sha1 = sha1[0]
-               self.parent_sha1 = sha1[1:]
-
-               #build the child list
-               for parent_id in self.parent_sha1:
-                       try:
-                               Commit.children_sha1[parent_id].append(self.commit_sha1)
-                       except KeyError:
-                               Commit.children_sha1[parent_id] = [self.commit_sha1]
-
-               # IF we don't have parent
-               if (len(self.parent_sha1) == 0):
-                       self.parent_sha1 = [0]
-
-               for line in commit_lines[1:]:
-                       m = re.match("^ ", line)
-                       if (m != None):
-                               # First line of the commit message used for short log
-                               if self.message == "":
-                                       self.message = string.strip(line)
-                               continue
-
-                       m = re.match("tree", line)
-                       if (m != None):
-                               continue
-
-                       m = re.match("parent", line)
-                       if (m != None):
-                               continue
-
-                       m = re_ident.match(line)
-                       if (m != None):
-                               date = show_date(m.group('epoch'), m.group('tz'))
-                               if m.group(1) == "author":
-                                       self.author = m.group('ident')
-                                       self.date = date
-                               elif m.group(1) == "committer":
-                                       self.committer = m.group('ident')
-                                       self.commit_date = date
-
-                               continue
-
-       def get_message(self, with_diff=0):
-               if (with_diff == 1):
-                       message = self.diff_tree()
-               else:
-                       fp = os.popen("git cat-file commit " + self.commit_sha1)
-                       message = fp.read()
-                       fp.close()
-
-               return message
-
-       def diff_tree(self):
-               fp = os.popen("git diff-tree --pretty --cc  -v -p --always " +  self.commit_sha1)
-               diff = fp.read()
-               fp.close()
-               return diff
-
-class AnnotateWindow(object):
-       """Annotate window.
-       This object represents and manages a single window containing the
-       annotate information of the file
-       """
-
-       def __init__(self):
-               self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
-               self.window.set_border_width(0)
-               self.window.set_title("Git repository browser annotation window")
-               self.prev_read = ""
-
-               # Use two thirds of the screen by default
-               screen = self.window.get_screen()
-               monitor = screen.get_monitor_geometry(0)
-               width = int(monitor.width * 0.66)
-               height = int(monitor.height * 0.66)
-               self.window.set_default_size(width, height)
-
-       def add_file_data(self, filename, commit_sha1, line_num):
-               fp = os.popen("git cat-file blob " + commit_sha1 +":"+filename)
-               i = 1;
-               for line in fp.readlines():
-                       line = string.rstrip(line)
-                       self.model.append(None, ["HEAD", filename, line, i])
-                       i = i+1
-               fp.close()
-
-               # now set the cursor position
-               self.treeview.set_cursor(line_num-1)
-               self.treeview.grab_focus()
-
-       def _treeview_cursor_cb(self, *args):
-               """Callback for when the treeview cursor changes."""
-               (path, col) = self.treeview.get_cursor()
-               commit_sha1 = self.model[path][0]
-               commit_msg = ""
-               fp = os.popen("git cat-file commit " + commit_sha1)
-               for line in fp.readlines():
-                       commit_msg =  commit_msg + line
-               fp.close()
-
-               self.commit_buffer.set_text(commit_msg)
-
-       def _treeview_row_activated(self, *args):
-               """Callback for when the treeview row gets selected."""
-               (path, col) = self.treeview.get_cursor()
-               commit_sha1 = self.model[path][0]
-               filename    = self.model[path][1]
-               line_num    = self.model[path][3]
-
-               window = AnnotateWindow();
-               fp = os.popen("git rev-parse "+ commit_sha1 + "~1")
-               commit_sha1 = string.strip(fp.readline())
-               fp.close()
-               window.annotate(filename, commit_sha1, line_num)
-
-       def data_ready(self, source, condition):
-               while (1):
-                       try :
-                               # A simple readline doesn't work
-                               # a readline bug ??
-                               buffer = source.read(100)
-
-                       except:
-                               # resource temporary not available
-                               return True
-
-                       if (len(buffer) == 0):
-                               gobject.source_remove(self.io_watch_tag)
-                               source.close()
-                               return False
-
-                       if (self.prev_read != ""):
-                               buffer = self.prev_read + buffer
-                               self.prev_read = ""
-
-                       if (buffer[len(buffer) -1] != '\n'):
-                               try:
-                                       newline_index = buffer.rindex("\n")
-                               except ValueError:
-                                       newline_index = 0
-
-                               self.prev_read = buffer[newline_index:(len(buffer))]
-                               buffer = buffer[0:newline_index]
-
-                       for buff in buffer.split("\n"):
-                               annotate_line = re.compile('^([0-9a-f]{40}) (.+) (.+) (.+)$')
-                               m = annotate_line.match(buff)
-                               if not m:
-                                       annotate_line = re.compile('^(filename) (.+)$')
-                                       m = annotate_line.match(buff)
-                                       if not m:
-                                               continue
-                                       filename = m.group(2)
-                               else:
-                                       self.commit_sha1 = m.group(1)
-                                       self.source_line = int(m.group(2))
-                                       self.result_line = int(m.group(3))
-                                       self.count          = int(m.group(4))
-                                       #set the details only when we have the file name
-                                       continue
-
-                               while (self.count > 0):
-                                       # set at result_line + count-1 the sha1 as commit_sha1
-                                       self.count = self.count - 1
-                                       iter = self.model.iter_nth_child(None, self.result_line + self.count-1)
-                                       self.model.set(iter, 0, self.commit_sha1, 1, filename, 3, self.source_line)
-
-
-       def annotate(self, filename, commit_sha1, line_num):
-               # verify the commit_sha1 specified has this filename
-
-               fp = os.popen("git ls-tree "+ commit_sha1 + " -- " + filename)
-               line = string.strip(fp.readline())
-               if line == '':
-                       # pop up the message the file is not there as a part of the commit
-                       fp.close()
-                       dialog = gtk.MessageDialog(parent=None, flags=0,
-                                       type=gtk.MESSAGE_WARNING, buttons=gtk.BUTTONS_CLOSE,
-                                       message_format=None)
-                       dialog.set_markup("The file %s is not present in the parent commit %s" % (filename, commit_sha1))
-                       dialog.run()
-                       dialog.destroy()
-                       return
-
-               fp.close()
-
-               vpan = gtk.VPaned();
-               self.window.add(vpan);
-               vpan.show()
-
-               scrollwin = gtk.ScrolledWindow()
-               scrollwin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
-               scrollwin.set_shadow_type(gtk.SHADOW_IN)
-               vpan.pack1(scrollwin, True, True);
-               scrollwin.show()
-
-               self.model = gtk.TreeStore(str, str, str, int)
-               self.treeview = gtk.TreeView(self.model)
-               self.treeview.set_rules_hint(True)
-               self.treeview.set_search_column(0)
-               self.treeview.connect("cursor-changed", self._treeview_cursor_cb)
-               self.treeview.connect("row-activated", self._treeview_row_activated)
-               scrollwin.add(self.treeview)
-               self.treeview.show()
-
-               cell = gtk.CellRendererText()
-               cell.set_property("width-chars", 10)
-               cell.set_property("ellipsize", pango.ELLIPSIZE_END)
-               column = gtk.TreeViewColumn("Commit")
-               column.set_resizable(True)
-               column.pack_start(cell, expand=True)
-               column.add_attribute(cell, "text", 0)
-               self.treeview.append_column(column)
-
-               cell = gtk.CellRendererText()
-               cell.set_property("width-chars", 20)
-               cell.set_property("ellipsize", pango.ELLIPSIZE_END)
-               column = gtk.TreeViewColumn("File Name")
-               column.set_resizable(True)
-               column.pack_start(cell, expand=True)
-               column.add_attribute(cell, "text", 1)
-               self.treeview.append_column(column)
-
-               cell = gtk.CellRendererText()
-               cell.set_property("width-chars", 20)
-               cell.set_property("ellipsize", pango.ELLIPSIZE_END)
-               column = gtk.TreeViewColumn("Data")
-               column.set_resizable(True)
-               column.pack_start(cell, expand=True)
-               column.add_attribute(cell, "text", 2)
-               self.treeview.append_column(column)
-
-               # The commit message window
-               scrollwin = gtk.ScrolledWindow()
-               scrollwin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
-               scrollwin.set_shadow_type(gtk.SHADOW_IN)
-               vpan.pack2(scrollwin, True, True);
-               scrollwin.show()
-
-               commit_text = gtk.TextView()
-               self.commit_buffer = gtk.TextBuffer()
-               commit_text.set_buffer(self.commit_buffer)
-               scrollwin.add(commit_text)
-               commit_text.show()
-
-               self.window.show()
-
-               self.add_file_data(filename, commit_sha1, line_num)
-
-               fp = os.popen("git blame --incremental -C -C -- " + filename + " " + commit_sha1)
-               flags = fcntl.fcntl(fp.fileno(), fcntl.F_GETFL)
-               fcntl.fcntl(fp.fileno(), fcntl.F_SETFL, flags | os.O_NONBLOCK)
-               self.io_watch_tag = gobject.io_add_watch(fp, gobject.IO_IN, self.data_ready)
-
-
-class DiffWindow(object):
-       """Diff window.
-       This object represents and manages a single window containing the
-       differences between two revisions on a branch.
-       """
-
-       def __init__(self):
-               self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
-               self.window.set_border_width(0)
-               self.window.set_title("Git repository browser diff window")
-
-               # Use two thirds of the screen by default
-               screen = self.window.get_screen()
-               monitor = screen.get_monitor_geometry(0)
-               width = int(monitor.width * 0.66)
-               height = int(monitor.height * 0.66)
-               self.window.set_default_size(width, height)
-
-
-               self.construct()
-
-       def construct(self):
-               """Construct the window contents."""
-               vbox = gtk.VBox()
-               self.window.add(vbox)
-               vbox.show()
-
-               menu_bar = gtk.MenuBar()
-               save_menu = gtk.ImageMenuItem(gtk.STOCK_SAVE)
-               save_menu.connect("activate", self.save_menu_response, "save")
-               save_menu.show()
-               menu_bar.append(save_menu)
-               vbox.pack_start(menu_bar, expand=False, fill=True)
-               menu_bar.show()
-
-               hpan = gtk.HPaned()
-
-               scrollwin = gtk.ScrolledWindow()
-               scrollwin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
-               scrollwin.set_shadow_type(gtk.SHADOW_IN)
-               hpan.pack1(scrollwin, True, True)
-               scrollwin.show()
-
-               (self.buffer, sourceview) = get_source_buffer_and_view()
-
-               sourceview.set_editable(False)
-               sourceview.modify_font(pango.FontDescription("Monospace"))
-               scrollwin.add(sourceview)
-               sourceview.show()
-
-               # The file hierarchy: a scrollable treeview
-               scrollwin = gtk.ScrolledWindow()
-               scrollwin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
-               scrollwin.set_shadow_type(gtk.SHADOW_IN)
-               scrollwin.set_size_request(20, -1)
-               hpan.pack2(scrollwin, True, True)
-               scrollwin.show()
-
-               self.model = gtk.TreeStore(str, str, str)
-               self.treeview = gtk.TreeView(self.model)
-               self.treeview.set_search_column(1)
-               self.treeview.connect("cursor-changed", self._treeview_clicked)
-               scrollwin.add(self.treeview)
-               self.treeview.show()
-
-               cell = gtk.CellRendererText()
-               cell.set_property("width-chars", 20)
-               column = gtk.TreeViewColumn("Select to annotate")
-               column.pack_start(cell, expand=True)
-               column.add_attribute(cell, "text", 0)
-               self.treeview.append_column(column)
-
-               vbox.pack_start(hpan, expand=True, fill=True)
-               hpan.show()
-
-       def _treeview_clicked(self, *args):
-               """Callback for when the treeview cursor changes."""
-               (path, col) = self.treeview.get_cursor()
-               specific_file = self.model[path][1]
-               commit_sha1 =  self.model[path][2]
-               if specific_file ==  None :
-                       return
-               elif specific_file ==  "" :
-                       specific_file =  None
-
-               window = AnnotateWindow();
-               window.annotate(specific_file, commit_sha1, 1)
-
-
-       def commit_files(self, commit_sha1, parent_sha1):
-               self.model.clear()
-               add  = self.model.append(None, [ "Added", None, None])
-               dele = self.model.append(None, [ "Deleted", None, None])
-               mod  = self.model.append(None, [ "Modified", None, None])
-               diff_tree = re.compile('^(:.{6}) (.{6}) (.{40}) (.{40}) (A|D|M)\s(.+)$')
-               fp = os.popen("git diff-tree -r --no-commit-id " + parent_sha1 + " " + commit_sha1)
-               while 1:
-                       line = string.strip(fp.readline())
-                       if line == '':
-                               break
-                       m = diff_tree.match(line)
-                       if not m:
-                               continue
-
-                       attr = m.group(5)
-                       filename = m.group(6)
-                       if attr == "A":
-                               self.model.append(add,  [filename, filename, commit_sha1])
-                       elif attr == "D":
-                               self.model.append(dele, [filename, filename, commit_sha1])
-                       elif attr == "M":
-                               self.model.append(mod,  [filename, filename, commit_sha1])
-               fp.close()
-
-               self.treeview.expand_all()
-
-       def set_diff(self, commit_sha1, parent_sha1, encoding):
-               """Set the differences showed by this window.
-               Compares the two trees and populates the window with the
-               differences.
-               """
-               # Diff with the first commit or the last commit shows nothing
-               if (commit_sha1 == 0 or parent_sha1 == 0 ):
-                       return
-
-               fp = os.popen("git diff-tree -p " + parent_sha1 + " " + commit_sha1)
-               self.buffer.set_text(unicode(fp.read(), encoding).encode('utf-8'))
-               fp.close()
-               self.commit_files(commit_sha1, parent_sha1)
-               self.window.show()
-
-       def save_menu_response(self, widget, string):
-               dialog = gtk.FileChooserDialog("Save..", None, gtk.FILE_CHOOSER_ACTION_SAVE,
-                               (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
-                                       gtk.STOCK_SAVE, gtk.RESPONSE_OK))
-               dialog.set_default_response(gtk.RESPONSE_OK)
-               response = dialog.run()
-               if response == gtk.RESPONSE_OK:
-                       patch_buffer = self.buffer.get_text(self.buffer.get_start_iter(),
-                                       self.buffer.get_end_iter())
-                       fp = open(dialog.get_filename(), "w")
-                       fp.write(patch_buffer)
-                       fp.close()
-               dialog.destroy()
-
-class GitView(object):
-       """ This is the main class
-       """
-       version = "0.9"
-
-       def __init__(self, with_diff=0):
-               self.with_diff = with_diff
-               self.window =   gtk.Window(gtk.WINDOW_TOPLEVEL)
-               self.window.set_border_width(0)
-               self.window.set_title("Git repository browser")
-
-               self.get_encoding()
-               self.get_bt_sha1()
-
-               # Use three-quarters of the screen by default
-               screen = self.window.get_screen()
-               monitor = screen.get_monitor_geometry(0)
-               width = int(monitor.width * 0.75)
-               height = int(monitor.height * 0.75)
-               self.window.set_default_size(width, height)
-
-               # FIXME AndyFitz!
-               icon = self.window.render_icon(gtk.STOCK_INDEX, gtk.ICON_SIZE_BUTTON)
-               self.window.set_icon(icon)
-
-               self.accel_group = gtk.AccelGroup()
-               self.window.add_accel_group(self.accel_group)
-               self.accel_group.connect_group(0xffc2, 0, gtk.ACCEL_LOCKED, self.refresh);
-               self.accel_group.connect_group(0xffc1, 0, gtk.ACCEL_LOCKED, self.maximize);
-               self.accel_group.connect_group(0xffc8, 0, gtk.ACCEL_LOCKED, self.fullscreen);
-               self.accel_group.connect_group(0xffc9, 0, gtk.ACCEL_LOCKED, self.unfullscreen);
-
-               self.window.add(self.construct())
-
-       def refresh(self, widget, event=None, *arguments, **keywords):
-               self.get_encoding()
-               self.get_bt_sha1()
-               Commit.children_sha1 = {}
-               self.set_branch(sys.argv[without_diff:])
-               self.window.show()
-               return True
-
-       def maximize(self, widget, event=None, *arguments, **keywords):
-               self.window.maximize()
-               return True
-
-       def fullscreen(self, widget, event=None, *arguments, **keywords):
-               self.window.fullscreen()
-               return True
-
-       def unfullscreen(self, widget, event=None, *arguments, **keywords):
-               self.window.unfullscreen()
-               return True
-
-       def get_bt_sha1(self):
-               """ Update the bt_sha1 dictionary with the
-               respective sha1 details """
-
-               self.bt_sha1 = { }
-               ls_remote = re.compile('^(.{40})\trefs/([^^]+)(?:\\^(..))?$');
-               fp = os.popen('git ls-remote "${GIT_DIR-.git}"')
-               while 1:
-                       line = string.strip(fp.readline())
-                       if line == '':
-                               break
-                       m = ls_remote.match(line)
-                       if not m:
-                               continue
-                       (sha1, name) = (m.group(1), m.group(2))
-                       if not self.bt_sha1.has_key(sha1):
-                               self.bt_sha1[sha1] = []
-                       self.bt_sha1[sha1].append(name)
-               fp.close()
-
-       def get_encoding(self):
-               fp = os.popen("git config --get i18n.commitencoding")
-               self.encoding=string.strip(fp.readline())
-               fp.close()
-               if (self.encoding == ""):
-                       self.encoding = "utf-8"
-
-
-       def construct(self):
-               """Construct the window contents."""
-               vbox = gtk.VBox()
-               paned = gtk.VPaned()
-               paned.pack1(self.construct_top(), resize=False, shrink=True)
-               paned.pack2(self.construct_bottom(), resize=False, shrink=True)
-               menu_bar = gtk.MenuBar()
-               menu_bar.set_pack_direction(gtk.PACK_DIRECTION_RTL)
-               help_menu = gtk.MenuItem("Help")
-               menu = gtk.Menu()
-               about_menu = gtk.MenuItem("About")
-               menu.append(about_menu)
-               about_menu.connect("activate", self.about_menu_response, "about")
-               about_menu.show()
-               help_menu.set_submenu(menu)
-               help_menu.show()
-               menu_bar.append(help_menu)
-               menu_bar.show()
-               vbox.pack_start(menu_bar, expand=False, fill=True)
-               vbox.pack_start(paned, expand=True, fill=True)
-               paned.show()
-               vbox.show()
-               return vbox
-
-
-       def construct_top(self):
-               """Construct the top-half of the window."""
-               vbox = gtk.VBox(spacing=6)
-               vbox.set_border_width(12)
-               vbox.show()
-
-
-               scrollwin = gtk.ScrolledWindow()
-               scrollwin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
-               scrollwin.set_shadow_type(gtk.SHADOW_IN)
-               vbox.pack_start(scrollwin, expand=True, fill=True)
-               scrollwin.show()
-
-               self.treeview = gtk.TreeView()
-               self.treeview.set_rules_hint(True)
-               self.treeview.set_search_column(4)
-               self.treeview.connect("cursor-changed", self._treeview_cursor_cb)
-               scrollwin.add(self.treeview)
-               self.treeview.show()
-
-               cell = CellRendererGraph()
-               column = gtk.TreeViewColumn()
-               column.set_resizable(True)
-               column.pack_start(cell, expand=True)
-               column.add_attribute(cell, "node", 1)
-               column.add_attribute(cell, "in-lines", 2)
-               column.add_attribute(cell, "out-lines", 3)
-               self.treeview.append_column(column)
-
-               cell = gtk.CellRendererText()
-               cell.set_property("width-chars", 65)
-               cell.set_property("ellipsize", pango.ELLIPSIZE_END)
-               column = gtk.TreeViewColumn("Message")
-               column.set_resizable(True)
-               column.pack_start(cell, expand=True)
-               column.add_attribute(cell, "text", 4)
-               self.treeview.append_column(column)
-
-               cell = gtk.CellRendererText()
-               cell.set_property("width-chars", 40)
-               cell.set_property("ellipsize", pango.ELLIPSIZE_END)
-               column = gtk.TreeViewColumn("Author")
-               column.set_resizable(True)
-               column.pack_start(cell, expand=True)
-               column.add_attribute(cell, "text", 5)
-               self.treeview.append_column(column)
-
-               cell = gtk.CellRendererText()
-               cell.set_property("ellipsize", pango.ELLIPSIZE_END)
-               column = gtk.TreeViewColumn("Date")
-               column.set_resizable(True)
-               column.pack_start(cell, expand=True)
-               column.add_attribute(cell, "text", 6)
-               self.treeview.append_column(column)
-
-               return vbox
-
-       def about_menu_response(self, widget, string):
-               dialog = gtk.AboutDialog()
-               dialog.set_name("Gitview")
-               dialog.set_version(GitView.version)
-               dialog.set_authors(["Aneesh Kumar K.V <aneesh.kumar@gmail.com>"])
-               dialog.set_website("http://www.kernel.org/pub/software/scm/git/")
-               dialog.set_copyright("Use and distribute under the terms of the GNU General Public License")
-               dialog.set_wrap_license(True)
-               dialog.run()
-               dialog.destroy()
-
-
-       def construct_bottom(self):
-               """Construct the bottom half of the window."""
-               vbox = gtk.VBox(False, spacing=6)
-               vbox.set_border_width(12)
-               (width, height) = self.window.get_size()
-               vbox.set_size_request(width, int(height / 2.5))
-               vbox.show()
-
-               self.table = gtk.Table(rows=4, columns=4)
-               self.table.set_row_spacings(6)
-               self.table.set_col_spacings(6)
-               vbox.pack_start(self.table, expand=False, fill=True)
-               self.table.show()
-
-               align = gtk.Alignment(0.0, 0.5)
-               label = gtk.Label()
-               label.set_markup("<b>Revision:</b>")
-               align.add(label)
-               self.table.attach(align, 0, 1, 0, 1, gtk.FILL, gtk.FILL)
-               label.show()
-               align.show()
-
-               align = gtk.Alignment(0.0, 0.5)
-               self.revid_label = gtk.Label()
-               self.revid_label.set_selectable(True)
-               align.add(self.revid_label)
-               self.table.attach(align, 1, 2, 0, 1, gtk.EXPAND | gtk.FILL, gtk.FILL)
-               self.revid_label.show()
-               align.show()
-
-               align = gtk.Alignment(0.0, 0.5)
-               label = gtk.Label()
-               label.set_markup("<b>Committer:</b>")
-               align.add(label)
-               self.table.attach(align, 0, 1, 1, 2, gtk.FILL, gtk.FILL)
-               label.show()
-               align.show()
-
-               align = gtk.Alignment(0.0, 0.5)
-               self.committer_label = gtk.Label()
-               self.committer_label.set_selectable(True)
-               align.add(self.committer_label)
-               self.table.attach(align, 1, 2, 1, 2, gtk.EXPAND | gtk.FILL, gtk.FILL)
-               self.committer_label.show()
-               align.show()
-
-               align = gtk.Alignment(0.0, 0.5)
-               label = gtk.Label()
-               label.set_markup("<b>Timestamp:</b>")
-               align.add(label)
-               self.table.attach(align, 0, 1, 2, 3, gtk.FILL, gtk.FILL)
-               label.show()
-               align.show()
-
-               align = gtk.Alignment(0.0, 0.5)
-               self.timestamp_label = gtk.Label()
-               self.timestamp_label.set_selectable(True)
-               align.add(self.timestamp_label)
-               self.table.attach(align, 1, 2, 2, 3, gtk.EXPAND | gtk.FILL, gtk.FILL)
-               self.timestamp_label.show()
-               align.show()
-
-               align = gtk.Alignment(0.0, 0.5)
-               label = gtk.Label()
-               label.set_markup("<b>Parents:</b>")
-               align.add(label)
-               self.table.attach(align, 0, 1, 3, 4, gtk.FILL, gtk.FILL)
-               label.show()
-               align.show()
-               self.parents_widgets = []
-
-               align = gtk.Alignment(0.0, 0.5)
-               label = gtk.Label()
-               label.set_markup("<b>Children:</b>")
-               align.add(label)
-               self.table.attach(align, 2, 3, 3, 4, gtk.FILL, gtk.FILL)
-               label.show()
-               align.show()
-               self.children_widgets = []
-
-               scrollwin = gtk.ScrolledWindow()
-               scrollwin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
-               scrollwin.set_shadow_type(gtk.SHADOW_IN)
-               vbox.pack_start(scrollwin, expand=True, fill=True)
-               scrollwin.show()
-
-               (self.message_buffer, sourceview) = get_source_buffer_and_view()
-
-               sourceview.set_editable(False)
-               sourceview.modify_font(pango.FontDescription("Monospace"))
-               scrollwin.add(sourceview)
-               sourceview.show()
-
-               return vbox
-
-       def _treeview_cursor_cb(self, *args):
-               """Callback for when the treeview cursor changes."""
-               (path, col) = self.treeview.get_cursor()
-               commit = self.model[path][0]
-
-               if commit.committer is not None:
-                       committer = commit.committer
-                       timestamp = commit.commit_date
-                       message   =  commit.get_message(self.with_diff)
-                       revid_label = commit.commit_sha1
-               else:
-                       committer = ""
-                       timestamp = ""
-                       message = ""
-                       revid_label = ""
-
-               self.revid_label.set_text(revid_label)
-               self.committer_label.set_text(committer)
-               self.timestamp_label.set_text(timestamp)
-               self.message_buffer.set_text(unicode(message, self.encoding).encode('utf-8'))
-
-               for widget in self.parents_widgets:
-                       self.table.remove(widget)
-
-               self.parents_widgets = []
-               self.table.resize(4 + len(commit.parent_sha1) - 1, 4)
-               for idx, parent_id in enumerate(commit.parent_sha1):
-                       self.table.set_row_spacing(idx + 3, 0)
-
-                       align = gtk.Alignment(0.0, 0.0)
-                       self.parents_widgets.append(align)
-                       self.table.attach(align, 1, 2, idx + 3, idx + 4,
-                                       gtk.EXPAND | gtk.FILL, gtk.FILL)
-                       align.show()
-
-                       hbox = gtk.HBox(False, 0)
-                       align.add(hbox)
-                       hbox.show()
-
-                       label = gtk.Label(parent_id)
-                       label.set_selectable(True)
-                       hbox.pack_start(label, expand=False, fill=True)
-                       label.show()
-
-                       image = gtk.Image()
-                       image.set_from_stock(gtk.STOCK_JUMP_TO, gtk.ICON_SIZE_MENU)
-                       image.show()
-
-                       button = gtk.Button()
-                       button.add(image)
-                       button.set_relief(gtk.RELIEF_NONE)
-                       button.connect("clicked", self._go_clicked_cb, parent_id)
-                       hbox.pack_start(button, expand=False, fill=True)
-                       button.show()
-
-                       image = gtk.Image()
-                       image.set_from_stock(gtk.STOCK_FIND, gtk.ICON_SIZE_MENU)
-                       image.show()
-
-                       button = gtk.Button()
-                       button.add(image)
-                       button.set_relief(gtk.RELIEF_NONE)
-                       button.set_sensitive(True)
-                       button.connect("clicked", self._show_clicked_cb,
-                                       commit.commit_sha1, parent_id, self.encoding)
-                       hbox.pack_start(button, expand=False, fill=True)
-                       button.show()
-
-               # Populate with child details
-               for widget in self.children_widgets:
-                       self.table.remove(widget)
-
-               self.children_widgets = []
-               try:
-                       child_sha1 = Commit.children_sha1[commit.commit_sha1]
-               except KeyError:
-                       # We don't have child
-                       child_sha1 = [ 0 ]
-
-               if ( len(child_sha1) > len(commit.parent_sha1)):
-                       self.table.resize(4 + len(child_sha1) - 1, 4)
-
-               for idx, child_id in enumerate(child_sha1):
-                       self.table.set_row_spacing(idx + 3, 0)
-
-                       align = gtk.Alignment(0.0, 0.0)
-                       self.children_widgets.append(align)
-                       self.table.attach(align, 3, 4, idx + 3, idx + 4,
-                                       gtk.EXPAND | gtk.FILL, gtk.FILL)
-                       align.show()
-
-                       hbox = gtk.HBox(False, 0)
-                       align.add(hbox)
-                       hbox.show()
-
-                       label = gtk.Label(child_id)
-                       label.set_selectable(True)
-                       hbox.pack_start(label, expand=False, fill=True)
-                       label.show()
-
-                       image = gtk.Image()
-                       image.set_from_stock(gtk.STOCK_JUMP_TO, gtk.ICON_SIZE_MENU)
-                       image.show()
-
-                       button = gtk.Button()
-                       button.add(image)
-                       button.set_relief(gtk.RELIEF_NONE)
-                       button.connect("clicked", self._go_clicked_cb, child_id)
-                       hbox.pack_start(button, expand=False, fill=True)
-                       button.show()
-
-                       image = gtk.Image()
-                       image.set_from_stock(gtk.STOCK_FIND, gtk.ICON_SIZE_MENU)
-                       image.show()
-
-                       button = gtk.Button()
-                       button.add(image)
-                       button.set_relief(gtk.RELIEF_NONE)
-                       button.set_sensitive(True)
-                       button.connect("clicked", self._show_clicked_cb,
-                                       child_id, commit.commit_sha1, self.encoding)
-                       hbox.pack_start(button, expand=False, fill=True)
-                       button.show()
-
-       def _destroy_cb(self, widget):
-               """Callback for when a window we manage is destroyed."""
-               self.quit()
-
-
-       def quit(self):
-               """Stop the GTK+ main loop."""
-               gtk.main_quit()
-
-       def run(self, args):
-               self.set_branch(args)
-               self.window.connect("destroy", self._destroy_cb)
-               self.window.show()
-               gtk.main()
-
-       def set_branch(self, args):
-               """Fill in different windows with info from the reposiroty"""
-               fp = os.popen("git rev-parse --sq --default HEAD " + list_to_string(args, 1))
-               git_rev_list_cmd = fp.read()
-               fp.close()
-               fp = os.popen("git rev-list  --header --topo-order --parents " + git_rev_list_cmd)
-               self.update_window(fp)
-
-       def update_window(self, fp):
-               commit_lines = []
-
-               self.model = gtk.ListStore(gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT,
-                               gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT, str, str, str)
-
-               # used for cursor positioning
-               self.index = {}
-
-               self.colours = {}
-               self.nodepos = {}
-               self.incomplete_line = {}
-               self.commits = []
-
-               index = 0
-               last_colour = 0
-               last_nodepos = -1
-               out_line = []
-               input_line = fp.readline()
-               while (input_line != ""):
-                       # The commit header ends with '\0'
-                       # This NULL is immediately followed by the sha1 of the
-                       # next commit
-                       if (input_line[0] != '\0'):
-                               commit_lines.append(input_line)
-                               input_line = fp.readline()
-                               continue;
-
-                       commit = Commit(commit_lines)
-                       if (commit != None ):
-                               self.commits.append(commit)
-
-                       # Skip the '\0
-                       commit_lines = []
-                       commit_lines.append(input_line[1:])
-                       input_line = fp.readline()
-
-               fp.close()
-
-               for commit in self.commits:
-                       (out_line, last_colour, last_nodepos) = self.draw_graph(commit,
-                                                                               index, out_line,
-                                                                               last_colour,
-                                                                               last_nodepos)
-                       self.index[commit.commit_sha1] = index
-                       index += 1
-
-               self.treeview.set_model(self.model)
-               self.treeview.show()
-
-       def draw_graph(self, commit, index, out_line, last_colour, last_nodepos):
-               in_line=[]
-
-               #   |   -> outline
-               #   X
-               #   |\  <- inline
-
-               # Reset nodepostion
-               if (last_nodepos > 5):
-                       last_nodepos = -1
-
-               # Add the incomplete lines of the last cell in this
-               try:
-                       colour = self.colours[commit.commit_sha1]
-               except KeyError:
-                       self.colours[commit.commit_sha1] = last_colour+1
-                       last_colour = self.colours[commit.commit_sha1]
-                       colour =   self.colours[commit.commit_sha1]
-
-               try:
-                       node_pos = self.nodepos[commit.commit_sha1]
-               except KeyError:
-                       self.nodepos[commit.commit_sha1] = last_nodepos+1
-                       last_nodepos = self.nodepos[commit.commit_sha1]
-                       node_pos =  self.nodepos[commit.commit_sha1]
-
-               #The first parent always continue on the same line
-               try:
-                       # check we already have the value
-                       tmp_node_pos = self.nodepos[commit.parent_sha1[0]]
-               except KeyError:
-                       self.colours[commit.parent_sha1[0]] = colour
-                       self.nodepos[commit.parent_sha1[0]] = node_pos
-
-               for sha1 in self.incomplete_line.keys():
-                       if (sha1 != commit.commit_sha1):
-                               self.draw_incomplete_line(sha1, node_pos,
-                                               out_line, in_line, index)
-                       else:
-                               del self.incomplete_line[sha1]
-
-
-               for parent_id in commit.parent_sha1:
-                       try:
-                               tmp_node_pos = self.nodepos[parent_id]
-                       except KeyError:
-                               self.colours[parent_id] = last_colour+1
-                               last_colour = self.colours[parent_id]
-                               self.nodepos[parent_id] = last_nodepos+1
-                               last_nodepos = self.nodepos[parent_id]
-
-                       in_line.append((node_pos, self.nodepos[parent_id],
-                                               self.colours[parent_id]))
-                       self.add_incomplete_line(parent_id)
-
-               try:
-                       branch_tag = self.bt_sha1[commit.commit_sha1]
-               except KeyError:
-                       branch_tag = [ ]
-
-
-               node = (node_pos, colour, branch_tag)
-
-               self.model.append([commit, node, out_line, in_line,
-                               commit.message, commit.author, commit.date])
-
-               return (in_line, last_colour, last_nodepos)
-
-       def add_incomplete_line(self, sha1):
-               try:
-                       self.incomplete_line[sha1].append(self.nodepos[sha1])
-               except KeyError:
-                       self.incomplete_line[sha1] = [self.nodepos[sha1]]
-
-       def draw_incomplete_line(self, sha1, node_pos, out_line, in_line, index):
-               for idx, pos in enumerate(self.incomplete_line[sha1]):
-                       if(pos == node_pos):
-                               #remove the straight line and add a slash
-                               if ((pos, pos, self.colours[sha1]) in out_line):
-                                       out_line.remove((pos, pos, self.colours[sha1]))
-                               out_line.append((pos, pos+0.5, self.colours[sha1]))
-                               self.incomplete_line[sha1][idx] = pos = pos+0.5
-                       try:
-                               next_commit = self.commits[index+1]
-                               if (next_commit.commit_sha1 == sha1 and pos != int(pos)):
-                               # join the line back to the node point
-                               # This need to be done only if we modified it
-                                       in_line.append((pos, pos-0.5, self.colours[sha1]))
-                                       continue;
-                       except IndexError:
-                               pass
-                       in_line.append((pos, pos, self.colours[sha1]))
-
-
-       def _go_clicked_cb(self, widget, revid):
-               """Callback for when the go button for a parent is clicked."""
-               try:
-                       self.treeview.set_cursor(self.index[revid])
-               except KeyError:
-                       dialog = gtk.MessageDialog(parent=None, flags=0,
-                                       type=gtk.MESSAGE_WARNING, buttons=gtk.BUTTONS_CLOSE,
-                                       message_format=None)
-                       dialog.set_markup("Revision <b>%s</b> not present in the list" % revid)
-                       # revid == 0 is the parent of the first commit
-                       if (revid != 0 ):
-                               dialog.format_secondary_text("Try running gitview without any options")
-                       dialog.run()
-                       dialog.destroy()
-
-               self.treeview.grab_focus()
-
-       def _show_clicked_cb(self, widget,  commit_sha1, parent_sha1, encoding):
-               """Callback for when the show button for a parent is clicked."""
-               window = DiffWindow()
-               window.set_diff(commit_sha1, parent_sha1, encoding)
-               self.treeview.grab_focus()
-
-without_diff = 0
-if __name__ == "__main__":
-
-       if (len(sys.argv) > 1 ):
-               if (sys.argv[1] == "--without-diff"):
-                       without_diff = 1
-
-       view = GitView( without_diff != 1)
-       view.run(sys.argv[without_diff:])
diff --git a/contrib/gitview/gitview.txt b/contrib/gitview/gitview.txt
deleted file mode 100644 (file)
index 7b5f900..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-gitview(1)
-==========
-
-NAME
-----
-gitview - A GTK based repository browser for git
-
-SYNOPSIS
---------
-[verse]
-'gitview' [options] [args]
-
-DESCRIPTION
----------
-
-Dependencies:
-
-* Python 2.4
-* PyGTK 2.8 or later
-* PyCairo 1.0 or later
-
-OPTIONS
--------
---without-diff::
-
-       If the user doesn't want to list the commit diffs in the main window.
-       This may speed up the repository browsing.
-
-<args>::
-
-       All the valid option for linkgit:git-rev-list[1].
-
-Key Bindings
-------------
-F4::
-       To maximize the window
-
-F5::
-       To reread references.
-
-F11::
-       Full screen
-
-F12::
-       Leave full screen
-
-EXAMPLES
---------
-
-gitview v2.6.12.. include/scsi drivers/scsi::
-
-       Show as the changes since version v2.6.12 that changed any file in the
-       include/scsi or drivers/scsi subdirectories
-
-gitview --since=2.weeks.ago::
-
-       Show the changes during the last two weeks
diff --git a/diff.c b/diff.c
index e2eb6d66a9a633e699398146e37dcbcc0b7ec81f..f08cd8e033d104253a4b120f70cb188e39ef9aee 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -32,6 +32,7 @@ static int diff_rename_limit_default = 400;
 static int diff_suppress_blank_empty;
 static int diff_use_color_default = -1;
 static int diff_context_default = 3;
+static int diff_interhunk_context_default;
 static const char *diff_word_regex_cfg;
 static const char *external_diff_cmd_cfg;
 static const char *diff_order_file_cfg;
@@ -239,6 +240,12 @@ int git_diff_ui_config(const char *var, const char *value, void *cb)
                        return -1;
                return 0;
        }
+       if (!strcmp(var, "diff.interhunkcontext")) {
+               diff_interhunk_context_default = git_config_int(var, value);
+               if (diff_interhunk_context_default < 0)
+                       return -1;
+               return 0;
+       }
        if (!strcmp(var, "diff.renames")) {
                diff_detect_rename_default = git_config_rename(var, value);
                return 0;
@@ -3362,6 +3369,7 @@ void diff_setup(struct diff_options *options)
        options->rename_limit = -1;
        options->dirstat_permille = diff_dirstat_permille_default;
        options->context = diff_context_default;
+       options->interhunkcontext = diff_interhunk_context_default;
        options->ws_error_highlight = ws_error_highlight_default;
        DIFF_OPT_SET(options, RENAME_EMPTY);
 
diff --git a/dir.c b/dir.c
index d872cc1570989bf22d8bd5d741d817372e76018f..65c3e681b8e04aa47b6cf41faca0822ead59fb6a 100644 (file)
--- a/dir.c
+++ b/dir.c
 #include "varint.h"
 #include "ewah/ewok.h"
 
-struct path_simplify {
-       int len;
-       const char *path;
-};
-
 /*
  * Tells read_directory_recursive how a file or directory should be treated.
  * Values are ordered by significance, e.g. if a directory contains both
@@ -50,7 +45,7 @@ struct cached_dir {
 
 static enum path_treatment read_directory_recursive(struct dir_struct *dir,
        const char *path, int len, struct untracked_cache_dir *untracked,
-       int check_only, const struct path_simplify *simplify);
+       int check_only, const struct pathspec *pathspec);
 static int get_dtype(struct dirent *de, const char *path, int len);
 
 int fspathcmp(const char *a, const char *b)
@@ -179,17 +174,21 @@ char *common_prefix(const struct pathspec *pathspec)
 
 int fill_directory(struct dir_struct *dir, const struct pathspec *pathspec)
 {
-       size_t len;
+       char *prefix;
+       size_t prefix_len;
 
        /*
         * Calculate common prefix for the pathspec, and
         * use that to optimize the directory walk
         */
-       len = common_prefix_len(pathspec);
+       prefix = common_prefix(pathspec);
+       prefix_len = prefix ? strlen(prefix) : 0;
 
        /* Read the directory and prune it */
-       read_directory(dir, pathspec->nr ? pathspec->_raw[0] : "", len, pathspec);
-       return len;
+       read_directory(dir, prefix, prefix_len, pathspec);
+
+       free(prefix);
+       return prefix_len;
 }
 
 int within_depth(const char *name, int namelen,
@@ -1312,7 +1311,7 @@ static enum exist_status directory_exists_in_index(const char *dirname, int len)
 static enum path_treatment treat_directory(struct dir_struct *dir,
        struct untracked_cache_dir *untracked,
        const char *dirname, int len, int baselen, int exclude,
-       const struct path_simplify *simplify)
+       const struct pathspec *pathspec)
 {
        /* The "len-1" is to strip the final '/' */
        switch (directory_exists_in_index(dirname, len-1)) {
@@ -1341,7 +1340,7 @@ static enum path_treatment treat_directory(struct dir_struct *dir,
        untracked = lookup_untracked(dir->untracked, untracked,
                                     dirname + baselen, len - baselen);
        return read_directory_recursive(dir, dirname, len,
-                                       untracked, 1, simplify);
+                                       untracked, 1, pathspec);
 }
 
 /*
@@ -1349,24 +1348,33 @@ static enum path_treatment treat_directory(struct dir_struct *dir,
  * reading - if the path cannot possibly be in the pathspec,
  * return true, and we'll skip it early.
  */
-static int simplify_away(const char *path, int pathlen, const struct path_simplify *simplify)
+static int simplify_away(const char *path, int pathlen,
+                        const struct pathspec *pathspec)
 {
-       if (simplify) {
-               for (;;) {
-                       const char *match = simplify->path;
-                       int len = simplify->len;
+       int i;
 
-                       if (!match)
-                               break;
-                       if (len > pathlen)
-                               len = pathlen;
-                       if (!memcmp(path, match, len))
-                               return 0;
-                       simplify++;
-               }
-               return 1;
+       if (!pathspec || !pathspec->nr)
+               return 0;
+
+       GUARD_PATHSPEC(pathspec,
+                      PATHSPEC_FROMTOP |
+                      PATHSPEC_MAXDEPTH |
+                      PATHSPEC_LITERAL |
+                      PATHSPEC_GLOB |
+                      PATHSPEC_ICASE |
+                      PATHSPEC_EXCLUDE);
+
+       for (i = 0; i < pathspec->nr; i++) {
+               const struct pathspec_item *item = &pathspec->items[i];
+               int len = item->nowildcard_len;
+
+               if (len > pathlen)
+                       len = pathlen;
+               if (!ps_strncmp(item, item->match, path, len))
+                       return 0;
        }
-       return 0;
+
+       return 1;
 }
 
 /*
@@ -1380,19 +1388,33 @@ static int simplify_away(const char *path, int pathlen, const struct path_simpli
  *   2. the path is a directory prefix of some element in the
  *      pathspec
  */
-static int exclude_matches_pathspec(const char *path, int len,
-               const struct path_simplify *simplify)
-{
-       if (simplify) {
-               for (; simplify->path; simplify++) {
-                       if (len == simplify->len
-                           && !memcmp(path, simplify->path, len))
-                               return 1;
-                       if (len < simplify->len
-                           && simplify->path[len] == '/'
-                           && !memcmp(path, simplify->path, len))
-                               return 1;
-               }
+static int exclude_matches_pathspec(const char *path, int pathlen,
+                                   const struct pathspec *pathspec)
+{
+       int i;
+
+       if (!pathspec || !pathspec->nr)
+               return 0;
+
+       GUARD_PATHSPEC(pathspec,
+                      PATHSPEC_FROMTOP |
+                      PATHSPEC_MAXDEPTH |
+                      PATHSPEC_LITERAL |
+                      PATHSPEC_GLOB |
+                      PATHSPEC_ICASE |
+                      PATHSPEC_EXCLUDE);
+
+       for (i = 0; i < pathspec->nr; i++) {
+               const struct pathspec_item *item = &pathspec->items[i];
+               int len = item->nowildcard_len;
+
+               if (len == pathlen &&
+                   !ps_strncmp(item, item->match, path, pathlen))
+                       return 1;
+               if (len > pathlen &&
+                   item->match[pathlen] == '/' &&
+                   !ps_strncmp(item, item->match, path, pathlen))
+                       return 1;
        }
        return 0;
 }
@@ -1460,7 +1482,7 @@ static enum path_treatment treat_one_path(struct dir_struct *dir,
                                          struct untracked_cache_dir *untracked,
                                          struct strbuf *path,
                                          int baselen,
-                                         const struct path_simplify *simplify,
+                                         const struct pathspec *pathspec,
                                          int dtype, struct dirent *de)
 {
        int exclude;
@@ -1512,7 +1534,7 @@ static enum path_treatment treat_one_path(struct dir_struct *dir,
        case DT_DIR:
                strbuf_addch(path, '/');
                return treat_directory(dir, untracked, path->buf, path->len,
-                                      baselen, exclude, simplify);
+                                      baselen, exclude, pathspec);
        case DT_REG:
        case DT_LNK:
                return exclude ? path_excluded : path_untracked;
@@ -1524,7 +1546,7 @@ static enum path_treatment treat_path_fast(struct dir_struct *dir,
                                           struct cached_dir *cdir,
                                           struct strbuf *path,
                                           int baselen,
-                                          const struct path_simplify *simplify)
+                                          const struct pathspec *pathspec)
 {
        strbuf_setlen(path, baselen);
        if (!cdir->ucd) {
@@ -1541,7 +1563,7 @@ static enum path_treatment treat_path_fast(struct dir_struct *dir,
                 * with check_only set.
                 */
                return read_directory_recursive(dir, path->buf, path->len,
-                                               cdir->ucd, 1, simplify);
+                                               cdir->ucd, 1, pathspec);
        /*
         * We get path_recurse in the first run when
         * directory_exists_in_index() returns index_nonexistent. We
@@ -1556,23 +1578,23 @@ static enum path_treatment treat_path(struct dir_struct *dir,
                                      struct cached_dir *cdir,
                                      struct strbuf *path,
                                      int baselen,
-                                     const struct path_simplify *simplify)
+                                     const struct pathspec *pathspec)
 {
        int dtype;
        struct dirent *de = cdir->de;
 
        if (!de)
                return treat_path_fast(dir, untracked, cdir, path,
-                                      baselen, simplify);
+                                      baselen, pathspec);
        if (is_dot_or_dotdot(de->d_name) || !strcmp(de->d_name, ".git"))
                return path_none;
        strbuf_setlen(path, baselen);
        strbuf_addstr(path, de->d_name);
-       if (simplify_away(path->buf, path->len, simplify))
+       if (simplify_away(path->buf, path->len, pathspec))
                return path_none;
 
        dtype = DTYPE(de);
-       return treat_one_path(dir, untracked, path, baselen, simplify, dtype, de);
+       return treat_one_path(dir, untracked, path, baselen, pathspec, dtype, de);
 }
 
 static void add_untracked(struct untracked_cache_dir *dir, const char *name)
@@ -1703,7 +1725,7 @@ static void close_cached_dir(struct cached_dir *cdir)
 static enum path_treatment read_directory_recursive(struct dir_struct *dir,
                                    const char *base, int baselen,
                                    struct untracked_cache_dir *untracked, int check_only,
-                                   const struct path_simplify *simplify)
+                                   const struct pathspec *pathspec)
 {
        struct cached_dir cdir;
        enum path_treatment state, subdir_state, dir_state = path_none;
@@ -1719,7 +1741,8 @@ static enum path_treatment read_directory_recursive(struct dir_struct *dir,
 
        while (!read_cached_dir(&cdir)) {
                /* check how the file or directory should be treated */
-               state = treat_path(dir, untracked, &cdir, &path, baselen, simplify);
+               state = treat_path(dir, untracked, &cdir, &path,
+                                  baselen, pathspec);
 
                if (state > dir_state)
                        dir_state = state;
@@ -1731,8 +1754,9 @@ static enum path_treatment read_directory_recursive(struct dir_struct *dir,
                                              path.buf + baselen,
                                              path.len - baselen);
                        subdir_state =
-                               read_directory_recursive(dir, path.buf, path.len,
-                                                        ud, check_only, simplify);
+                               read_directory_recursive(dir, path.buf,
+                                                        path.len, ud,
+                                                        check_only, pathspec);
                        if (subdir_state > dir_state)
                                dir_state = subdir_state;
                }
@@ -1756,7 +1780,7 @@ static enum path_treatment read_directory_recursive(struct dir_struct *dir,
                        else if ((dir->flags & DIR_SHOW_IGNORED_TOO) ||
                                ((dir->flags & DIR_COLLECT_IGNORED) &&
                                exclude_matches_pathspec(path.buf, path.len,
-                                       simplify)))
+                                                        pathspec)))
                                dir_add_ignored(dir, path.buf, path.len);
                        break;
 
@@ -1787,36 +1811,9 @@ static int cmp_name(const void *p1, const void *p2)
        return name_compare(e1->name, e1->len, e2->name, e2->len);
 }
 
-static struct path_simplify *create_simplify(const char **pathspec)
-{
-       int nr, alloc = 0;
-       struct path_simplify *simplify = NULL;
-
-       if (!pathspec)
-               return NULL;
-
-       for (nr = 0 ; ; nr++) {
-               const char *match;
-               ALLOC_GROW(simplify, nr + 1, alloc);
-               match = *pathspec++;
-               if (!match)
-                       break;
-               simplify[nr].path = match;
-               simplify[nr].len = simple_length(match);
-       }
-       simplify[nr].path = NULL;
-       simplify[nr].len = 0;
-       return simplify;
-}
-
-static void free_simplify(struct path_simplify *simplify)
-{
-       free(simplify);
-}
-
 static int treat_leading_path(struct dir_struct *dir,
                              const char *path, int len,
-                             const struct path_simplify *simplify)
+                             const struct pathspec *pathspec)
 {
        struct strbuf sb = STRBUF_INIT;
        int baselen, rc = 0;
@@ -1840,9 +1837,9 @@ static int treat_leading_path(struct dir_struct *dir,
                strbuf_add(&sb, path, baselen);
                if (!is_directory(sb.buf))
                        break;
-               if (simplify_away(sb.buf, sb.len, simplify))
+               if (simplify_away(sb.buf, sb.len, pathspec))
                        break;
-               if (treat_one_path(dir, NULL, &sb, baselen, simplify,
+               if (treat_one_path(dir, NULL, &sb, baselen, pathspec,
                                   DT_DIR, NULL) == path_none)
                        break; /* do not recurse into it */
                if (len <= baselen) {
@@ -2010,33 +2007,14 @@ static struct untracked_cache_dir *validate_untracked_cache(struct dir_struct *d
        return root;
 }
 
-int read_directory(struct dir_struct *dir, const char *path, int len, const struct pathspec *pathspec)
+int read_directory(struct dir_struct *dir, const char *path,
+                  int len, const struct pathspec *pathspec)
 {
-       struct path_simplify *simplify;
        struct untracked_cache_dir *untracked;
 
-       /*
-        * Check out create_simplify()
-        */
-       if (pathspec)
-               GUARD_PATHSPEC(pathspec,
-                              PATHSPEC_FROMTOP |
-                              PATHSPEC_MAXDEPTH |
-                              PATHSPEC_LITERAL |
-                              PATHSPEC_GLOB |
-                              PATHSPEC_ICASE |
-                              PATHSPEC_EXCLUDE);
-
        if (has_symlink_leading_path(path, len))
                return dir->nr;
 
-       /*
-        * exclude patterns are treated like positive ones in
-        * create_simplify. Usually exclude patterns should be a
-        * subset of positive ones, which has no impacts on
-        * create_simplify().
-        */
-       simplify = create_simplify(pathspec ? pathspec->_raw : NULL);
        untracked = validate_untracked_cache(dir, len, pathspec);
        if (!untracked)
                /*
@@ -2044,9 +2022,8 @@ int read_directory(struct dir_struct *dir, const char *path, int len, const stru
                 * e.g. prep_exclude()
                 */
                dir->untracked = NULL;
-       if (!len || treat_leading_path(dir, path, len, simplify))
-               read_directory_recursive(dir, path, len, untracked, 0, simplify);
-       free_simplify(simplify);
+       if (!len || treat_leading_path(dir, path, len, pathspec))
+               read_directory_recursive(dir, path, len, untracked, 0, pathspec);
        QSORT(dir->entries, dir->nr, cmp_name);
        QSORT(dir->ignored, dir->ignored_nr, cmp_name);
        if (dir->untracked) {
@@ -2754,8 +2731,8 @@ void connect_work_tree_and_git_dir(const char *work_tree_, const char *git_dir_)
 {
        struct strbuf file_name = STRBUF_INIT;
        struct strbuf rel_path = STRBUF_INIT;
-       char *git_dir = xstrdup(real_path(git_dir_));
-       char *work_tree = xstrdup(real_path(work_tree_));
+       char *git_dir = real_pathdup(git_dir_);
+       char *work_tree = real_pathdup(work_tree_);
 
        /* Update gitfile */
        strbuf_addf(&file_name, "%s/.git", work_tree);
index 4bce3eebfafa45d5781520781693029ffb317985..8a83101d04240b1b5b1471e68c810f822f68269d 100644 (file)
@@ -259,7 +259,7 @@ void set_git_work_tree(const char *new_work_tree)
                return;
        }
        git_work_tree_initialized = 1;
-       work_tree = xstrdup(real_path(new_work_tree));
+       work_tree = real_pathdup(new_work_tree);
 }
 
 const char *get_git_work_tree(void)
index e52b4e4f24088d7552d88aa5a3c9ffc308f6a4cf..c062e3de3a503bd01203f2a6aef63eea63cae5c5 100755 (executable)
@@ -421,7 +421,7 @@ main () {
                        prompt=true
                        ;;
                -O*)
-                       orderfile="$1"
+                       orderfile="${1#-O}"
                        ;;
                --)
                        shift
@@ -454,6 +454,17 @@ main () {
        merge_keep_backup="$(git config --bool mergetool.keepBackup || echo true)"
        merge_keep_temporaries="$(git config --bool mergetool.keepTemporaries || echo false)"
 
+       prefix=$(git rev-parse --show-prefix) || exit 1
+       cd_to_toplevel
+
+       if test -n "$orderfile"
+       then
+               orderfile=$(
+                       git rev-parse --prefix "$prefix" -- "$orderfile" |
+                       sed -e 1d
+               )
+       fi
+
        if test $# -eq 0 && test -e "$GIT_DIR/MERGE_RR"
        then
                set -- $(git rerere remaining)
@@ -461,13 +472,15 @@ main () {
                then
                        print_noop_and_exit
                fi
+       elif test $# -ge 0
+       then
+               # rev-parse provides the -- needed for 'set'
+               eval "set $(git rev-parse --sq --prefix "$prefix" -- "$@")"
        fi
 
        files=$(git -c core.quotePath=false \
                diff --name-only --diff-filter=U \
-               ${orderfile:+"$orderfile"} -- "$@")
-
-       cd_to_toplevel
+               ${orderfile:+"-O$orderfile"} -- "$@")
 
        if test -z "$files"
        then
index 22e3f57e7d93783ef6bb3f0bfd4ef2f6bbd0f93f..7bda915bd0c9af438d7303abaabf53fc15f9a5dd 100755 (executable)
--- a/git-p4.py
+++ b/git-p4.py
@@ -83,7 +83,9 @@ def p4_build_cmd(cmd):
     if retries is None:
         # Perform 3 retries by default
         retries = 3
-    real_cmd += ["-r", str(retries)]
+    if retries > 0:
+        # Provide a way to not pass this option by setting git-p4.retries to 0
+        real_cmd += ["-r", str(retries)]
 
     if isinstance(cmd,basestring):
         real_cmd = ' '.join(real_cmd) + ' ' + cmd
index b0a6f2b7bae0d4ccd5fcf00ae4dac6c29e50bd71..4734094a3f1aaa7d7c767a0740b46dd6abdd6f7a 100644 (file)
@@ -425,7 +425,7 @@ update_squash_messages () {
        if test -f "$squash_msg"; then
                mv "$squash_msg" "$squash_msg".bak || exit
                count=$(($(sed -n \
-                       -e "1s/^$comment_char.*\([0-9][0-9]*\).*/\1/p" \
+                       -e "1s/^$comment_char[^0-9]*\([0-9][0-9]*\).*/\1/p" \
                        -e "q" < "$squash_msg".bak)+1))
                {
                        printf '%s\n' "$comment_char $(eval_ngettext \
index 554bd1c4943a5f2dd9aa9615dadc962bf6405d7a..9788175979273f8748635e2f5078f3ca8d4b7500 100755 (executable)
@@ -12,7 +12,8 @@ USAGE="[--quiet] add [-b <branch>] [-f|--force] [--name <name>] [--reference <re
    or: $dashless [--quiet] update [--init] [--remote] [-N|--no-fetch] [-f|--force] [--checkout|--merge|--rebase] [--[no-]recommend-shallow] [--reference <repository>] [--recursive] [--] [<path>...]
    or: $dashless [--quiet] summary [--cached|--files] [--summary-limit <n>] [commit] [--] [<path>...]
    or: $dashless [--quiet] foreach [--recursive] <command>
-   or: $dashless [--quiet] sync [--recursive] [--] [<path>...]"
+   or: $dashless [--quiet] sync [--recursive] [--] [<path>...]
+   or: $dashless [--quiet] absorbgitdirs [--] [<path>...]"
 OPTIONS_SPEC=
 SUBDIRECTORY_OK=Yes
 . git-sh-setup
@@ -370,7 +371,7 @@ cmd_init()
                shift
        done
 
-       git ${wt_prefix:+-C "$wt_prefix"} submodule--helper init ${GIT_QUIET:+--quiet} ${prefix:+--prefix "$prefix"} "$@"
+       git ${wt_prefix:+-C "$wt_prefix"} ${prefix:+--super-prefix "$prefix"} submodule--helper init ${GIT_QUIET:+--quiet}  "$@"
 }
 
 #
diff --git a/git.c b/git.c
index bbaa949e9cea975b0b5a8ef1799249bf0445dcaa..b367cf6686029a7b54ff4b3ca3d63be2409904b3 100644 (file)
--- a/git.c
+++ b/git.c
@@ -434,7 +434,7 @@ static struct cmd_struct commands[] = {
        { "fsck-objects", cmd_fsck, RUN_SETUP },
        { "gc", cmd_gc, RUN_SETUP },
        { "get-tar-commit-id", cmd_get_tar_commit_id },
-       { "grep", cmd_grep, RUN_SETUP_GENTLY },
+       { "grep", cmd_grep, RUN_SETUP_GENTLY | SUPPORT_SUPER_PREFIX },
        { "hash-object", cmd_hash_object },
        { "help", cmd_help },
        { "index-pack", cmd_index_pack, RUN_SETUP_GENTLY },
@@ -575,8 +575,7 @@ static void handle_builtin(int argc, const char **argv)
 
 static void execv_dashed_external(const char **argv)
 {
-       struct strbuf cmd = STRBUF_INIT;
-       const char *tmp;
+       struct child_process cmd = CHILD_PROCESS_INIT;
        int status;
 
        if (get_super_prefix())
@@ -586,30 +585,25 @@ static void execv_dashed_external(const char **argv)
                use_pager = check_pager_config(argv[0]);
        commit_pager_choice();
 
-       strbuf_addf(&cmd, "git-%s", argv[0]);
+       argv_array_pushf(&cmd.args, "git-%s", argv[0]);
+       argv_array_pushv(&cmd.args, argv + 1);
+       cmd.clean_on_exit = 1;
+       cmd.wait_after_clean = 1;
+       cmd.silent_exec_failure = 1;
 
-       /*
-        * argv[0] must be the git command, but the argv array
-        * belongs to the caller, and may be reused in
-        * subsequent loop iterations. Save argv[0] and
-        * restore it on error.
-        */
-       tmp = argv[0];
-       argv[0] = cmd.buf;
-
-       trace_argv_printf(argv, "trace: exec:");
+       trace_argv_printf(cmd.args.argv, "trace: exec:");
 
        /*
-        * if we fail because the command is not found, it is
-        * OK to return. Otherwise, we just pass along the status code.
+        * If we fail because the command is not found, it is
+        * OK to return. Otherwise, we just pass along the status code,
+        * or our usual generic code if we were not even able to exec
+        * the program.
         */
-       status = run_command_v_opt(argv, RUN_SILENT_EXEC_FAILURE | RUN_CLEAN_ON_EXIT);
-       if (status >= 0 || errno != ENOENT)
+       status = run_command(&cmd);
+       if (status >= 0)
                exit(status);
-
-       argv[0] = tmp;
-
-       strbuf_release(&cmd);
+       else if (errno != ENOENT)
+               exit(128);
 }
 
 static int run_argv(int *argcp, const char ***argv)
index 5acdc900abdfb3ccc1ad7616fecb098ce6f060e2..5bdd52a6ebfa722fe077ec6a3160f9bbd8ff6a2b 100644 (file)
@@ -50,6 +50,7 @@ endif
 all:: gitk-wish $(ALL_MSGFILES)
 
 install:: all
+       $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)'
        $(INSTALL) -m 755 gitk-wish '$(DESTDIR_SQ)$(bindir_SQ)'/gitk
        $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(msgsdir_SQ)'
        $(foreach p,$(ALL_MSGFILES), $(INSTALL) -m 644 $p '$(DESTDIR_SQ)$(msgsdir_SQ)' &&) true
index 805a1c703040d4b08fc0d6992c92e2a044c5ddc5..a14d7a16b2dd1162fa8572d776f4adee8a0ce91f 100755 (executable)
@@ -2,7 +2,7 @@
 # Tcl ignores the next line -*- tcl -*- \
 exec wish "$0" -- "$@"
 
-# Copyright © 2005-2014 Paul Mackerras.  All rights reserved.
+# Copyright © 2005-2016 Paul Mackerras.  All rights reserved.
 # This program is free software; it may be used, copied, modified
 # and distributed under the terms of the GNU General Public Licence,
 # either version 2, or (at your option) any later version.
@@ -588,7 +588,7 @@ proc updatecommits {} {
 proc reloadcommits {} {
     global curview viewcomplete selectedline currentid thickerline
     global showneartags treediffs commitinterest cached_commitrow
-    global targetid
+    global targetid commitinfo
 
     set selid {}
     if {$selectedline ne {}} {
@@ -609,6 +609,7 @@ proc reloadcommits {} {
        getallcommits
     }
     clear_display
+    unset -nocomplain commitinfo
     unset -nocomplain commitinterest
     unset -nocomplain cached_commitrow
     unset -nocomplain targetid
@@ -1315,7 +1316,7 @@ proc commitonrow {row} {
 
 proc closevarcs {v} {
     global varctok varccommits varcid parents children
-    global cmitlisted commitidx vtokmod
+    global cmitlisted commitidx vtokmod curview numcommits
 
     set missing_parents 0
     set scripts {}
@@ -1340,6 +1341,9 @@ proc closevarcs {v} {
            }
            lappend varccommits($v,$b) $p
            incr commitidx($v)
+           if {$v == $curview} {
+               set numcommits $commitidx($v)
+           }
            set scripts [check_interest $p $scripts]
        }
     }
@@ -2265,7 +2269,7 @@ proc makewindow {} {
        set h [expr {[font metrics uifont -linespace] + 2}]
        set progresscanv .tf.bar.progress
        canvas $progresscanv -relief sunken -height $h -borderwidth 2
-       set progressitem [$progresscanv create rect -1 0 0 $h -fill lime]
+       set progressitem [$progresscanv create rect -1 0 0 $h -fill "#00ff00"]
        set fprogitem [$progresscanv create rect -1 0 0 $h -fill yellow]
        set rprogitem [$progresscanv create rect -1 0 0 $h -fill red]
     }
@@ -2403,7 +2407,7 @@ proc makewindow {} {
 
     set ctext .bleft.bottom.ctext
     text $ctext -background $bgcolor -foreground $fgcolor \
-       -state disabled -font textfont \
+       -state disabled -undo 0 -font textfont \
        -yscrollcommand scrolltext -wrap none \
        -xscrollcommand ".bleft.bottom.sbhorizontal set"
     if {$have_tk85} {
@@ -2664,6 +2668,7 @@ proc makewindow {} {
     set headctxmenu .headctxmenu
     makemenu $headctxmenu {
        {mc "Check out this branch" command cobranch}
+       {mc "Rename this branch" command mvbranch}
        {mc "Remove this branch" command rmbranch}
        {mc "Copy branch name" command {clipboard clear; clipboard append $headmenuhead}}
     }
@@ -3033,7 +3038,7 @@ proc about {} {
     message $w.m -text [mc "
 Gitk - a commit viewer for git
 
-Copyright \u00a9 2005-2014 Paul Mackerras
+Copyright \u00a9 2005-2016 Paul Mackerras
 
 Use and redistribute under the terms of the GNU General Public License"] \
            -justify center -aspect 400 -border 2 -bg $bgcolor -relief groove
@@ -3397,7 +3402,7 @@ set rectmask {
        0x00, 0x00, 0xfc, 0x0f, 0xfc, 0x0f, 0xfc, 0x0f,
        0xfc, 0x0f, 0xfc, 0x0f, 0xfc, 0x0f, 0xfc, 0x0f, 0x00, 0x00};
 }
-image create bitmap reficon-H -background black -foreground lime \
+image create bitmap reficon-H -background black -foreground "#00ff00" \
     -data $rectdata -maskdata $rectmask
 image create bitmap reficon-o -background black -foreground "#ddddff" \
     -data $rectdata -maskdata $rectmask
@@ -8069,7 +8074,11 @@ proc getblobdiffline {bdf ids} {
     $ctext conf -state normal
     while {[incr nr] <= 1000 && [gets $bdf line] >= 0} {
        if {$ids != $diffids || $bdf != $blobdifffd($ids)} {
+           # Older diff read. Abort it.
            catch {close $bdf}
+           if {$ids != $diffids} {
+               array unset blobdifffd $ids
+           }
            return 0
        }
        parseblobdiffline $ids $line
@@ -8078,6 +8087,7 @@ proc getblobdiffline {bdf ids} {
     blobdiffmaybeseehere [eof $bdf]
     if {[eof $bdf]} {
        catch {close $bdf}
+       array unset blobdifffd $ids
        return 0
     }
     return [expr {$nr >= 1000? 2: 1}]
@@ -9452,26 +9462,63 @@ proc wrcomcan {} {
 }
 
 proc mkbranch {} {
-    global rowmenuid mkbrtop NS
+    global NS rowmenuid
+
+    set top .branchdialog
+
+    set val(name) ""
+    set val(id) $rowmenuid
+    set val(command) [list mkbrgo $top]
+
+    set ui(title) [mc "Create branch"]
+    set ui(accept) [mc "Create"]
+
+    branchdia $top val ui
+}
+
+proc mvbranch {} {
+    global NS
+    global headmenuid headmenuhead
+
+    set top .branchdialog
+
+    set val(name) $headmenuhead
+    set val(id) $headmenuid
+    set val(command) [list mvbrgo $top $headmenuhead]
+
+    set ui(title) [mc "Rename branch %s" $headmenuhead]
+    set ui(accept) [mc "Rename"]
+
+    branchdia $top val ui
+}
+
+proc branchdia {top valvar uivar} {
+    global NS commitinfo
+    upvar $valvar val $uivar ui
 
-    set top .makebranch
     catch {destroy $top}
     ttk_toplevel $top
     make_transient $top .
-    ${NS}::label $top.title -text [mc "Create new branch"]
+    ${NS}::label $top.title -text $ui(title)
     grid $top.title - -pady 10
     ${NS}::label $top.id -text [mc "ID:"]
     ${NS}::entry $top.sha1 -width 40
-    $top.sha1 insert 0 $rowmenuid
+    $top.sha1 insert 0 $val(id)
     $top.sha1 conf -state readonly
     grid $top.id $top.sha1 -sticky w
+    ${NS}::entry $top.head -width 60
+    $top.head insert 0 [lindex $commitinfo($val(id)) 0]
+    $top.head conf -state readonly
+    grid x $top.head -sticky ew
+    grid columnconfigure $top 1 -weight 1
     ${NS}::label $top.nlab -text [mc "Name:"]
     ${NS}::entry $top.name -width 40
+    $top.name insert 0 $val(name)
     grid $top.nlab $top.name -sticky w
     ${NS}::frame $top.buts
-    ${NS}::button $top.buts.go -text [mc "Create"] -command [list mkbrgo $top]
+    ${NS}::button $top.buts.go -text $ui(accept) -command $val(command)
     ${NS}::button $top.buts.can -text [mc "Cancel"] -command "catch {destroy $top}"
-    bind $top <Key-Return> [list mkbrgo $top]
+    bind $top <Key-Return> $val(command)
     bind $top <Key-Escape> "catch {destroy $top}"
     grid $top.buts.go $top.buts.can
     grid columnconfigure $top.buts 0 -weight 1 -uniform a
@@ -9526,6 +9573,46 @@ proc mkbrgo {top} {
     }
 }
 
+proc mvbrgo {top prevname} {
+    global headids idheads mainhead mainheadid
+
+    set name [$top.name get]
+    set id [$top.sha1 get]
+    set cmdargs {}
+    if {$name eq $prevname} {
+       catch {destroy $top}
+       return
+    }
+    if {$name eq {}} {
+       error_popup [mc "Please specify a new name for the branch"] $top
+       return
+    }
+    catch {destroy $top}
+    lappend cmdargs -m $prevname $name
+    nowbusy renamebranch
+    update
+    if {[catch {
+       eval exec git branch $cmdargs
+    } err]} {
+       notbusy renamebranch
+       error_popup $err
+    } else {
+       notbusy renamebranch
+       removehead $id $prevname
+       removedhead $id $prevname
+       set headids($name) $id
+       lappend idheads($id) $name
+       addedhead $id $name
+       if {$prevname eq $mainhead} {
+           set mainhead $name
+           set mainheadid $id
+       }
+       redrawtags $id
+       dispneartags 0
+       run refill_reflist
+    }
+}
+
 proc exec_citool {tool_args {baseid {}}} {
     global commitinfo env
 
@@ -9751,20 +9838,25 @@ proc readresetstat {fd} {
 
 # context menu for a head
 proc headmenu {x y id head} {
-    global headmenuid headmenuhead headctxmenu mainhead
+    global headmenuid headmenuhead headctxmenu mainhead headids
 
     stopfinding
     set headmenuid $id
     set headmenuhead $head
-    set state normal
+    array set state {0 normal 1 normal 2 normal}
     if {[string match "remotes/*" $head]} {
-       set state disabled
+       set localhead [string range $head [expr [string last / $head] + 1] end]
+       if {[info exists headids($localhead)]} {
+           set state(0) disabled
+       }
+       array set state {1 disabled 2 disabled}
     }
     if {$head eq $mainhead} {
-       set state disabled
+       array set state {0 disabled 2 disabled}
+    }
+    foreach i {0 1 2} {
+       $headctxmenu entryconfigure $i -state $state($i)
     }
-    $headctxmenu entryconfigure 0 -state $state
-    $headctxmenu entryconfigure 1 -state $state
     tk_popup $headctxmenu $x $y
 }
 
@@ -9773,11 +9865,27 @@ proc cobranch {} {
     global showlocalchanges
 
     # check the tree is clean first??
+    set newhead $headmenuhead
+    set command [list | git checkout]
+    if {[string match "remotes/*" $newhead]} {
+       set remote $newhead
+       set newhead [string range $newhead [expr [string last / $newhead] + 1] end]
+       # The following check is redundant - the menu option should
+       # be disabled to begin with...
+       if {[info exists headids($newhead)]} {
+           error_popup [mc "A local branch named %s exists already" $newhead]
+           return
+       }
+       lappend command -b $newhead --track $remote
+    } else {
+       lappend command $newhead
+    }
+    lappend command 2>@1
     nowbusy checkout [mc "Checking out"]
     update
     dohidelocalchanges
     if {[catch {
-       set fd [open [list | git checkout $headmenuhead 2>@1] r]
+       set fd [open $command r]
     } err]} {
        notbusy checkout
        error_popup $err
@@ -9785,12 +9893,12 @@ proc cobranch {} {
            dodiffindex
        }
     } else {
-       filerun $fd [list readcheckoutstat $fd $headmenuhead $headmenuid]
+       filerun $fd [list readcheckoutstat $fd $newhead $headmenuid]
     }
 }
 
 proc readcheckoutstat {fd newhead newheadid} {
-    global mainhead mainheadid headids showlocalchanges progresscoords
+    global mainhead mainheadid headids idheads showlocalchanges progresscoords
     global viewmainheadid curview
 
     if {[gets $fd line] >= 0} {
@@ -9805,8 +9913,14 @@ proc readcheckoutstat {fd newhead newheadid} {
     notbusy checkout
     if {[catch {close $fd} err]} {
        error_popup $err
+       return
     }
     set oldmainid $mainheadid
+    if {! [info exists headids($newhead)]} {
+       set headids($newhead) $newheadid
+       lappend idheads($newheadid) $newhead
+       addedhead $newheadid $newhead
+    }
     set mainhead $newhead
     set mainheadid $newheadid
     set viewmainheadid($curview) $newheadid
@@ -12188,7 +12302,7 @@ if {[tk windowingsystem] eq "aqua"} {
     set extdifftool "meld"
 }
 
-set colors {lime red blue magenta darkgrey brown orange}
+set colors {"#00ff00" red blue magenta darkgrey brown orange}
 if {[tk windowingsystem] eq "win32"} {
     set uicolor SystemButtonFace
     set uifgcolor SystemButtonText
@@ -12206,12 +12320,12 @@ if {[tk windowingsystem] eq "win32"} {
 }
 set diffcolors {red "#00a000" blue}
 set diffcontext 3
-set mergecolors {red blue lime purple brown "#009090" magenta "#808000" "#009000" "#ff0080" cyan "#b07070" "#70b0f0" "#70f0b0" "#f0b070" "#ff70b0"}
+set mergecolors {red blue "#00ff00" purple brown "#009090" magenta "#808000" "#009000" "#ff0080" cyan "#b07070" "#70b0f0" "#70f0b0" "#f0b070" "#ff70b0"}
 set ignorespace 0
 set worddiff ""
 set markbgcolor "#e0e0ff"
 
-set headbgcolor lime
+set headbgcolor "#00ff00"
 set headfgcolor black
 set headoutlinecolor black
 set remotebgcolor #ffddaa
@@ -12226,7 +12340,7 @@ set linehoverfgcolor black
 set linehoveroutlinecolor black
 set mainheadcirclecolor yellow
 set workingfilescirclecolor red
-set indexcirclecolor lime
+set indexcirclecolor "#00ff00"
 set circlecolors {white blue gray blue blue}
 set linkfgcolor blue
 set circleoutlinecolor $fgcolor
index 99aa77aa63bd24af12a2ccee120b2a1db7307781..407d5550b1aba947549ad509a641134c34f28d46 100644 (file)
@@ -371,14 +371,14 @@ msgid ""
 "\n"
 "Gitk - a commit viewer for git\n"
 "\n"
-"Copyright © 2005-2014 Paul Mackerras\n"
+"Copyright © 2005-2016 Paul Mackerras\n"
 "\n"
 "Use and redistribute under the terms of the GNU General Public License"
 msgstr ""
 "\n"
 "Gitk — визуализация на подаванията в Git\n"
 "\n"
-"Авторски права: © 2005-2014 Paul Mackerras\n"
+"Авторски права: © 2005-2016 Paul Mackerras\n"
 "\n"
 "Използвайте и разпространявайте при условията на ОПЛ на ГНУ"
 
index 5ad066f7ce84c4be7801a467498ee3de3abccbf9..87dfc18b4406b202221b7e09894f0372988e728d 100644 (file)
@@ -1,5 +1,5 @@
 # Translation of gitk
-# Copyright (C) 2005-2014 Paul Mackerras
+# Copyright (C) 2005-2016 Paul Mackerras
 # This file is distributed under the same license as the gitk package.
 # Alex Henrie <alexhenrie24@gmail.com>, 2015.
 #
@@ -365,14 +365,14 @@ msgid ""
 "\n"
 "Gitk - a commit viewer for git\n"
 "\n"
-"Copyright © 2005-2014 Paul Mackerras\n"
+"Copyright © 2005-2016 Paul Mackerras\n"
 "\n"
 "Use and redistribute under the terms of the GNU General Public License"
 msgstr ""
 "\n"
 "Gitk - visualitzador de comissions per al git\n"
 "\n"
-"Copyright © 2005-2014 Paul Mackerras\n"
+"Copyright © 2005-2016 Paul Mackerras\n"
 "\n"
 "Useu-lo i redistribuïu-lo sota els termes de la Llicència Pública General GNU"
 
index bde749ed8ac7391746e06a00742c44905f7e686e..5db38248289baa42f1732a6f72566652d5819289 100644 (file)
@@ -363,14 +363,14 @@ msgid ""
 "\n"
 "Gitk - a commit viewer for git\n"
 "\n"
-"Copyright © 2005-2014 Paul Mackerras\n"
+"Copyright © 2005-2016 Paul Mackerras\n"
 "\n"
 "Use and redistribute under the terms of the GNU General Public License"
 msgstr ""
 "\n"
 "Gitk - eine Visualisierung der Git-Historie\n"
 "\n"
-"Copyright \\u00a9 2005-2014 Paul Mackerras\n"
+"Copyright \\u00a9 2005-2016 Paul Mackerras\n"
 "\n"
 "Benutzung und Weiterverbreitung gemäß den Bedingungen der GNU General Public "
 "License"
index ddcb0a5f68dc27127d72f2a42f6d77d2bbf7c23f..fef3bbafeead3e6f25300efa219ad1304596cf78 100644 (file)
@@ -370,14 +370,14 @@ msgid ""
 "\n"
 "Gitk - a commit viewer for git\n"
 "\n"
-"Copyright © 2005-2014 Paul Mackerras\n"
+"Copyright © 2005-2016 Paul Mackerras\n"
 "\n"
 "Use and redistribute under the terms of the GNU General Public License"
 msgstr ""
 "\n"
 "Gitk - un visualizador de revisiones para git\n"
 "\n"
-"Copyright \\u00a9 2005-2010 Paul Mackerras\n"
+"Copyright \\u00a9 2005-2016 Paul Mackerras\n"
 "\n"
 "Uso y redistribución permitidos según los términos de la Licencia Pública "
 "General de GNU (GNU GPL)"
index c44f994fa5807036043e2ebe197e832c9d2e3e1f..e4fac932e5b0f6267076943c8244196e31bbd678 100644 (file)
@@ -372,14 +372,14 @@ msgid ""
 "\n"
 "Gitk - a commit viewer for git\n"
 "\n"
-"Copyright © 2005-2014 Paul Mackerras\n"
+"Copyright © 2005-2016 Paul Mackerras\n"
 "\n"
 "Use and redistribute under the terms of the GNU General Public License"
 msgstr ""
 "\n"
 "Gitk - visualisateur de commit pour git\n"
 "\n"
-"Copyright \\u00a9 2005-2014 Paul Mackerras\n"
+"Copyright \\u00a9 2005-2016 Paul Mackerras\n"
 "\n"
 "Utilisation et redistribution soumises aux termes de la GNU General Public License"
 
index 66fd75ba5b1634f13f067e4aab3cc1d921ca804b..79ec5a565674b0bc3fcec50588bb845afbde07a3 100644 (file)
@@ -366,14 +366,14 @@ msgid ""
 "\n"
 "Gitk - a commit viewer for git\n"
 "\n"
-"Copyright © 2005-2014 Paul Mackerras\n"
+"Copyright © 2005-2016 Paul Mackerras\n"
 "\n"
 "Use and redistribute under the terms of the GNU General Public License"
 msgstr ""
 "\n"
 "Gitk - commit nézegető a githez\n"
 "\n"
-"Szerzői jog \\u00a9 2005-2010 Paul Mackerras\n"
+"Szerzői jog \\u00a9 2005-2016 Paul Mackerras\n"
 "\n"
 "Használd és terjeszd a GNU General Public License feltételei mellett"
 
index b5f002db7d52d3106d3a52dbb03047ad67233b62..b58d23eb2b9253808282e31d44125e98098584e0 100644 (file)
@@ -367,14 +367,14 @@ msgid ""
 "\n"
 "Gitk - a commit viewer for git\n"
 "\n"
-"Copyright © 2005-2014 Paul Mackerras\n"
+"Copyright © 2005-2016 Paul Mackerras\n"
 "\n"
 "Use and redistribute under the terms of the GNU General Public License"
 msgstr ""
 "\n"
 "Gitk - un visualizzatore di revisioni per git\n"
 "\n"
-"Copyright \\u00a9 2005-2010 Paul Mackerras\n"
+"Copyright \\u00a9 2005-2016 Paul Mackerras\n"
 "\n"
 "Utilizzo e redistribuzione permessi sotto i termini della GNU General Public "
 "License"
index f143753db0b27cf0dfd1ef28ba89372c921feb7b..ca3c29b457bd375ff5f42bdc321d809f4cd301dc 100644 (file)
@@ -2,16 +2,17 @@
 # Copyright (C) 2005-2015 Paul Mackerras
 # This file is distributed under the same license as the gitk package.
 #
-# YOKOTA Hiroshi <yokota@netlab.cs.tsukuba.ac.jp>, 2015.
 # Mizar <mizar.jp@gmail.com>, 2009.
 # Junio C Hamano <gitster@pobox.com>, 2009.
+# YOKOTA Hiroshi <yokota@netlab.cs.tsukuba.ac.jp>, 2015.
+# Satoshi Yasushima <s.yasushima@gmail.com>, 2016.
 msgid ""
 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-11-12 13:00+0900\n"
-"Last-Translator: YOKOTA Hiroshi <yokota@netlab.cs.tsukuba.ac.jp>\n"
+"Last-Translator: Satoshi Yasushima <s.yasushima@gmail.com>\n"
 "Language-Team: Japanese\n"
 "Language: ja\n"
 "MIME-Version: 1.0\n"
@@ -314,11 +315,11 @@ msgstr "マークを付けたコミットと比較する"
 
 #: gitk:2630 gitk:2641
 msgid "Diff this -> marked commit"
-msgstr "これと選択したコミットのdiffを見る"
+msgstr "これとマークを付けたコミットのdiffを見る"
 
 #: gitk:2631 gitk:2642
 msgid "Diff marked commit -> this"
-msgstr "選択したコミットとこれのdiffを見る"
+msgstr "マークを付けたコミットとこれのdiffを見る"
 
 #: gitk:2632
 msgid "Revert this commit"
@@ -373,14 +374,14 @@ msgid ""
 "\n"
 "Gitk - a commit viewer for git\n"
 "\n"
-"Copyright © 2005-2014 Paul Mackerras\n"
+"Copyright © 2005-2016 Paul Mackerras\n"
 "\n"
 "Use and redistribute under the terms of the GNU General Public License"
 msgstr ""
 "\n"
 "Gitk - gitコミットビューア\n"
 "\n"
-"Copyright © 2005-2014 Paul Mackerras\n"
+"Copyright © 2005-2016 Paul Mackerras\n"
 "\n"
 "使用および再配布は GNU General Public License に従ってください"
 
index 3f78f1b7482614098e295f3f1ffb6c0925ae6383..1feb34854b32ccd03c0eaf15adbd488d3573aef4 100644 (file)
@@ -368,14 +368,14 @@ msgid ""
 "\n"
 "Gitk - a commit viewer for git\n"
 "\n"
-"Copyright © 2005-2014 Paul Mackerras\n"
+"Copyright © 2005-2016 Paul Mackerras\n"
 "\n"
 "Use and redistribute under the terms of the GNU General Public License"
 msgstr ""
 "\n"
 "Gitk - um visualizador de revisões para o git \n"
 "\n"
-"Copyright ©9 2005-2010 Paul Mackerras\n"
+"Copyright ©9 2005-2016 Paul Mackerras\n"
 "\n"
 "Uso e distribuição segundo os termos da Licença Pública Geral GNU"
 
diff --git a/gitk-git/po/pt_pt.po b/gitk-git/po/pt_pt.po
new file mode 100644 (file)
index 0000000..f680ea8
--- /dev/null
@@ -0,0 +1,1376 @@
+# Portuguese translations for gitk package.
+# Copyright (C) 2016 Paul Mackerras
+# This file is distributed under the same license as the gitk package.
+# Vasco Almeida <vascomalmeida@sapo.pt>, 2016.
+msgid ""
+msgstr ""
+"Project-Id-Version: gitk\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2016-04-15 16:52+0000\n"
+"PO-Revision-Date: 2016-05-06 15:35+0000\n"
+"Last-Translator: Vasco Almeida <vascomalmeida@sapo.pt>\n"
+"Language-Team: Portuguese\n"
+"Language: pt\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Virtaal 0.7.1\n"
+
+#: gitk:140
+msgid "Couldn't get list of unmerged files:"
+msgstr "Não foi possível obter lista de ficheiros não integrados:"
+
+#: gitk:212 gitk:2399
+msgid "Color words"
+msgstr "Colorir palavras"
+
+#: gitk:217 gitk:2399 gitk:8239 gitk:8272
+msgid "Markup words"
+msgstr "Marcar palavras"
+
+#: gitk:324
+msgid "Error parsing revisions:"
+msgstr "Erro ao analisar revisões:"
+
+#: gitk:380
+msgid "Error executing --argscmd command:"
+msgstr "Erro ao executar o comando de --argscmd:"
+
+#: gitk:393
+msgid "No files selected: --merge specified but no files are unmerged."
+msgstr ""
+"Nenhum ficheiro selecionado: --merge especificado mas não há ficheiros por "
+"integrar."
+
+#: gitk:396
+msgid ""
+"No files selected: --merge specified but no unmerged files are within file "
+"limit."
+msgstr ""
+"Nenhum ficheiro selecionado: --merge especificado mas não há ficheiros por "
+"integrar ao nível de ficheiro."
+
+#: gitk:418 gitk:566
+msgid "Error executing git log:"
+msgstr "Erro ao executar git log:"
+
+#: gitk:436 gitk:582
+msgid "Reading"
+msgstr "A ler"
+
+#: gitk:496 gitk:4544
+msgid "Reading commits..."
+msgstr "A ler commits..."
+
+#: gitk:499 gitk:1637 gitk:4547
+msgid "No commits selected"
+msgstr "Nenhum commit selecionado"
+
+#: gitk:1445 gitk:4064 gitk:12469
+msgid "Command line"
+msgstr "Linha de comandos"
+
+#: gitk:1511
+msgid "Can't parse git log output:"
+msgstr "Não é possível analisar a saída de git log:"
+
+#: gitk:1740
+msgid "No commit information available"
+msgstr "Não há informação disponível sobre o commit"
+
+#: gitk:1903 gitk:1932 gitk:4334 gitk:9702 gitk:11274 gitk:11554
+msgid "OK"
+msgstr "OK"
+
+#: gitk:1934 gitk:4336 gitk:9215 gitk:9294 gitk:9424 gitk:9473 gitk:9704
+#: gitk:11275 gitk:11555
+msgid "Cancel"
+msgstr "Cancelar"
+
+#: gitk:2083
+msgid "&Update"
+msgstr "At&ualizar"
+
+#: gitk:2084
+msgid "&Reload"
+msgstr "&Recarregar"
+
+#: gitk:2085
+msgid "Reread re&ferences"
+msgstr "Reler re&ferências"
+
+#: gitk:2086
+msgid "&List references"
+msgstr "&Listar referências"
+
+#: gitk:2088
+msgid "Start git &gui"
+msgstr "Iniciar git &gui"
+
+#: gitk:2090
+msgid "&Quit"
+msgstr "&Sair"
+
+#: gitk:2082
+msgid "&File"
+msgstr "&Ficheiro"
+
+#: gitk:2094
+msgid "&Preferences"
+msgstr "&Preferências"
+
+#: gitk:2093
+msgid "&Edit"
+msgstr "&Editar"
+
+#: gitk:2098
+msgid "&New view..."
+msgstr "&Nova vista..."
+
+#: gitk:2099
+msgid "&Edit view..."
+msgstr "&Editar vista..."
+
+#: gitk:2100
+msgid "&Delete view"
+msgstr "Elimina&r vista"
+
+#: gitk:2102
+msgid "&All files"
+msgstr "&Todos os ficheiros"
+
+#: gitk:2097
+msgid "&View"
+msgstr "&Ver"
+
+#: gitk:2107 gitk:2117
+msgid "&About gitk"
+msgstr "&Sobre gitk"
+
+#: gitk:2108 gitk:2122
+msgid "&Key bindings"
+msgstr "&Atalhos"
+
+#: gitk:2106 gitk:2121
+msgid "&Help"
+msgstr "&Ajuda"
+
+#: gitk:2199 gitk:8671
+msgid "SHA1 ID:"
+msgstr "ID SHA1:"
+
+#: gitk:2243
+msgid "Row"
+msgstr "Linha"
+
+#: gitk:2281
+msgid "Find"
+msgstr "Procurar"
+
+#: gitk:2309
+msgid "commit"
+msgstr "commit"
+
+#: gitk:2313 gitk:2315 gitk:4706 gitk:4729 gitk:4753 gitk:6774 gitk:6846
+#: gitk:6931
+msgid "containing:"
+msgstr "contendo:"
+
+#: gitk:2316 gitk:3545 gitk:3550 gitk:4782
+msgid "touching paths:"
+msgstr "altera os caminhos:"
+
+#: gitk:2317 gitk:4796
+msgid "adding/removing string:"
+msgstr "adiciona/remove a cadeia:"
+
+#: gitk:2318 gitk:4798
+msgid "changing lines matching:"
+msgstr "altera linhas com:"
+
+#: gitk:2327 gitk:2329 gitk:4785
+msgid "Exact"
+msgstr "Exato"
+
+#: gitk:2329 gitk:4873 gitk:6742
+msgid "IgnCase"
+msgstr "IgnMaiúsculas"
+
+#: gitk:2329 gitk:4755 gitk:4871 gitk:6738
+msgid "Regexp"
+msgstr "Expr. regular"
+
+#: gitk:2331 gitk:2332 gitk:4893 gitk:4923 gitk:4930 gitk:6867 gitk:6935
+msgid "All fields"
+msgstr "Todos os campos"
+
+#: gitk:2332 gitk:4890 gitk:4923 gitk:6805
+msgid "Headline"
+msgstr "Cabeçalho"
+
+#: gitk:2333 gitk:4890 gitk:6805 gitk:6935 gitk:7408
+msgid "Comments"
+msgstr "Comentários"
+
+#: gitk:2333 gitk:4890 gitk:4895 gitk:4930 gitk:6805 gitk:7343 gitk:8849
+#: gitk:8864
+msgid "Author"
+msgstr "Autor"
+
+#: gitk:2333 gitk:4890 gitk:6805 gitk:7345
+msgid "Committer"
+msgstr "Committer"
+
+#: gitk:2367
+msgid "Search"
+msgstr "Pesquisar"
+
+#: gitk:2375
+msgid "Diff"
+msgstr "Diff"
+
+#: gitk:2377
+msgid "Old version"
+msgstr "Versão antiga"
+
+#: gitk:2379
+msgid "New version"
+msgstr "Versão nova"
+
+#: gitk:2382
+msgid "Lines of context"
+msgstr "Linhas de contexto"
+
+#: gitk:2392
+msgid "Ignore space change"
+msgstr "Ignorar espaços"
+
+#: gitk:2396 gitk:2398 gitk:7978 gitk:8225
+msgid "Line diff"
+msgstr "Diff de linha"
+
+#: gitk:2463
+msgid "Patch"
+msgstr "Patch"
+
+#: gitk:2465
+msgid "Tree"
+msgstr "Árvore"
+
+#: gitk:2635 gitk:2656
+msgid "Diff this -> selected"
+msgstr "Diff este -> seleção"
+
+#: gitk:2636 gitk:2657
+msgid "Diff selected -> this"
+msgstr "Diff seleção -> este"
+
+#: gitk:2637 gitk:2658
+msgid "Make patch"
+msgstr "Gerar patch"
+
+#: gitk:2638 gitk:9273
+msgid "Create tag"
+msgstr "Criar tag"
+
+#: gitk:2639
+msgid "Copy commit summary"
+msgstr "Copiar sumário do commit"
+
+#: gitk:2640 gitk:9404
+msgid "Write commit to file"
+msgstr "Escrever commit num ficheiro"
+
+#: gitk:2641 gitk:9461
+msgid "Create new branch"
+msgstr "Criar novo ramo"
+
+#: gitk:2642
+msgid "Cherry-pick this commit"
+msgstr "Efetuar cherry-pick deste commit"
+
+#: gitk:2643
+msgid "Reset HEAD branch to here"
+msgstr "Repor ramo HEAD para aqui"
+
+#: gitk:2644
+msgid "Mark this commit"
+msgstr "Marcar este commit"
+
+#: gitk:2645
+msgid "Return to mark"
+msgstr "Voltar à marca"
+
+#: gitk:2646
+msgid "Find descendant of this and mark"
+msgstr "Encontrar descendeste deste e da marca"
+
+#: gitk:2647
+msgid "Compare with marked commit"
+msgstr "Comparar com o commit marcado"
+
+#: gitk:2648 gitk:2659
+msgid "Diff this -> marked commit"
+msgstr "Diff este -> commit marcado"
+
+#: gitk:2649 gitk:2660
+msgid "Diff marked commit -> this"
+msgstr "Diff commit marcado -> este"
+
+#: gitk:2650
+msgid "Revert this commit"
+msgstr "Reverter este commit"
+
+#: gitk:2666
+msgid "Check out this branch"
+msgstr "Extrair este ramo"
+
+#: gitk:2667
+msgid "Remove this branch"
+msgstr "Remover este ramo"
+
+#: gitk:2668
+msgid "Copy branch name"
+msgstr "Copiar nome do ramo"
+
+#: gitk:2675
+msgid "Highlight this too"
+msgstr "Realçar este também"
+
+#: gitk:2676
+msgid "Highlight this only"
+msgstr "Realçar apenas este"
+
+#: gitk:2677
+msgid "External diff"
+msgstr "Diff externo"
+
+#: gitk:2678
+msgid "Blame parent commit"
+msgstr "Culpar commit pai"
+
+#: gitk:2679
+msgid "Copy path"
+msgstr "Copiar caminho"
+
+#: gitk:2686
+msgid "Show origin of this line"
+msgstr "Mostrar origem deste ficheiro"
+
+#: gitk:2687
+msgid "Run git gui blame on this line"
+msgstr "Executar git gui blame sobre esta linha"
+
+#: gitk:3031
+msgid "About gitk"
+msgstr "Sobre gitk"
+
+#: gitk:3033
+msgid ""
+"\n"
+"Gitk - a commit viewer for git\n"
+"\n"
+"Copyright © 2005-2016 Paul Mackerras\n"
+"\n"
+"Use and redistribute under the terms of the GNU General Public License"
+msgstr ""
+"\n"
+"Gitk - um visualizador de commits do git\n"
+"\n"
+"Copyright © 2005-2016 Paul Mackerras\n"
+"\n"
+"Use e redistribua sob os termos da GNU General Public License"
+
+#: gitk:3041 gitk:3108 gitk:9890
+msgid "Close"
+msgstr "Fechar"
+
+#: gitk:3062
+msgid "Gitk key bindings"
+msgstr "Atalhos do gitk"
+
+#: gitk:3065
+msgid "Gitk key bindings:"
+msgstr "Atalhos do gitk:"
+
+#: gitk:3067
+#, tcl-format
+msgid "<%s-Q>\t\tQuit"
+msgstr "<%s-Q>\t\tSair"
+
+#: gitk:3068
+#, tcl-format
+msgid "<%s-W>\t\tClose window"
+msgstr "<%s-W>\t\tFechar janela"
+
+#: gitk:3069
+msgid "<Home>\t\tMove to first commit"
+msgstr "<Home>\t\tMover para o primeiro commit"
+
+#: gitk:3070
+msgid "<End>\t\tMove to last commit"
+msgstr "<End>\t\tMover para o último commit"
+
+#: gitk:3071
+msgid "<Up>, p, k\tMove up one commit"
+msgstr "<Cima>, p, k\tMover para o commit acima"
+
+#: gitk:3072
+msgid "<Down>, n, j\tMove down one commit"
+msgstr "<Baixo>, n, j\tMover para o commit abaixo"
+
+#: gitk:3073
+msgid "<Left>, z, h\tGo back in history list"
+msgstr "<Esquerda>, z, h\tRecuar no histórico"
+
+#: gitk:3074
+msgid "<Right>, x, l\tGo forward in history list"
+msgstr "<Direita>, x, l\tAvançar no histórico"
+
+#: gitk:3075
+#, tcl-format
+msgid "<%s-n>\tGo to n-th parent of current commit in history list"
+msgstr "<%s-n>\tIr para o n-ésimo pai do commit atual no histórico"
+
+#: gitk:3076
+msgid "<PageUp>\tMove up one page in commit list"
+msgstr "<PageUp>\tMover a lista de commits uma página para cima"
+
+#: gitk:3077
+msgid "<PageDown>\tMove down one page in commit list"
+msgstr "<PageDown>\tMover a lista de commits uma página para baixo"
+
+#: gitk:3078
+#, tcl-format
+msgid "<%s-Home>\tScroll to top of commit list"
+msgstr "<%s-Home>\tDeslocar para o topo da lista"
+
+#: gitk:3079
+#, tcl-format
+msgid "<%s-End>\tScroll to bottom of commit list"
+msgstr "<%s-End>\tDeslocar para o fim da lista"
+
+#: gitk:3080
+#, tcl-format
+msgid "<%s-Up>\tScroll commit list up one line"
+msgstr "<%s-Cima>\tDeslocar a lista de commits uma linha para cima"
+
+#: gitk:3081
+#, tcl-format
+msgid "<%s-Down>\tScroll commit list down one line"
+msgstr "<%s-Baixo>\tDeslocar a lista de commits uma linha para baixo"
+
+#: gitk:3082
+#, tcl-format
+msgid "<%s-PageUp>\tScroll commit list up one page"
+msgstr "<%s-PageUp>\tDeslocar a lista de commits uma página para cima"
+
+#: gitk:3083
+#, tcl-format
+msgid "<%s-PageDown>\tScroll commit list down one page"
+msgstr "<%s-PageDown>\tDeslocar a lista de commits uma página para baixo"
+
+#: gitk:3084
+msgid "<Shift-Up>\tFind backwards (upwards, later commits)"
+msgstr "<Shift-Cima>\tProcurar para trás (para cima, commits posteriores)"
+
+#: gitk:3085
+msgid "<Shift-Down>\tFind forwards (downwards, earlier commits)"
+msgstr "<Shift-Baixo>\tProcurar para a frente (para baixo, commits anteriores)"
+
+#: gitk:3086
+msgid "<Delete>, b\tScroll diff view up one page"
+msgstr "<Delete>, b\tDeslocar vista diff uma página para cima"
+
+#: gitk:3087
+msgid "<Backspace>\tScroll diff view up one page"
+msgstr "<Retrocesso>\tDeslocar vista diff uma página para cima"
+
+#: gitk:3088
+msgid "<Space>\t\tScroll diff view down one page"
+msgstr "<Espaço>\tDeslocar vista diff uma página para baixo"
+
+#: gitk:3089
+msgid "u\t\tScroll diff view up 18 lines"
+msgstr "u\t\tDeslocar vista diff 18 linhas para cima"
+
+#: gitk:3090
+msgid "d\t\tScroll diff view down 18 lines"
+msgstr "d\t\tDeslocar vista diff 18 linhas para baixo"
+
+#: gitk:3091
+#, tcl-format
+msgid "<%s-F>\t\tFind"
+msgstr "<%s-F>\t\tProcurar"
+
+#: gitk:3092
+#, tcl-format
+msgid "<%s-G>\t\tMove to next find hit"
+msgstr "<%s-G>\t\tMover para a ocorrência seguinte"
+
+#: gitk:3093
+msgid "<Return>\tMove to next find hit"
+msgstr "<Return>\tMover para a ocorrência seguinte"
+
+#: gitk:3094
+msgid "g\t\tGo to commit"
+msgstr "g\t\tIr para o commit"
+
+#: gitk:3095
+msgid "/\t\tFocus the search box"
+msgstr "/\t\tFocar a caixa de pesquisa"
+
+#: gitk:3096
+msgid "?\t\tMove to previous find hit"
+msgstr "?\t\tMover para a ocorrência anterior"
+
+#: gitk:3097
+msgid "f\t\tScroll diff view to next file"
+msgstr "f\t\tDeslocar vista diff para o ficheiro seguinte"
+
+#: gitk:3098
+#, tcl-format
+msgid "<%s-S>\t\tSearch for next hit in diff view"
+msgstr "<%s-S>\t\tProcurar pela ocorrência seguinte na vista diff"
+
+#: gitk:3099
+#, tcl-format
+msgid "<%s-R>\t\tSearch for previous hit in diff view"
+msgstr "<%s-R>\t\tProcurar pela ocorrência anterior na vista diff"
+
+#: gitk:3100
+#, tcl-format
+msgid "<%s-KP+>\tIncrease font size"
+msgstr "<%s-KP+>\tAumentar o tamanho da letra"
+
+#: gitk:3101
+#, tcl-format
+msgid "<%s-plus>\tIncrease font size"
+msgstr "<%s-mais>\tAumentar o tamanho da letra"
+
+#: gitk:3102
+#, tcl-format
+msgid "<%s-KP->\tDecrease font size"
+msgstr "<%s-KP->\tDiminuir o tamanho da letra"
+
+#: gitk:3103
+#, tcl-format
+msgid "<%s-minus>\tDecrease font size"
+msgstr "<%s-menos>\tDiminuir o tamanho da letra"
+
+#: gitk:3104
+msgid "<F5>\t\tUpdate"
+msgstr "<F5>\t\tAtualizar"
+
+#: gitk:3569 gitk:3578
+#, tcl-format
+msgid "Error creating temporary directory %s:"
+msgstr "Erro ao criar ficheiro temporário %s:"
+
+#: gitk:3591
+#, tcl-format
+msgid "Error getting \"%s\" from %s:"
+msgstr "Erro ao obter \"%s\" de %s:"
+
+#: gitk:3654
+msgid "command failed:"
+msgstr "o comando falhou:"
+
+#: gitk:3803
+msgid "No such commit"
+msgstr "Commit inexistente"
+
+#: gitk:3817
+msgid "git gui blame: command failed:"
+msgstr "git gui blame: o comando falhou:"
+
+#: gitk:3848
+#, tcl-format
+msgid "Couldn't read merge head: %s"
+msgstr "Não foi possível ler a cabeça de integração: %s"
+
+#: gitk:3856
+#, tcl-format
+msgid "Error reading index: %s"
+msgstr "Erro ao ler o índice: %s"
+
+#: gitk:3881
+#, tcl-format
+msgid "Couldn't start git blame: %s"
+msgstr "Não foi possível iniciar git blame: %s"
+
+#: gitk:3884 gitk:6773
+msgid "Searching"
+msgstr "A procurar"
+
+#: gitk:3916
+#, tcl-format
+msgid "Error running git blame: %s"
+msgstr "Erro ao executar git blame: %s"
+
+#: gitk:3944
+#, tcl-format
+msgid "That line comes from commit %s,  which is not in this view"
+msgstr "Essa linha provém do commit %s, que não está nesta vista"
+
+#: gitk:3958
+msgid "External diff viewer failed:"
+msgstr "Visualizador diff externo falhou:"
+
+#: gitk:4062
+msgid "All files"
+msgstr "Todos os ficheiros"
+
+#: gitk:4086
+msgid "View"
+msgstr "Vista"
+
+#: gitk:4089
+msgid "Gitk view definition"
+msgstr "Definição de vistas do gitk"
+
+#: gitk:4093
+msgid "Remember this view"
+msgstr "Recordar esta vista"
+
+#: gitk:4094
+msgid "References (space separated list):"
+msgstr "Referências (lista separada por espaço):"
+
+#: gitk:4095
+msgid "Branches & tags:"
+msgstr "Ramos e tags:"
+
+#: gitk:4096
+msgid "All refs"
+msgstr "Todas as referências"
+
+#: gitk:4097
+msgid "All (local) branches"
+msgstr "Todos os ramos (locais)"
+
+#: gitk:4098
+msgid "All tags"
+msgstr "Todas as tags"
+
+#: gitk:4099
+msgid "All remote-tracking branches"
+msgstr "Todos os ramos remotos de monitorização"
+
+#: gitk:4100
+msgid "Commit Info (regular expressions):"
+msgstr "Informação Sobre o Commit (expressões regulares):"
+
+#: gitk:4101
+msgid "Author:"
+msgstr "Autor:"
+
+#: gitk:4102
+msgid "Committer:"
+msgstr "Committer:"
+
+#: gitk:4103
+msgid "Commit Message:"
+msgstr "Mensagem de Commit:"
+
+#: gitk:4104
+msgid "Matches all Commit Info criteria"
+msgstr "Corresponde a todos os critérios da Informação Sobre o Commit"
+
+#: gitk:4105
+msgid "Matches no Commit Info criteria"
+msgstr "Não corresponde a nenhum critério da Informação Sobre o Commit"
+
+#: gitk:4106
+msgid "Changes to Files:"
+msgstr "Alterações nos Ficheiros:"
+
+#: gitk:4107
+msgid "Fixed String"
+msgstr "Cadeia Fixa"
+
+#: gitk:4108
+msgid "Regular Expression"
+msgstr "Expressão Regular"
+
+#: gitk:4109
+msgid "Search string:"
+msgstr "Procurar pela cadeia:"
+
+#: gitk:4110
+msgid ""
+"Commit Dates (\"2 weeks ago\", \"2009-03-17 15:27:38\", \"March 17, 2009 "
+"15:27:38\"):"
+msgstr ""
+"Datas de Commit (\"2 weeks ago\", \"2009-03-17 15:27:38\", \"March 17, 2009 "
+"15:27:38\"):"
+
+#: gitk:4111
+msgid "Since:"
+msgstr "Desde:"
+
+#: gitk:4112
+msgid "Until:"
+msgstr "Até:"
+
+#: gitk:4113
+msgid "Limit and/or skip a number of revisions (positive integer):"
+msgstr "Limitar e/ou ignorar um número de revisões (inteiro positivo):"
+
+#: gitk:4114
+msgid "Number to show:"
+msgstr "Número a mostrar:"
+
+#: gitk:4115
+msgid "Number to skip:"
+msgstr "Número a ignorar:"
+
+#: gitk:4116
+msgid "Miscellaneous options:"
+msgstr "Opções diversas:"
+
+#: gitk:4117
+msgid "Strictly sort by date"
+msgstr "Ordenar estritamente pela data"
+
+#: gitk:4118
+msgid "Mark branch sides"
+msgstr "Marcar lado dos ramos"
+
+#: gitk:4119
+msgid "Limit to first parent"
+msgstr "Restringir ao primeiro pai"
+
+#: gitk:4120
+msgid "Simple history"
+msgstr "Histórico simples"
+
+#: gitk:4121
+msgid "Additional arguments to git log:"
+msgstr "Argumentos adicionais ao git log:"
+
+#: gitk:4122
+msgid "Enter files and directories to include, one per line:"
+msgstr "Introduza ficheiros e diretórios para incluir, um por linha:"
+
+#: gitk:4123
+msgid "Command to generate more commits to include:"
+msgstr "Comando para gerar mais commits para incluir:"
+
+#: gitk:4247
+msgid "Gitk: edit view"
+msgstr "Gitk: editar vista"
+
+#: gitk:4255
+msgid "-- criteria for selecting revisions"
+msgstr "-- critério para selecionar revisões"
+
+#: gitk:4260
+msgid "View Name"
+msgstr "Nome da Vista"
+
+#: gitk:4335
+msgid "Apply (F5)"
+msgstr "Aplicar (F5)"
+
+#: gitk:4373
+msgid "Error in commit selection arguments:"
+msgstr "Erro nos argumentos de seleção de commits:"
+
+#: gitk:4428 gitk:4481 gitk:4943 gitk:4957 gitk:6227 gitk:12410 gitk:12411
+msgid "None"
+msgstr "Nenhum"
+
+#: gitk:5040 gitk:5045
+msgid "Descendant"
+msgstr "Descendente"
+
+#: gitk:5041
+msgid "Not descendant"
+msgstr "Não descendente"
+
+#: gitk:5048 gitk:5053
+msgid "Ancestor"
+msgstr "Antecessor"
+
+#: gitk:5049
+msgid "Not ancestor"
+msgstr "Não antecessor"
+
+#: gitk:5343
+msgid "Local changes checked in to index but not committed"
+msgstr "Alterações locais preparadas no índice mas não submetidas"
+
+#: gitk:5379
+msgid "Local uncommitted changes, not checked in to index"
+msgstr "Alterações locais não submetidas, não preparadas no índice"
+
+#: gitk:7153
+msgid "and many more"
+msgstr "e muitos mais"
+
+#: gitk:7156
+msgid "many"
+msgstr "muitos"
+
+#: gitk:7347
+msgid "Tags:"
+msgstr "Tags:"
+
+#: gitk:7364 gitk:7370 gitk:8844
+msgid "Parent"
+msgstr "Pai"
+
+#: gitk:7375
+msgid "Child"
+msgstr "Filho"
+
+#: gitk:7384
+msgid "Branch"
+msgstr "Ramo"
+
+#: gitk:7387
+msgid "Follows"
+msgstr "Sucede"
+
+#: gitk:7390
+msgid "Precedes"
+msgstr "Precede"
+
+#: gitk:7985
+#, tcl-format
+msgid "Error getting diffs: %s"
+msgstr "Erro ao obter diferenças: %s"
+
+#: gitk:8669
+msgid "Goto:"
+msgstr "Ir para:"
+
+#: gitk:8690
+#, tcl-format
+msgid "Short SHA1 id %s is ambiguous"
+msgstr "O id SHA1 abreviado %s é ambíguo"
+
+#: gitk:8697
+#, tcl-format
+msgid "Revision %s is not known"
+msgstr "A revisão %s não é conhecida"
+
+#: gitk:8707
+#, tcl-format
+msgid "SHA1 id %s is not known"
+msgstr "O id SHA1 %s não é conhecido"
+
+#: gitk:8709
+#, tcl-format
+msgid "Revision %s is not in the current view"
+msgstr "A revisão %s não se encontra na vista atual"
+
+#: gitk:8851 gitk:8866
+msgid "Date"
+msgstr "Data"
+
+#: gitk:8854
+msgid "Children"
+msgstr "Filhos"
+
+#: gitk:8917
+#, tcl-format
+msgid "Reset %s branch to here"
+msgstr "Repor o ramo %s para aqui"
+
+#: gitk:8919
+msgid "Detached head: can't reset"
+msgstr "Cabeça destacada: não é possível repor"
+
+#: gitk:9024 gitk:9030
+msgid "Skipping merge commit "
+msgstr "A ignorar commit de integração "
+
+#: gitk:9039 gitk:9044
+msgid "Error getting patch ID for "
+msgstr "Erro ao obter ID de patch de "
+
+#: gitk:9040 gitk:9045
+msgid " - stopping\n"
+msgstr " - a interromper\n"
+
+#: gitk:9050 gitk:9053 gitk:9061 gitk:9075 gitk:9084
+msgid "Commit "
+msgstr "Commit "
+
+#: gitk:9054
+msgid ""
+" is the same patch as\n"
+"       "
+msgstr ""
+" é o mesmo patch que\n"
+"       "
+
+#: gitk:9062
+msgid ""
+" differs from\n"
+"       "
+msgstr ""
+" difere de\n"
+"       "
+
+#: gitk:9064
+msgid ""
+"Diff of commits:\n"
+"\n"
+msgstr ""
+"Diferença dos commits:\n"
+"\n"
+
+#: gitk:9076 gitk:9085
+#, tcl-format
+msgid " has %s children - stopping\n"
+msgstr " tem %s filhos - a interromper\n"
+
+#: gitk:9104
+#, tcl-format
+msgid "Error writing commit to file: %s"
+msgstr "Erro ao escrever commit no ficheiro: %s"
+
+#: gitk:9110
+#, tcl-format
+msgid "Error diffing commits: %s"
+msgstr "Erro ao calcular as diferenças dos commits: %s"
+
+#: gitk:9156
+msgid "Top"
+msgstr "Topo"
+
+#: gitk:9157
+msgid "From"
+msgstr "De"
+
+#: gitk:9162
+msgid "To"
+msgstr "Para"
+
+#: gitk:9186
+msgid "Generate patch"
+msgstr "Gerar patch"
+
+#: gitk:9188
+msgid "From:"
+msgstr "De:"
+
+#: gitk:9197
+msgid "To:"
+msgstr "Para:"
+
+#: gitk:9206
+msgid "Reverse"
+msgstr "Reverter"
+
+#: gitk:9208 gitk:9418
+msgid "Output file:"
+msgstr "Ficheiro de saída:"
+
+#: gitk:9214
+msgid "Generate"
+msgstr "Gerar"
+
+#: gitk:9252
+msgid "Error creating patch:"
+msgstr "Erro ao criar patch:"
+
+#: gitk:9275 gitk:9406 gitk:9463
+msgid "ID:"
+msgstr "ID:"
+
+#: gitk:9284
+msgid "Tag name:"
+msgstr "Nome da tag:"
+
+#: gitk:9287
+msgid "Tag message is optional"
+msgstr "A mensagem da tag é opcional"
+
+#: gitk:9289
+msgid "Tag message:"
+msgstr "Mensagem da tag:"
+
+#: gitk:9293 gitk:9472
+msgid "Create"
+msgstr "Criar"
+
+#: gitk:9311
+msgid "No tag name specified"
+msgstr "Nenhum nome de tag especificado"
+
+#: gitk:9315
+#, tcl-format
+msgid "Tag \"%s\" already exists"
+msgstr "A tag \"%s\" já existe"
+
+#: gitk:9325
+msgid "Error creating tag:"
+msgstr "Erro ao criar tag:"
+
+#: gitk:9415
+msgid "Command:"
+msgstr "Comando:"
+
+#: gitk:9423
+msgid "Write"
+msgstr "Escrever"
+
+#: gitk:9441
+msgid "Error writing commit:"
+msgstr "Erro ao escrever commit:"
+
+#: gitk:9468
+msgid "Name:"
+msgstr "Nome:"
+
+#: gitk:9491
+msgid "Please specify a name for the new branch"
+msgstr "Especifique um nome para o novo ramo"
+
+#: gitk:9496
+#, tcl-format
+msgid "Branch '%s' already exists. Overwrite?"
+msgstr "O ramo '%s' já existe. Substituí-lo?"
+
+#: gitk:9563
+#, tcl-format
+msgid "Commit %s is already included in branch %s -- really re-apply it?"
+msgstr "O commit %s já está incluído no ramo %s -- reaplicá-lo mesmo assim?"
+
+#: gitk:9568
+msgid "Cherry-picking"
+msgstr "A efetuar cherry-pick"
+
+#: gitk:9577
+#, tcl-format
+msgid ""
+"Cherry-pick failed because of local changes to file '%s'.\n"
+"Please commit, reset or stash your changes and try again."
+msgstr ""
+"Falha ao efetuar cherry-pick devido a alterações locais no ficheiro '%s'.\n"
+"Submeta, empilhe ou reponha as alterações e tente de novo."
+
+#: gitk:9583
+msgid ""
+"Cherry-pick failed because of merge conflict.\n"
+"Do you wish to run git citool to resolve it?"
+msgstr ""
+"Falha ao efetuar cherry-pick devido a conflito de integração.\n"
+"Deseja executar git citool para resolvê-lo?"
+
+#: gitk:9599 gitk:9657
+msgid "No changes committed"
+msgstr "Não foi submetida nenhum alteração"
+
+#: gitk:9626
+#, tcl-format
+msgid "Commit %s is not included in branch %s -- really revert it?"
+msgstr "O commit %s não está incluído no ramo %s -- revertê-lo mesmo assim?"
+
+#: gitk:9631
+msgid "Reverting"
+msgstr "A reverter"
+
+#: gitk:9639
+#, 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 ""
+"Falha ao reverter devido a alterações locais nos seguintes ficheiros:%s "
+"Submeta, empilhe ou reponha as alterações e tente de novo."
+
+#: gitk:9643
+msgid ""
+"Revert failed because of merge conflict.\n"
+" Do you wish to run git citool to resolve it?"
+msgstr ""
+"Falha ao reverter devido a conflito de integração.\n"
+"Deseja executar git citool para resolvê-lo?"
+
+#: gitk:9686
+msgid "Confirm reset"
+msgstr "Confirmar reposição"
+
+#: gitk:9688
+#, tcl-format
+msgid "Reset branch %s to %s?"
+msgstr "Repor o ramo %s para %s?"
+
+#: gitk:9690
+msgid "Reset type:"
+msgstr "Tipo de reposição:"
+
+#: gitk:9693
+msgid "Soft: Leave working tree and index untouched"
+msgstr "Suave: Deixar a árvore de trabalho e o índice intactos"
+
+#: gitk:9696
+msgid "Mixed: Leave working tree untouched, reset index"
+msgstr "Misto: Deixar a árvore de trabalho intacta, repor índice"
+
+#: gitk:9699
+msgid ""
+"Hard: Reset working tree and index\n"
+"(discard ALL local changes)"
+msgstr ""
+"Forte: Repor árvore de trabalho e índice\n"
+"(descartar TODAS as alterações locais)"
+
+#: gitk:9716
+msgid "Resetting"
+msgstr "A repor"
+
+#: gitk:9776
+msgid "Checking out"
+msgstr "A extrair"
+
+#: gitk:9829
+msgid "Cannot delete the currently checked-out branch"
+msgstr "Não é possível eliminar o ramo atual extraído"
+
+#: gitk:9835
+#, tcl-format
+msgid ""
+"The commits on branch %s aren't on any other branch.\n"
+"Really delete branch %s?"
+msgstr ""
+"Os commits no ramo %s não estão presentes em mais nenhum ramo.\n"
+"Eliminar o ramo %s mesmo assim?"
+
+#: gitk:9866
+#, tcl-format
+msgid "Tags and heads: %s"
+msgstr "Tags e cabeças: %s"
+
+#: gitk:9883
+msgid "Filter"
+msgstr "Filtrar"
+
+#: gitk:10179
+msgid ""
+"Error reading commit topology information; branch and preceding/following "
+"tag information will be incomplete."
+msgstr ""
+"Erro ao ler informação de topologia do commit; a informação do ramo e da tag "
+"precedente/seguinte ficará incompleta."
+
+#: gitk:11156
+msgid "Tag"
+msgstr "Tag"
+
+#: gitk:11160
+msgid "Id"
+msgstr "Id"
+
+#: gitk:11243
+msgid "Gitk font chooser"
+msgstr "Escolha de tipo de letra do gitk"
+
+#: gitk:11260
+msgid "B"
+msgstr "B"
+
+#: gitk:11263
+msgid "I"
+msgstr "I"
+
+#: gitk:11381
+msgid "Commit list display options"
+msgstr "Opções de visualização da lista de commits"
+
+#: gitk:11384
+msgid "Maximum graph width (lines)"
+msgstr "Largura máxima do gráfico (linhas)"
+
+#: gitk:11388
+#, no-tcl-format
+msgid "Maximum graph width (% of pane)"
+msgstr "Largura máxima do gráfico (% do painel)"
+
+#: gitk:11391
+msgid "Show local changes"
+msgstr "Mostrar alterações locais"
+
+#: gitk:11394
+msgid "Auto-select SHA1 (length)"
+msgstr "Selecionar automaticamente SHA1 (largura)"
+
+#: gitk:11398
+msgid "Hide remote refs"
+msgstr "Ocultar referências remotas"
+
+#: gitk:11402
+msgid "Diff display options"
+msgstr "Opções de visualização de diferenças"
+
+#: gitk:11404
+msgid "Tab spacing"
+msgstr "Espaçamento da tabulação"
+
+#: gitk:11407
+msgid "Display nearby tags/heads"
+msgstr "Mostrar tags/cabeças próximas"
+
+#: gitk:11410
+msgid "Maximum # tags/heads to show"
+msgstr "Nº máximo de tags/cabeças a mostrar"
+
+#: gitk:11413
+msgid "Limit diffs to listed paths"
+msgstr "Limitar diferenças aos caminhos listados"
+
+#: gitk:11416
+msgid "Support per-file encodings"
+msgstr "Suportar codificação por cada ficheiro"
+
+#: gitk:11422 gitk:11569
+msgid "External diff tool"
+msgstr "Ferramenta diff externa"
+
+#: gitk:11423
+msgid "Choose..."
+msgstr "Escolher..."
+
+#: gitk:11428
+msgid "General options"
+msgstr "Opções gerais"
+
+#: gitk:11431
+msgid "Use themed widgets"
+msgstr "Usar widgets com estilo"
+
+#: gitk:11433
+msgid "(change requires restart)"
+msgstr "(alteração exige reiniciar)"
+
+#: gitk:11435
+msgid "(currently unavailable)"
+msgstr "(não disponível de momento)"
+
+#: gitk:11446
+msgid "Colors: press to choose"
+msgstr "Cores: pressione para escolher"
+
+#: gitk:11449
+msgid "Interface"
+msgstr "Interface"
+
+#: gitk:11450
+msgid "interface"
+msgstr "interface"
+
+#: gitk:11453
+msgid "Background"
+msgstr "Fundo"
+
+#: gitk:11454 gitk:11484
+msgid "background"
+msgstr "fundo"
+
+#: gitk:11457
+msgid "Foreground"
+msgstr "Primeiro plano"
+
+#: gitk:11458
+msgid "foreground"
+msgstr "primeiro plano"
+
+#: gitk:11461
+msgid "Diff: old lines"
+msgstr "Diff: linhas antigas"
+
+#: gitk:11462
+msgid "diff old lines"
+msgstr "diff linhas antigas"
+
+#: gitk:11466
+msgid "Diff: new lines"
+msgstr "Diff: linhas novas"
+
+#: gitk:11467
+msgid "diff new lines"
+msgstr "diff linhas novas"
+
+#: gitk:11471
+msgid "Diff: hunk header"
+msgstr "Diff: cabeçalho do excerto"
+
+#: gitk:11473
+msgid "diff hunk header"
+msgstr "diff cabeçalho do excerto"
+
+#: gitk:11477
+msgid "Marked line bg"
+msgstr "Fundo da linha marcada"
+
+#: gitk:11479
+msgid "marked line background"
+msgstr "fundo da linha marcada"
+
+#: gitk:11483
+msgid "Select bg"
+msgstr "Selecionar fundo"
+
+#: gitk:11492
+msgid "Fonts: press to choose"
+msgstr "Tipo de letra: pressione para escolher"
+
+#: gitk:11494
+msgid "Main font"
+msgstr "Tipo de letra principal"
+
+#: gitk:11495
+msgid "Diff display font"
+msgstr "Tipo de letra ao mostrar diferenças"
+
+#: gitk:11496
+msgid "User interface font"
+msgstr "Tipo de letra da interface de utilizador"
+
+#: gitk:11518
+msgid "Gitk preferences"
+msgstr "Preferências do gitk"
+
+#: gitk:11527
+msgid "General"
+msgstr "Geral"
+
+#: gitk:11528
+msgid "Colors"
+msgstr "Cores"
+
+#: gitk:11529
+msgid "Fonts"
+msgstr "Tipos de letra"
+
+#: gitk:11579
+#, tcl-format
+msgid "Gitk: choose color for %s"
+msgstr "Gitk: escolher cor de %s"
+
+#: gitk:12092
+msgid ""
+"Sorry, gitk cannot run with this version of Tcl/Tk.\n"
+" Gitk requires at least Tcl/Tk 8.4."
+msgstr ""
+"Não é possível executar o gitk com esta versão do Tcl/Tk.\n"
+"O gitk requer pelo menos Tcl/Tk 8.4."
+
+#: gitk:12302
+msgid "Cannot find a git repository here."
+msgstr "Não foi encontrado nenhum repositório git aqui."
+
+#: gitk:12349
+#, tcl-format
+msgid "Ambiguous argument '%s': both revision and filename"
+msgstr "Argumento '%s' ambíguo: pode ser uma revisão ou um ficheiro"
+
+#: gitk:12361
+msgid "Bad arguments to gitk:"
+msgstr "Argumentos do gitk incorretos:"
index 17ed026aa7da7c636457c302b2c28f7b866ae45a..9b08c263eadea7226a70d92d072f7a581da2bd20 100644 (file)
@@ -3,15 +3,15 @@
 # Translators:
 # 0xAX <kuleshovmail@gmail.com>, 2014
 # Alex Riesen <raa.lkml@gmail.com>, 2015
-# Dimitriy Ryazantcev <DJm00n@mail.ru>, 2015
+# Dimitriy Ryazantcev <DJm00n@mail.ru>, 2015-2016
 # Dmitry Potapov <dpotapov@gmail.com>, 2009
 # Skip <bsvskip@rambler.ru>, 2011
 msgid ""
 msgstr ""
 "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: 2015-10-12 10:14+0000\n"
+"POT-Creation-Date: 2016-12-15 00:18+0200\n"
+"PO-Revision-Date: 2016-12-14 22:23+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"
@@ -24,11 +24,11 @@ msgstr ""
 msgid "Couldn't get list of unmerged files:"
 msgstr "Невозможно получить список файлов незавершённой операции слияния:"
 
-#: gitk:212 gitk:2381
+#: gitk:212 gitk:2403
 msgid "Color words"
 msgstr "Цветные слова"
 
-#: gitk:217 gitk:2381 gitk:8220 gitk:8253
+#: gitk:217 gitk:2403 gitk:8249 gitk:8282
 msgid "Markup words"
 msgstr "Помеченые слова"
 
@@ -58,1272 +58,1314 @@ msgstr "Ошибка запуска git log:"
 msgid "Reading"
 msgstr "Чтение"
 
-#: gitk:496 gitk:4525
+#: gitk:496 gitk:4549
 msgid "Reading commits..."
 msgstr "Чтение коммитов..."
 
-#: gitk:499 gitk:1637 gitk:4528
+#: gitk:499 gitk:1641 gitk:4552
 msgid "No commits selected"
 msgstr "Ничего не выбрано"
 
-#: gitk:1445 gitk:4045 gitk:12432
+#: gitk:1449 gitk:4069 gitk:12583
 msgid "Command line"
 msgstr "Командная строка"
 
-#: gitk:1511
+#: gitk:1515
 msgid "Can't parse git log output:"
 msgstr "Ошибка обработки вывода команды git log:"
 
-#: gitk:1740
+#: gitk:1744
 msgid "No commit information available"
 msgstr "Нет информации о коммите"
 
-#: gitk:1903 gitk:1932 gitk:4315 gitk:9669 gitk:11241 gitk:11521
+#: gitk:1907 gitk:1936 gitk:4339 gitk:9789 gitk:11388 gitk:11668
 msgid "OK"
 msgstr "Ok"
 
-#: gitk:1934 gitk:4317 gitk:9196 gitk:9275 gitk:9391 gitk:9440 gitk:9671
-#: gitk:11242 gitk:11522
+#: gitk:1938 gitk:4341 gitk:9225 gitk:9304 gitk:9434 gitk:9520 gitk:9791
+#: gitk:11389 gitk:11669
 msgid "Cancel"
 msgstr "Отмена"
 
-#: gitk:2069
+#: gitk:2087
 msgid "&Update"
 msgstr "Обновить"
 
-#: gitk:2070
+#: gitk:2088
 msgid "&Reload"
 msgstr "Перечитать"
 
-#: gitk:2071
+#: gitk:2089
 msgid "Reread re&ferences"
 msgstr "Обновить список ссылок"
 
-#: gitk:2072
+#: gitk:2090
 msgid "&List references"
 msgstr "Список ссылок"
 
-#: gitk:2074
+#: gitk:2092
 msgid "Start git &gui"
 msgstr "Запустить git gui"
 
-#: gitk:2076
+#: gitk:2094
 msgid "&Quit"
 msgstr "Завершить"
 
-#: gitk:2068
+#: gitk:2086
 msgid "&File"
 msgstr "Файл"
 
-#: gitk:2080
+#: gitk:2098
 msgid "&Preferences"
 msgstr "Настройки"
 
-#: gitk:2079
+#: gitk:2097
 msgid "&Edit"
 msgstr "Редактировать"
 
-#: gitk:2084
+#: gitk:2102
 msgid "&New view..."
 msgstr "Новое представление..."
 
-#: gitk:2085
+#: gitk:2103
 msgid "&Edit view..."
 msgstr "Редактировать представление..."
 
-#: gitk:2086
+#: gitk:2104
 msgid "&Delete view"
 msgstr "Удалить представление"
 
-#: gitk:2088 gitk:4043
+#: gitk:2106
 msgid "&All files"
 msgstr "Все файлы"
 
-#: gitk:2083 gitk:4067
+#: gitk:2101
 msgid "&View"
 msgstr "Представление"
 
-#: gitk:2093 gitk:2103 gitk:3012
+#: gitk:2111 gitk:2121
 msgid "&About gitk"
 msgstr "О gitk"
 
-#: gitk:2094 gitk:2108
+#: gitk:2112 gitk:2126
 msgid "&Key bindings"
 msgstr "Назначения клавиатуры"
 
-#: gitk:2092 gitk:2107
+#: gitk:2110 gitk:2125
 msgid "&Help"
 msgstr "Подсказка"
 
-#: gitk:2185 gitk:8652
+#: gitk:2203 gitk:8681
 msgid "SHA1 ID:"
 msgstr "SHA1 ID:"
 
-#: gitk:2229
+#: gitk:2247
 msgid "Row"
 msgstr "Строка"
 
-#: gitk:2267
+#: gitk:2285
 msgid "Find"
 msgstr "Поиск"
 
-#: gitk:2295
+#: gitk:2313
 msgid "commit"
 msgstr "коммит"
 
-#: gitk:2299 gitk:2301 gitk:4687 gitk:4710 gitk:4734 gitk:6755 gitk:6827
-#: gitk:6912
+#: gitk:2317 gitk:2319 gitk:4711 gitk:4734 gitk:4758 gitk:6779 gitk:6851
+#: gitk:6936
 msgid "containing:"
 msgstr "содержащее:"
 
-#: gitk:2302 gitk:3526 gitk:3531 gitk:4763
+#: gitk:2320 gitk:3550 gitk:3555 gitk:4787
 msgid "touching paths:"
 msgstr "касательно файлов:"
 
-#: gitk:2303 gitk:4777
+#: gitk:2321 gitk:4801
 msgid "adding/removing string:"
 msgstr "добавив/удалив строку:"
 
-#: gitk:2304 gitk:4779
+#: gitk:2322 gitk:4803
 msgid "changing lines matching:"
 msgstr "изменяя совпадающие строки:"
 
-#: gitk:2313 gitk:2315 gitk:4766
+#: gitk:2331 gitk:2333 gitk:4790
 msgid "Exact"
 msgstr "Точно"
 
-#: gitk:2315 gitk:4854 gitk:6723
+#: gitk:2333 gitk:4878 gitk:6747
 msgid "IgnCase"
 msgstr "Игнорировать большие/маленькие"
 
-#: gitk:2315 gitk:4736 gitk:4852 gitk:6719
+#: gitk:2333 gitk:4760 gitk:4876 gitk:6743
 msgid "Regexp"
 msgstr "Регулярные выражения"
 
-#: gitk:2317 gitk:2318 gitk:4874 gitk:4904 gitk:4911 gitk:6848 gitk:6916
+#: gitk:2335 gitk:2336 gitk:4898 gitk:4928 gitk:4935 gitk:6872 gitk:6940
 msgid "All fields"
 msgstr "Во всех полях"
 
-#: gitk:2318 gitk:4871 gitk:4904 gitk:6786
+#: gitk:2336 gitk:4895 gitk:4928 gitk:6810
 msgid "Headline"
 msgstr "Заголовок"
 
-#: gitk:2319 gitk:4871 gitk:6786 gitk:6916 gitk:7389
+#: gitk:2337 gitk:4895 gitk:6810 gitk:6940 gitk:7413
 msgid "Comments"
 msgstr "Комментарии"
 
-#: gitk:2319 gitk:4871 gitk:4876 gitk:4911 gitk:6786 gitk:7324 gitk:8830
-#: gitk:8845
+#: gitk:2337 gitk:4895 gitk:4900 gitk:4935 gitk:6810 gitk:7348 gitk:8859
+#: gitk:8874
 msgid "Author"
 msgstr "Автор"
 
-#: gitk:2319 gitk:4871 gitk:6786 gitk:7326
+#: gitk:2337 gitk:4895 gitk:6810 gitk:7350
 msgid "Committer"
 msgstr "Коммитер"
 
-#: gitk:2350
+#: gitk:2371
 msgid "Search"
 msgstr "Найти"
 
-#: gitk:2358
+#: gitk:2379
 msgid "Diff"
 msgstr "Сравнить"
 
-#: gitk:2360
+#: gitk:2381
 msgid "Old version"
 msgstr "Старая версия"
 
-#: gitk:2362
+#: gitk:2383
 msgid "New version"
 msgstr "Новая версия"
 
-#: gitk:2364
+#: gitk:2386
 msgid "Lines of context"
 msgstr "Строк контекста"
 
-#: gitk:2374
+#: gitk:2396
 msgid "Ignore space change"
 msgstr "Игнорировать пробелы"
 
-#: gitk:2378 gitk:2380 gitk:7959 gitk:8206
+#: gitk:2400 gitk:2402 gitk:7983 gitk:8235
 msgid "Line diff"
 msgstr "Изменения строк"
 
-#: gitk:2445
+#: gitk:2467
 msgid "Patch"
 msgstr "Патч"
 
-#: gitk:2447
+#: gitk:2469
 msgid "Tree"
 msgstr "Файлы"
 
-#: gitk:2617 gitk:2637
+#: gitk:2639 gitk:2660
 msgid "Diff this -> selected"
 msgstr "Сравнить этот коммит с выделенным"
 
-#: gitk:2618 gitk:2638
+#: gitk:2640 gitk:2661
 msgid "Diff selected -> this"
 msgstr "Сравнить выделенный с этим коммитом"
 
-#: gitk:2619 gitk:2639
+#: gitk:2641 gitk:2662
 msgid "Make patch"
 msgstr "Создать патч"
 
-#: gitk:2620 gitk:9254
+#: gitk:2642 gitk:9283
 msgid "Create tag"
 msgstr "Создать метку"
 
-#: gitk:2621 gitk:9371
+#: gitk:2643
+msgid "Copy commit summary"
+msgstr "Копировать информацию о коммите"
+
+#: gitk:2644 gitk:9414
 msgid "Write commit to file"
 msgstr "Сохранить коммит в файл"
 
-#: gitk:2622 gitk:9428
+#: gitk:2645
 msgid "Create new branch"
 msgstr "Создать ветку"
 
-#: gitk:2623
+#: gitk:2646
 msgid "Cherry-pick this commit"
-msgstr "Ð\9eÑ\82боÑ\80 Ð»Ñ\83Ñ\87Ñ\88его Ð´Ð»Ñ\8f Ñ\8dÑ\82ого ÐºÐ¾Ð¼Ð¼Ð¸Ñ\82а"
+msgstr "Ð\9aопиÑ\80оваÑ\82Ñ\8c Ñ\8dÑ\82оÑ\82 ÐºÐ¾Ð¼Ð¼Ð¸Ñ\82 Ð² Ñ\82екÑ\83Ñ\89Ñ\83Ñ\8e Ð²ÐµÑ\82кÑ\83"
 
-#: gitk:2624
+#: gitk:2647
 msgid "Reset HEAD branch to here"
 msgstr "Установить HEAD на этот коммит"
 
-#: gitk:2625
+#: gitk:2648
 msgid "Mark this commit"
 msgstr "Пометить этот коммит"
 
-#: gitk:2626
+#: gitk:2649
 msgid "Return to mark"
 msgstr "Вернуться на пометку"
 
-#: gitk:2627
+#: gitk:2650
 msgid "Find descendant of this and mark"
 msgstr "Найти и пометить потомка этого коммита"
 
-#: gitk:2628
+#: gitk:2651
 msgid "Compare with marked commit"
 msgstr "Сравнить с помеченным коммитом"
 
-#: gitk:2629 gitk:2640
+#: gitk:2652 gitk:2663
 msgid "Diff this -> marked commit"
 msgstr "Сравнить выделенное с помеченным коммитом"
 
-#: gitk:2630 gitk:2641
+#: gitk:2653 gitk:2664
 msgid "Diff marked commit -> this"
 msgstr "Сравнить помеченный с этим коммитом"
 
-#: gitk:2631
+#: gitk:2654
 msgid "Revert this commit"
-msgstr "Ð\92озвÑ\80аÑ\82 этого коммита"
+msgstr "Ð\9eбÑ\80аÑ\82иÑ\82Ñ\8c Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ\8f этого коммита"
 
-#: gitk:2647
+#: gitk:2670
 msgid "Check out this branch"
 msgstr "Перейти на эту ветку"
 
-#: gitk:2648
+#: gitk:2671
+msgid "Rename this branch"
+msgstr "Переименовать эту ветку"
+
+#: gitk:2672
 msgid "Remove this branch"
 msgstr "Удалить эту ветку"
 
-#: gitk:2649
+#: gitk:2673
 msgid "Copy branch name"
 msgstr "Копировать имя ветки"
 
-#: gitk:2656
+#: gitk:2680
 msgid "Highlight this too"
 msgstr "Подсветить этот тоже"
 
-#: gitk:2657
+#: gitk:2681
 msgid "Highlight this only"
 msgstr "Подсветить только этот"
 
-#: gitk:2658
+#: gitk:2682
 msgid "External diff"
 msgstr "Программа сравнения"
 
-#: gitk:2659
+#: gitk:2683
 msgid "Blame parent commit"
 msgstr "Авторы изменений родительского коммита"
 
-#: gitk:2660
+#: gitk:2684
 msgid "Copy path"
 msgstr "Копировать путь"
 
-#: gitk:2667
+#: gitk:2691
 msgid "Show origin of this line"
 msgstr "Показать источник этой строки"
 
-#: gitk:2668
+#: gitk:2692
 msgid "Run git gui blame on this line"
 msgstr "Запустить git gui blame для этой строки"
 
-#: gitk:3014
+#: gitk:3036
+msgid "About gitk"
+msgstr "О gitk"
+
+#: gitk:3038
 msgid ""
 "\n"
 "Gitk - a commit viewer for git\n"
 "\n"
-"Copyright  2005-2014 Paul Mackerras\n"
+"Copyright © 2005-2016 Paul Mackerras\n"
 "\n"
 "Use and redistribute under the terms of the GNU General Public License"
-msgstr "\nGitk - программа просмотра истории репозиториев git\n\n©  2005-2014 Paul Mackerras\n\nИспользование и распространение согласно условиям GNU General Public License"
+msgstr "\nGitk — программа просмотра истории репозиториев git\n\n© 2005-2016 Paul Mackerras\n\nИспользование и распространение согласно условиям GNU General Public License"
 
-#: gitk:3022 gitk:3089 gitk:9857
+#: gitk:3046 gitk:3113 gitk:10004
 msgid "Close"
 msgstr "Закрыть"
 
-#: gitk:3043
+#: gitk:3067
 msgid "Gitk key bindings"
 msgstr "Назначения клавиатуры в Gitk"
 
-#: gitk:3046
+#: gitk:3070
 msgid "Gitk key bindings:"
 msgstr "Назначения клавиатуры в Gitk:"
 
-#: gitk:3048
+#: gitk:3072
 #, tcl-format
 msgid "<%s-Q>\t\tQuit"
 msgstr "<%s-Q>\t\tЗавершить"
 
-#: gitk:3049
+#: gitk:3073
 #, tcl-format
 msgid "<%s-W>\t\tClose window"
 msgstr "<%s-W>\t\tЗакрыть окно"
 
-#: gitk:3050
+#: gitk:3074
 msgid "<Home>\t\tMove to first commit"
 msgstr "<Home>\t\tПерейти к первому коммиту"
 
-#: gitk:3051
+#: gitk:3075
 msgid "<End>\t\tMove to last commit"
 msgstr "<End>\t\tПерейти к последнему коммиту"
 
-#: gitk:3052
+#: gitk:3076
 msgid "<Up>, p, k\tMove up one commit"
 msgstr "<Up>, p, k\tПерейти на один коммит вверх"
 
-#: gitk:3053
+#: gitk:3077
 msgid "<Down>, n, j\tMove down one commit"
 msgstr "<Down>, n, j\tПерейти на один коммит вниз"
 
-#: gitk:3054
+#: gitk:3078
 msgid "<Left>, z, h\tGo back in history list"
 msgstr "<Left>, z, h\tПоказать ранее посещённое состояние"
 
-#: gitk:3055
+#: gitk:3079
 msgid "<Right>, x, l\tGo forward in history list"
 msgstr "<Right>, x, l\tПоказать следующий посещённый коммит"
 
-#: gitk:3056
+#: gitk:3080
 #, tcl-format
 msgid "<%s-n>\tGo to n-th parent of current commit in history list"
 msgstr "<%s-n>\tПерейти на n родителя от текущего коммита"
 
-#: gitk:3057
+#: gitk:3081
 msgid "<PageUp>\tMove up one page in commit list"
 msgstr "<PageUp>\tПерейти на страницу выше в списке коммитов"
 
-#: gitk:3058
+#: gitk:3082
 msgid "<PageDown>\tMove down one page in commit list"
 msgstr "<PageDown>\tПерейти на страницу ниже в списке коммитов"
 
-#: gitk:3059
+#: gitk:3083
 #, tcl-format
 msgid "<%s-Home>\tScroll to top of commit list"
 msgstr "<%s-Home>\tПерейти на начало списка коммитов"
 
-#: gitk:3060
+#: gitk:3084
 #, tcl-format
 msgid "<%s-End>\tScroll to bottom of commit list"
 msgstr "<%s-End>\tПерейти на конец списка коммитов"
 
-#: gitk:3061
+#: gitk:3085
 #, tcl-format
 msgid "<%s-Up>\tScroll commit list up one line"
 msgstr "<%s-Up>\tПровернуть список коммитов вверх"
 
-#: gitk:3062
+#: gitk:3086
 #, tcl-format
 msgid "<%s-Down>\tScroll commit list down one line"
 msgstr "<%s-Down>\tПровернуть список коммитов вниз"
 
-#: gitk:3063
+#: gitk:3087
 #, tcl-format
 msgid "<%s-PageUp>\tScroll commit list up one page"
 msgstr "<%s-PageUp>\tПровернуть список коммитов на страницу вверх"
 
-#: gitk:3064
+#: gitk:3088
 #, tcl-format
 msgid "<%s-PageDown>\tScroll commit list down one page"
 msgstr "<%s-PageDown>\tПровернуть список коммитов на страницу вниз"
 
-#: gitk:3065
+#: gitk:3089
 msgid "<Shift-Up>\tFind backwards (upwards, later commits)"
 msgstr "<Shift-Up>\tПоиск в обратном порядке (вверх, среди новых коммитов)"
 
-#: gitk:3066
+#: gitk:3090
 msgid "<Shift-Down>\tFind forwards (downwards, earlier commits)"
 msgstr "<Shift-Down>\tПоиск (вниз, среди старых коммитов)"
 
-#: gitk:3067
+#: gitk:3091
 msgid "<Delete>, b\tScroll diff view up one page"
 msgstr "<Delete>, b\tПрокрутить список изменений на страницу выше"
 
-#: gitk:3068
+#: gitk:3092
 msgid "<Backspace>\tScroll diff view up one page"
 msgstr "<Backspace>\tПрокрутить список изменений на страницу выше"
 
-#: gitk:3069
+#: gitk:3093
 msgid "<Space>\t\tScroll diff view down one page"
 msgstr "<Leertaste>\t\tПрокрутить список изменений на страницу ниже"
 
-#: gitk:3070
+#: gitk:3094
 msgid "u\t\tScroll diff view up 18 lines"
 msgstr "u\t\tПрокрутить список изменений на 18 строк вверх"
 
-#: gitk:3071
+#: gitk:3095
 msgid "d\t\tScroll diff view down 18 lines"
 msgstr "d\t\tПрокрутить список изменений на 18 строк вниз"
 
-#: gitk:3072
+#: gitk:3096
 #, tcl-format
 msgid "<%s-F>\t\tFind"
 msgstr "<%s-F>\t\tПоиск"
 
-#: gitk:3073
+#: gitk:3097
 #, tcl-format
 msgid "<%s-G>\t\tMove to next find hit"
 msgstr "<%s-G>\t\tПерейти к следующему найденному коммиту"
 
-#: gitk:3074
+#: gitk:3098
 msgid "<Return>\tMove to next find hit"
 msgstr "<Return>\tПерейти к следующему найденному коммиту"
 
-#: gitk:3075
+#: gitk:3099
 msgid "g\t\tGo to commit"
 msgstr "g\t\tПерейти на коммит"
 
-#: gitk:3076
+#: gitk:3100
 msgid "/\t\tFocus the search box"
 msgstr "/\t\tПерейти к полю поиска"
 
-#: gitk:3077
+#: gitk:3101
 msgid "?\t\tMove to previous find hit"
 msgstr "?\t\tПерейти к предыдущему найденному коммиту"
 
-#: gitk:3078
+#: gitk:3102
 msgid "f\t\tScroll diff view to next file"
 msgstr "f\t\tПрокрутить список изменений к следующему файлу"
 
-#: gitk:3079
+#: gitk:3103
 #, tcl-format
 msgid "<%s-S>\t\tSearch for next hit in diff view"
 msgstr "<%s-S>\t\tПродолжить поиск в списке изменений"
 
-#: gitk:3080
+#: gitk:3104
 #, tcl-format
 msgid "<%s-R>\t\tSearch for previous hit in diff view"
 msgstr "<%s-R>\t\tПерейти к предыдущему найденному тексту в списке изменений"
 
-#: gitk:3081
+#: gitk:3105
 #, tcl-format
 msgid "<%s-KP+>\tIncrease font size"
 msgstr "<%s-KP+>\tУвеличить размер шрифта"
 
-#: gitk:3082
+#: gitk:3106
 #, tcl-format
 msgid "<%s-plus>\tIncrease font size"
 msgstr "<%s-plus>\tУвеличить размер шрифта"
 
-#: gitk:3083
+#: gitk:3107
 #, tcl-format
 msgid "<%s-KP->\tDecrease font size"
 msgstr "<%s-KP->\tУменьшить размер шрифта"
 
-#: gitk:3084
+#: gitk:3108
 #, tcl-format
 msgid "<%s-minus>\tDecrease font size"
 msgstr "<%s-minus>\tУменьшить размер шрифта"
 
-#: gitk:3085
+#: gitk:3109
 msgid "<F5>\t\tUpdate"
 msgstr "<F5>\t\tОбновить"
 
-#: gitk:3550 gitk:3559
+#: gitk:3574 gitk:3583
 #, tcl-format
 msgid "Error creating temporary directory %s:"
 msgstr "Ошибка создания временного каталога %s:"
 
-#: gitk:3572
+#: gitk:3596
 #, tcl-format
 msgid "Error getting \"%s\" from %s:"
 msgstr "Ошибка получения «%s» из %s:"
 
-#: gitk:3635
+#: gitk:3659
 msgid "command failed:"
 msgstr "ошибка выполнения команды:"
 
-#: gitk:3784
+#: gitk:3808
 msgid "No such commit"
 msgstr "Коммит не найден"
 
-#: gitk:3798
+#: gitk:3822
 msgid "git gui blame: command failed:"
 msgstr "git gui blame: ошибка выполнения команды:"
 
-#: gitk:3829
+#: gitk:3853
 #, tcl-format
 msgid "Couldn't read merge head: %s"
 msgstr "Ошибка чтения MERGE_HEAD: %s"
 
-#: gitk:3837
+#: gitk:3861
 #, tcl-format
 msgid "Error reading index: %s"
 msgstr "Ошибка чтения индекса: %s"
 
-#: gitk:3862
+#: gitk:3886
 #, tcl-format
 msgid "Couldn't start git blame: %s"
 msgstr "Ошибка запуска git blame: %s"
 
-#: gitk:3865 gitk:6754
+#: gitk:3889 gitk:6778
 msgid "Searching"
 msgstr "Поиск"
 
-#: gitk:3897
+#: gitk:3921
 #, tcl-format
 msgid "Error running git blame: %s"
 msgstr "Ошибка выполнения git blame: %s"
 
-#: gitk:3925
+#: gitk:3949
 #, tcl-format
 msgid "That line comes from commit %s,  which is not in this view"
 msgstr "Эта строка принадлежит коммиту %s, который не показан в этом представлении"
 
-#: gitk:3939
+#: gitk:3963
 msgid "External diff viewer failed:"
 msgstr "Ошибка выполнения программы сравнения:"
 
-#: gitk:4070
+#: gitk:4067
+msgid "All files"
+msgstr "Все файлы"
+
+#: gitk:4091
+msgid "View"
+msgstr "Представление"
+
+#: gitk:4094
 msgid "Gitk view definition"
 msgstr "Gitk определение представлений"
 
-#: gitk:4074
+#: gitk:4098
 msgid "Remember this view"
 msgstr "Запомнить представление"
 
-#: gitk:4075
+#: gitk:4099
 msgid "References (space separated list):"
 msgstr "Ссылки (разделённые пробелом):"
 
-#: gitk:4076
+#: gitk:4100
 msgid "Branches & tags:"
 msgstr "Ветки и метки"
 
-#: gitk:4077
+#: gitk:4101
 msgid "All refs"
 msgstr "Все ссылки"
 
-#: gitk:4078
+#: gitk:4102
 msgid "All (local) branches"
 msgstr "Все (локальные) ветки"
 
-#: gitk:4079
+#: gitk:4103
 msgid "All tags"
 msgstr "Все метки"
 
-#: gitk:4080
+#: gitk:4104
 msgid "All remote-tracking branches"
 msgstr "Все внешние отслеживаемые ветки"
 
-#: gitk:4081
+#: gitk:4105
 msgid "Commit Info (regular expressions):"
 msgstr "Информация о коммите (регулярные выражения):"
 
-#: gitk:4082
+#: gitk:4106
 msgid "Author:"
 msgstr "Автор:"
 
-#: gitk:4083
+#: gitk:4107
 msgid "Committer:"
 msgstr "Коммитер:"
 
-#: gitk:4084
+#: gitk:4108
 msgid "Commit Message:"
 msgstr "Сообщение коммита:"
 
-#: gitk:4085
+#: gitk:4109
 msgid "Matches all Commit Info criteria"
 msgstr "Совпадает со всеми условиями информации о коммите"
 
-#: gitk:4086
+#: gitk:4110
 msgid "Matches no Commit Info criteria"
 msgstr "Не совпадает с условиями информации о коммите"
 
-#: gitk:4087
+#: gitk:4111
 msgid "Changes to Files:"
 msgstr "Изменения файлов:"
 
-#: gitk:4088
+#: gitk:4112
 msgid "Fixed String"
 msgstr "Обычная строка"
 
-#: gitk:4089
+#: gitk:4113
 msgid "Regular Expression"
 msgstr "Регулярное выражение:"
 
-#: gitk:4090
+#: gitk:4114
 msgid "Search string:"
 msgstr "Строка для поиска:"
 
-#: gitk:4091
+#: gitk:4115
 msgid ""
 "Commit Dates (\"2 weeks ago\", \"2009-03-17 15:27:38\", \"March 17, 2009 "
 "15:27:38\"):"
 msgstr "Даты коммита («2 недели назад», «2009-03-17 15:27:38», «17 марта 2009 15:27:38»):"
 
-#: gitk:4092
+#: gitk:4116
 msgid "Since:"
 msgstr "С даты:"
 
-#: gitk:4093
+#: gitk:4117
 msgid "Until:"
 msgstr "По дату:"
 
-#: gitk:4094
+#: gitk:4118
 msgid "Limit and/or skip a number of revisions (positive integer):"
 msgstr "Ограничить и/или пропустить количество редакций (положительное число):"
 
-#: gitk:4095
+#: gitk:4119
 msgid "Number to show:"
 msgstr "Показать количество:"
 
-#: gitk:4096
+#: gitk:4120
 msgid "Number to skip:"
 msgstr "Пропустить количество:"
 
-#: gitk:4097
+#: gitk:4121
 msgid "Miscellaneous options:"
 msgstr "Различные опции:"
 
-#: gitk:4098
+#: gitk:4122
 msgid "Strictly sort by date"
 msgstr "Строгая сортировка по дате"
 
-#: gitk:4099
+#: gitk:4123
 msgid "Mark branch sides"
 msgstr "Отметить стороны веток"
 
-#: gitk:4100
+#: gitk:4124
 msgid "Limit to first parent"
 msgstr "Ограничить первым предком"
 
-#: gitk:4101
+#: gitk:4125
 msgid "Simple history"
 msgstr "Упрощенная история"
 
-#: gitk:4102
+#: gitk:4126
 msgid "Additional arguments to git log:"
 msgstr "Дополнительные аргументы для git log:"
 
-#: gitk:4103
+#: gitk:4127
 msgid "Enter files and directories to include, one per line:"
 msgstr "Файлы и каталоги для ограничения истории, по одному на строку:"
 
-#: gitk:4104
+#: gitk:4128
 msgid "Command to generate more commits to include:"
 msgstr "Дополнительная команда для списка коммитов:"
 
-#: gitk:4228
+#: gitk:4252
 msgid "Gitk: edit view"
 msgstr "Gitk: изменить представление"
 
-#: gitk:4236
+#: gitk:4260
 msgid "-- criteria for selecting revisions"
 msgstr "— критерий поиска редакций"
 
-#: gitk:4241
+#: gitk:4265
 msgid "View Name"
 msgstr "Имя представления"
 
-#: gitk:4316
+#: gitk:4340
 msgid "Apply (F5)"
 msgstr "Применить (F5)"
 
-#: gitk:4354
+#: gitk:4378
 msgid "Error in commit selection arguments:"
 msgstr "Ошибка в параметрах выбора коммитов:"
 
-#: gitk:4409 gitk:4462 gitk:4924 gitk:4938 gitk:6208 gitk:12373 gitk:12374
+#: gitk:4433 gitk:4486 gitk:4948 gitk:4962 gitk:6232 gitk:12524 gitk:12525
 msgid "None"
 msgstr "Ни одного"
 
-#: gitk:5021 gitk:5026
+#: gitk:5045 gitk:5050
 msgid "Descendant"
 msgstr "Порождённое"
 
-#: gitk:5022
+#: gitk:5046
 msgid "Not descendant"
 msgstr "Не порождённое"
 
-#: gitk:5029 gitk:5034
+#: gitk:5053 gitk:5058
 msgid "Ancestor"
 msgstr "Предок"
 
-#: gitk:5030
+#: gitk:5054
 msgid "Not ancestor"
 msgstr "Не предок"
 
-#: gitk:5324
+#: gitk:5348
 msgid "Local changes checked in to index but not committed"
 msgstr "Проиндексированные изменения"
 
-#: gitk:5360
+#: gitk:5384
 msgid "Local uncommitted changes, not checked in to index"
 msgstr "Непроиндексированные изменения"
 
-#: gitk:7134
+#: gitk:7158
 msgid "and many more"
 msgstr "и многое другое"
 
-#: gitk:7137
+#: gitk:7161
 msgid "many"
 msgstr "много"
 
-#: gitk:7328
+#: gitk:7352
 msgid "Tags:"
 msgstr "Метки:"
 
-#: gitk:7345 gitk:7351 gitk:8825
+#: gitk:7369 gitk:7375 gitk:8854
 msgid "Parent"
 msgstr "Предок"
 
-#: gitk:7356
+#: gitk:7380
 msgid "Child"
 msgstr "Потомок"
 
-#: gitk:7365
+#: gitk:7389
 msgid "Branch"
 msgstr "Ветка"
 
-#: gitk:7368
+#: gitk:7392
 msgid "Follows"
 msgstr "Следует за"
 
-#: gitk:7371
+#: gitk:7395
 msgid "Precedes"
 msgstr "Предшествует"
 
-#: gitk:7966
+#: gitk:7990
 #, tcl-format
 msgid "Error getting diffs: %s"
 msgstr "Ошибка получения изменений: %s"
 
-#: gitk:8650
+#: gitk:8679
 msgid "Goto:"
 msgstr "Перейти к:"
 
-#: gitk:8671
+#: gitk:8700
 #, tcl-format
 msgid "Short SHA1 id %s is ambiguous"
 msgstr "Сокращённый SHA1 идентификатор %s неоднозначен"
 
-#: gitk:8678
+#: gitk:8707
 #, tcl-format
 msgid "Revision %s is not known"
 msgstr "Редакция %s не найдена"
 
-#: gitk:8688
+#: gitk:8717
 #, tcl-format
 msgid "SHA1 id %s is not known"
 msgstr "SHA1 идентификатор %s не найден"
 
-#: gitk:8690
+#: gitk:8719
 #, tcl-format
 msgid "Revision %s is not in the current view"
 msgstr "Редакция %s не найдена в текущем представлении"
 
-#: gitk:8832 gitk:8847
+#: gitk:8861 gitk:8876
 msgid "Date"
 msgstr "Дата"
 
-#: gitk:8835
+#: gitk:8864
 msgid "Children"
 msgstr "Потомки"
 
-#: gitk:8898
+#: gitk:8927
 #, tcl-format
 msgid "Reset %s branch to here"
 msgstr "Сбросить ветку %s на этот коммит"
 
-#: gitk:8900
+#: gitk:8929
 msgid "Detached head: can't reset"
 msgstr "Коммит не принадлежит ни одной ветке, сбросить невозможно"
 
-#: gitk:9005 gitk:9011
+#: gitk:9034 gitk:9040
 msgid "Skipping merge commit "
 msgstr "Пропускаю коммит-слияние"
 
-#: gitk:9020 gitk:9025
+#: gitk:9049 gitk:9054
 msgid "Error getting patch ID for "
 msgstr "Не удалось получить идентификатор патча для "
 
-#: gitk:9021 gitk:9026
+#: gitk:9050 gitk:9055
 msgid " - stopping\n"
 msgstr " — останов\n"
 
-#: gitk:9031 gitk:9034 gitk:9042 gitk:9056 gitk:9065
+#: gitk:9060 gitk:9063 gitk:9071 gitk:9085 gitk:9094
 msgid "Commit "
 msgstr "Коммит"
 
-#: gitk:9035
+#: gitk:9064
 msgid ""
 " is the same patch as\n"
 "       "
 msgstr " такой же патч, как и\n       "
 
-#: gitk:9043
+#: gitk:9072
 msgid ""
 " differs from\n"
 "       "
 msgstr " отличается от\n       "
 
-#: gitk:9045
+#: gitk:9074
 msgid ""
 "Diff of commits:\n"
 "\n"
 msgstr "Различия коммитов:\n\n"
 
-#: gitk:9057 gitk:9066
+#: gitk:9086 gitk:9095
 #, tcl-format
 msgid " has %s children - stopping\n"
 msgstr " является %s потомком — останов\n"
 
-#: gitk:9085
+#: gitk:9114
 #, tcl-format
 msgid "Error writing commit to file: %s"
 msgstr "Произошла ошибка при записи коммита в файл: %s"
 
-#: gitk:9091
+#: gitk:9120
 #, tcl-format
 msgid "Error diffing commits: %s"
 msgstr "Произошла ошибка при выводе различий коммитов: %s"
 
-#: gitk:9137
+#: gitk:9166
 msgid "Top"
 msgstr "Верх"
 
-#: gitk:9138
+#: gitk:9167
 msgid "From"
 msgstr "От"
 
-#: gitk:9143
+#: gitk:9172
 msgid "To"
 msgstr "До"
 
-#: gitk:9167
+#: gitk:9196
 msgid "Generate patch"
 msgstr "Создать патч"
 
-#: gitk:9169
+#: gitk:9198
 msgid "From:"
 msgstr "От:"
 
-#: gitk:9178
+#: gitk:9207
 msgid "To:"
 msgstr "До:"
 
-#: gitk:9187
+#: gitk:9216
 msgid "Reverse"
 msgstr "В обратном порядке"
 
-#: gitk:9189 gitk:9385
+#: gitk:9218 gitk:9428
 msgid "Output file:"
 msgstr "Файл для сохранения:"
 
-#: gitk:9195
+#: gitk:9224
 msgid "Generate"
 msgstr "Создать"
 
-#: gitk:9233
+#: gitk:9262
 msgid "Error creating patch:"
 msgstr "Ошибка создания патча:"
 
-#: gitk:9256 gitk:9373 gitk:9430
+#: gitk:9285 gitk:9416 gitk:9504
 msgid "ID:"
 msgstr "ID:"
 
-#: gitk:9265
+#: gitk:9294
 msgid "Tag name:"
 msgstr "Имя метки:"
 
-#: gitk:9268
+#: gitk:9297
 msgid "Tag message is optional"
 msgstr "Описание метки указывать не обязательно"
 
-#: gitk:9270
+#: gitk:9299
 msgid "Tag message:"
 msgstr "Описание метки:"
 
-#: gitk:9274 gitk:9439
+#: gitk:9303 gitk:9474
 msgid "Create"
 msgstr "Создать"
 
-#: gitk:9292
+#: gitk:9321
 msgid "No tag name specified"
 msgstr "Не задано имя метки"
 
-#: gitk:9296
+#: gitk:9325
 #, tcl-format
 msgid "Tag \"%s\" already exists"
 msgstr "Метка «%s» уже существует"
 
-#: gitk:9306
+#: gitk:9335
 msgid "Error creating tag:"
 msgstr "Ошибка создания метки:"
 
-#: gitk:9382
+#: gitk:9425
 msgid "Command:"
 msgstr "Команда:"
 
-#: gitk:9390
+#: gitk:9433
 msgid "Write"
 msgstr "Запись"
 
-#: gitk:9408
+#: gitk:9451
 msgid "Error writing commit:"
 msgstr "Произошла ошибка при записи коммита:"
 
-#: gitk:9435
+#: gitk:9473
+msgid "Create branch"
+msgstr "Создать ветку"
+
+#: gitk:9489
+#, tcl-format
+msgid "Rename branch %s"
+msgstr "Переименовать ветку %s"
+
+#: gitk:9490
+msgid "Rename"
+msgstr "Переименовать"
+
+#: gitk:9514
 msgid "Name:"
 msgstr "Имя:"
 
-#: gitk:9458
+#: gitk:9538
 msgid "Please specify a name for the new branch"
 msgstr "Укажите имя для новой ветки"
 
-#: gitk:9463
+#: gitk:9543
 #, tcl-format
 msgid "Branch '%s' already exists. Overwrite?"
 msgstr "Ветка «%s» уже существует. Переписать?"
 
-#: gitk:9530
+#: gitk:9587
+msgid "Please specify a new name for the branch"
+msgstr "Укажите имя для новой ветки"
+
+#: gitk:9650
 #, tcl-format
 msgid "Commit %s is already included in branch %s -- really re-apply it?"
 msgstr "Коммит %s уже включён в ветку %s. Продолжить операцию?"
 
-#: gitk:9535
+#: gitk:9655
 msgid "Cherry-picking"
-msgstr "Ð\9aопиÑ\80ование Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ð¹"
+msgstr "Ð\9aопиÑ\80ование ÐºÐ¾Ð¼Ð¼Ð¸Ñ\82а"
 
-#: gitk:9544
+#: gitk:9664
 #, tcl-format
 msgid ""
 "Cherry-pick failed because of local changes to file '%s'.\n"
 "Please commit, reset or stash your changes and try again."
-msgstr "Ð\9eÑ\82боÑ\80 Ð»Ñ\83Ñ\87Ñ\88его Ð½ÐµÐ²Ð¾Ð·Ð¼Ð¾Ð¶ÐµÐ½ Ð¸Ð·-за Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ð¹ Ð² Ñ\84айле Â«%s».\nÐ\97акомитьте, сбросьте или спрячьте изменения и повторите операцию."
+msgstr "Ð\9aопиÑ\80ование ÐºÐ¾Ð¼Ð¼Ð¸Ñ\82а Ð½ÐµÐ²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ð¾ Ð¸Ð·-за Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ð¹ Ð² Ñ\84айле Â«%s».\nÐ\97акоммитьте, сбросьте или спрячьте изменения и повторите операцию."
 
-#: gitk:9550
+#: gitk:9670
 msgid ""
 "Cherry-pick failed because of merge conflict.\n"
 "Do you wish to run git citool to resolve it?"
 msgstr "Копирование изменений невозможно из-за незавершённой операции слияния.\nЗапустить git citool для завершения этой операции?"
 
-#: gitk:9566 gitk:9624
+#: gitk:9686 gitk:9744
 msgid "No changes committed"
 msgstr "Изменения не закоммичены"
 
-#: gitk:9593
+#: gitk:9713
 #, tcl-format
 msgid "Commit %s is not included in branch %s -- really revert it?"
 msgstr "Коммит %s не включён в ветку %s. Продолжить операцию?"
 
-#: gitk:9598
+#: gitk:9718
 msgid "Reverting"
-msgstr "Ð\92озвÑ\80аÑ\82 изменений"
+msgstr "Ð\9eбÑ\80аÑ\89ение изменений"
 
-#: gitk:9606
+#: gitk:9726
 #, 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 "Ð\92озвÑ\80аÑ\82 Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ð¹ ÐºÐ¾Ð¼Ð¼Ð¸Ñ\82а Ð½Ðµ Ñ\83далÑ\81Ñ\8f Ð¸Ð·-за Ð»Ð¾ÐºÐ°Ð»Ñ\8cнÑ\8bÑ\85 Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ð¹ Ð² Ñ\83казаннÑ\8bÑ\85 Ñ\84айлаÑ\85: %s\nÐ\97акоммиÑ\82Ñ\8cÑ\82е, Ñ\81бÑ\80оÑ\81Ñ\8cÑ\82е Ð¸Ð»Ð¸ Ñ\81пÑ\80Ñ\8fÑ\87Ñ\8cÑ\82е Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ\8f Ð¸ Ð¿Ð¾Ð²Ñ\82оÑ\80иÑ\82е Ð¾Ð¿ÐµÑ\80аÑ\86иÑ\8e."
 
-#: gitk:9610
+#: gitk:9730
 msgid ""
 "Revert failed because of merge conflict.\n"
 " Do you wish to run git citool to resolve it?"
 msgstr "Возврат изменений невозможен из-за незавершённой операции слияния.\nЗапустить git citool для завершения этой операции?"
 
-#: gitk:9653
+#: gitk:9773
 msgid "Confirm reset"
 msgstr "Подтвердите операцию перехода"
 
-#: gitk:9655
+#: gitk:9775
 #, tcl-format
 msgid "Reset branch %s to %s?"
 msgstr "Сбросить ветку %s на коммит %s?"
 
-#: gitk:9657
+#: gitk:9777
 msgid "Reset type:"
 msgstr "Тип операции перехода:"
 
-#: gitk:9660
+#: gitk:9780
 msgid "Soft: Leave working tree and index untouched"
 msgstr "Лёгкий: оставить рабочий каталог и индекс неизменными"
 
-#: gitk:9663
+#: gitk:9783
 msgid "Mixed: Leave working tree untouched, reset index"
 msgstr "Смешанный: оставить рабочий каталог неизменным, установить индекс"
 
-#: gitk:9666
+#: gitk:9786
 msgid ""
 "Hard: Reset working tree and index\n"
 "(discard ALL local changes)"
 msgstr "Жесткий: переписать индекс и рабочий каталог\n(все изменения в рабочем каталоге будут потеряны)"
 
-#: gitk:9683
+#: gitk:9803
 msgid "Resetting"
 msgstr "Сброс"
 
-#: gitk:9743
+#: gitk:9876
+#, tcl-format
+msgid "A local branch named %s exists already"
+msgstr "Локальная ветка с именем %s уже существует"
+
+#: gitk:9884
 msgid "Checking out"
 msgstr "Переход"
 
-#: gitk:9796
+#: gitk:9943
 msgid "Cannot delete the currently checked-out branch"
 msgstr "Активная ветка не может быть удалена"
 
-#: gitk:9802
+#: gitk:9949
 #, tcl-format
 msgid ""
 "The commits on branch %s aren't on any other branch.\n"
 "Really delete branch %s?"
 msgstr "Коммиты из ветки %s не принадлежат больше никакой другой ветке.\nДействительно удалить ветку %s?"
 
-#: gitk:9833
+#: gitk:9980
 #, tcl-format
 msgid "Tags and heads: %s"
 msgstr "Метки и ветки: %s"
 
-#: gitk:9850
+#: gitk:9997
 msgid "Filter"
 msgstr "Фильтровать"
 
-#: gitk:10146
+#: gitk:10293
 msgid ""
 "Error reading commit topology information; branch and preceding/following "
 "tag information will be incomplete."
 msgstr "Ошибка чтения истории проекта; информация о ветках и коммитах вокруг меток (до/после) может быть неполной."
 
-#: gitk:11123
+#: gitk:11270
 msgid "Tag"
 msgstr "Метка"
 
-#: gitk:11127
+#: gitk:11274
 msgid "Id"
 msgstr "Id"
 
-#: gitk:11210
+#: gitk:11357
 msgid "Gitk font chooser"
 msgstr "Шрифт Gitk"
 
-#: gitk:11227
+#: gitk:11374
 msgid "B"
 msgstr "Ж"
 
-#: gitk:11230
+#: gitk:11377
 msgid "I"
 msgstr "К"
 
-#: gitk:11348
+#: gitk:11495
 msgid "Commit list display options"
 msgstr "Параметры показа списка коммитов"
 
-#: gitk:11351
+#: gitk:11498
 msgid "Maximum graph width (lines)"
 msgstr "Макс. ширина графа (строк)"
 
-#: gitk:11355
+#: gitk:11502
 #, no-tcl-format
 msgid "Maximum graph width (% of pane)"
 msgstr "Макс. ширина графа (% ширины панели)"
 
-#: gitk:11358
+#: gitk:11505
 msgid "Show local changes"
 msgstr "Показывать изменения в рабочем каталоге"
 
-#: gitk:11361
+#: gitk:11508
 msgid "Auto-select SHA1 (length)"
 msgstr "Автоматически выделить SHA1 (длинна)"
 
-#: gitk:11365
+#: gitk:11512
 msgid "Hide remote refs"
 msgstr "Скрыть внешние ссылки"
 
-#: gitk:11369
+#: gitk:11516
 msgid "Diff display options"
 msgstr "Параметры показа изменений"
 
-#: gitk:11371
+#: gitk:11518
 msgid "Tab spacing"
 msgstr "Ширина табуляции"
 
-#: gitk:11374
+#: gitk:11521
 msgid "Display nearby tags/heads"
 msgstr "Показывать близкие метки/ветки"
 
-#: gitk:11377
+#: gitk:11524
 msgid "Maximum # tags/heads to show"
 msgstr "Показывать максимальное количество меток/веток"
 
-#: gitk:11380
+#: gitk:11527
 msgid "Limit diffs to listed paths"
 msgstr "Ограничить показ изменений выбранными файлами"
 
-#: gitk:11383
+#: gitk:11530
 msgid "Support per-file encodings"
 msgstr "Поддержка кодировок в отдельных файлах"
 
-#: gitk:11389 gitk:11536
+#: gitk:11536 gitk:11683
 msgid "External diff tool"
 msgstr "Программа для показа изменений"
 
-#: gitk:11390
+#: gitk:11537
 msgid "Choose..."
 msgstr "Выберите..."
 
-#: gitk:11395
+#: gitk:11542
 msgid "General options"
 msgstr "Общие опции"
 
-#: gitk:11398
+#: gitk:11545
 msgid "Use themed widgets"
 msgstr "Использовать стили виджетов"
 
-#: gitk:11400
+#: gitk:11547
 msgid "(change requires restart)"
 msgstr "(изменение потребует перезапуск)"
 
-#: gitk:11402
+#: gitk:11549
 msgid "(currently unavailable)"
 msgstr "(недоступно в данный момент)"
 
-#: gitk:11413
+#: gitk:11560
 msgid "Colors: press to choose"
 msgstr "Цвета: нажмите для выбора"
 
-#: gitk:11416
+#: gitk:11563
 msgid "Interface"
 msgstr "Интерфейс"
 
-#: gitk:11417
+#: gitk:11564
 msgid "interface"
 msgstr "интерфейс"
 
-#: gitk:11420
+#: gitk:11567
 msgid "Background"
 msgstr "Фон"
 
-#: gitk:11421 gitk:11451
+#: gitk:11568 gitk:11598
 msgid "background"
 msgstr "фон"
 
-#: gitk:11424
+#: gitk:11571
 msgid "Foreground"
 msgstr "Передний план"
 
-#: gitk:11425
+#: gitk:11572
 msgid "foreground"
 msgstr "передний план"
 
-#: gitk:11428
+#: gitk:11575
 msgid "Diff: old lines"
 msgstr "Изменения: старый текст"
 
-#: gitk:11429
+#: gitk:11576
 msgid "diff old lines"
 msgstr "старый текст изменения"
 
-#: gitk:11433
+#: gitk:11580
 msgid "Diff: new lines"
 msgstr "Изменения: новый текст"
 
-#: gitk:11434
+#: gitk:11581
 msgid "diff new lines"
 msgstr "новый текст изменения"
 
-#: gitk:11438
+#: gitk:11585
 msgid "Diff: hunk header"
 msgstr "Изменения: заголовок блока"
 
-#: gitk:11440
+#: gitk:11587
 msgid "diff hunk header"
 msgstr "заголовок блока изменений"
 
-#: gitk:11444
+#: gitk:11591
 msgid "Marked line bg"
 msgstr "Фон выбранной строки"
 
-#: gitk:11446
+#: gitk:11593
 msgid "marked line background"
 msgstr "фон выбранной строки"
 
-#: gitk:11450
+#: gitk:11597
 msgid "Select bg"
 msgstr "Выберите фон"
 
-#: gitk:11459
+#: gitk:11606
 msgid "Fonts: press to choose"
 msgstr "Шрифт: нажмите для выбора"
 
-#: gitk:11461
+#: gitk:11608
 msgid "Main font"
 msgstr "Основной шрифт"
 
-#: gitk:11462
+#: gitk:11609
 msgid "Diff display font"
 msgstr "Шрифт показа изменений"
 
-#: gitk:11463
+#: gitk:11610
 msgid "User interface font"
 msgstr "Шрифт интерфейса"
 
-#: gitk:11485
+#: gitk:11632
 msgid "Gitk preferences"
 msgstr "Настройки Gitk"
 
-#: gitk:11494
+#: gitk:11641
 msgid "General"
 msgstr "Общие"
 
-#: gitk:11495
+#: gitk:11642
 msgid "Colors"
 msgstr "Цвета"
 
-#: gitk:11496
+#: gitk:11643
 msgid "Fonts"
 msgstr "Шрифты"
 
-#: gitk:11546
+#: gitk:11693
 #, tcl-format
 msgid "Gitk: choose color for %s"
 msgstr "Gitk: выберите цвет для %s"
 
-#: gitk:12059
+#: gitk:12206
 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."
 
-#: gitk:12269
+#: gitk:12416
 msgid "Cannot find a git repository here."
 msgstr "Git-репозитарий не найден в текущем каталоге."
 
-#: gitk:12316
+#: gitk:12463
 #, tcl-format
 msgid "Ambiguous argument '%s': both revision and filename"
 msgstr "Неоднозначный аргумент «%s»: существует как редакция и как имя файла"
 
-#: gitk:12328
+#: gitk:12475
 msgid "Bad arguments to gitk:"
 msgstr "Неправильные аргументы для gitk:"
index d9d4e87a44a5cddafe957f276248ceec3c3ebc40..2a06fe5bbcf826c4a3312b0cff1e1ff53cf69490 100644 (file)
@@ -374,14 +374,14 @@ msgid ""
 "\n"
 "Gitk - a commit viewer for git\n"
 "\n"
-"Copyright © 2005-2014 Paul Mackerras\n"
+"Copyright © 2005-2016 Paul Mackerras\n"
 "\n"
 "Use and redistribute under the terms of the GNU General Public License"
 msgstr ""
 "\n"
 "Gitk - en incheckningsvisare för git\n"
 "\n"
-"Copyright © 2005-2014 Paul Mackerras\n"
+"Copyright © 2005-2016 Paul Mackerras\n"
 "\n"
 "Använd och vidareförmedla enligt villkoren i GNU General Public License"
 
@@ -1385,21 +1385,6 @@ msgstr "Felaktiga argument till gitk:"
 #~ msgid "mc"
 #~ msgstr "mc"
 
-#~ msgid ""
-#~ "\n"
-#~ "Gitk - a commit viewer for git\n"
-#~ "\n"
-#~ "Copyright © 2005-2015 Paul Mackerras\n"
-#~ "\n"
-#~ "Use and redistribute under the terms of the GNU General Public License"
-#~ msgstr ""
-#~ "\n"
-#~ "Gitk - en incheckningsvisare för git\n"
-#~ "\n"
-#~ "Copyright © 2005-2015 Paul Mackerras\n"
-#~ "\n"
-#~ "Använd och vidareförmedla enligt villkoren i GNU General Public License"
-
 #~ msgid "next"
 #~ msgstr "nästa"
 
index 8966812368a626e8da9f42012b69f233b4181642..59674986604891d9901e87c163e61dd39db14184 100644 (file)
@@ -363,14 +363,14 @@ msgid ""
 "\n"
 "Gitk - a commit viewer for git\n"
 "\n"
-"Copyright © 2005-2014 Paul Mackerras\n"
+"Copyright © 2005-2016 Paul Mackerras\n"
 "\n"
 "Use and redistribute under the terms of the GNU General Public License"
 msgstr ""
 "\n"
 "Gitk - ứng dụng để xem các lần chuyển giao dành cho git\n"
 "\n"
-"Bản quyền © 2005-2014 Paul Mackerras\n"
+"Bản quyền © 2005-2016 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"
 
diff --git a/grep.c b/grep.c
index 1194d35b5d06d7960d4464884952e755914e2519..0dbdc1d007893042dc1005478f3023eed4ecfc12 100644 (file)
--- a/grep.c
+++ b/grep.c
@@ -1735,12 +1735,23 @@ void grep_source_init(struct grep_source *gs, enum grep_source_type type,
        case GREP_SOURCE_FILE:
                gs->identifier = xstrdup(identifier);
                break;
+       case GREP_SOURCE_SUBMODULE:
+               if (!identifier) {
+                       gs->identifier = NULL;
+                       break;
+               }
+               /*
+                * FALL THROUGH
+                * If the identifier is non-NULL (in the submodule case) it
+                * will be a SHA1 that needs to be copied.
+                */
        case GREP_SOURCE_SHA1:
                gs->identifier = xmalloc(20);
                hashcpy(gs->identifier, identifier);
                break;
        case GREP_SOURCE_BUF:
                gs->identifier = NULL;
+               break;
        }
 }
 
@@ -1760,6 +1771,7 @@ void grep_source_clear_data(struct grep_source *gs)
        switch (gs->type) {
        case GREP_SOURCE_FILE:
        case GREP_SOURCE_SHA1:
+       case GREP_SOURCE_SUBMODULE:
                free(gs->buf);
                gs->buf = NULL;
                gs->size = 0;
@@ -1831,8 +1843,10 @@ static int grep_source_load(struct grep_source *gs)
                return grep_source_load_sha1(gs);
        case GREP_SOURCE_BUF:
                return gs->buf ? 0 : -1;
+       case GREP_SOURCE_SUBMODULE:
+               break;
        }
-       die("BUG: invalid grep_source type");
+       die("BUG: invalid grep_source type to load");
 }
 
 void grep_source_load_driver(struct grep_source *gs)
diff --git a/grep.h b/grep.h
index 5856a23e4620773cbda2e2944f93946c2701f923..267534ca24df532505854e1af31c53ed01bab24e 100644 (file)
--- a/grep.h
+++ b/grep.h
@@ -161,6 +161,7 @@ struct grep_source {
                GREP_SOURCE_SHA1,
                GREP_SOURCE_FILE,
                GREP_SOURCE_BUF,
+               GREP_SOURCE_SUBMODULE,
        } type;
        void *identifier;
 
index 22ca74a126e79995607e68a557ae8bd4c2ab02f7..7ababb315944d5536ec251b19937beef5def2538 100644 (file)
@@ -67,20 +67,19 @@ static struct pathspec_magic {
        char mnemonic; /* this cannot be ':'! */
        const char *name;
 } pathspec_magic[] = {
-       { PATHSPEC_FROMTOP, '/', "top" },
-       { PATHSPEC_LITERAL,   0, "literal" },
-       { PATHSPEC_GLOB,   '\0', "glob" },
-       { PATHSPEC_ICASE,  '\0', "icase" },
-       { PATHSPEC_EXCLUDE, '!', "exclude" },
+       { PATHSPEC_FROMTOP,  '/', "top" },
+       { PATHSPEC_LITERAL, '\0', "literal" },
+       { PATHSPEC_GLOB,    '\0', "glob" },
+       { PATHSPEC_ICASE,   '\0', "icase" },
+       { PATHSPEC_EXCLUDE,  '!', "exclude" },
 };
 
-static void prefix_short_magic(struct strbuf *sb, int prefixlen,
-                              unsigned short_magic)
+static void prefix_magic(struct strbuf *sb, int prefixlen, unsigned magic)
 {
        int i;
        strbuf_addstr(sb, ":(");
        for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++)
-               if (short_magic & pathspec_magic[i].bit) {
+               if (magic & pathspec_magic[i].bit) {
                        if (sb->buf[sb->len - 1] != '(')
                                strbuf_addch(sb, ',');
                        strbuf_addstr(sb, pathspec_magic[i].name);
@@ -88,54 +87,61 @@ static void prefix_short_magic(struct strbuf *sb, int prefixlen,
        strbuf_addf(sb, ",prefix:%d)", prefixlen);
 }
 
-/*
- * Take an element of a pathspec and check for magic signatures.
- * Append the result to the prefix. Return the magic bitmap.
- *
- * For now, we only parse the syntax and throw out anything other than
- * "top" magic.
- *
- * NEEDSWORK: This needs to be rewritten when we start migrating
- * get_pathspec() users to use the "struct pathspec" interface.  For
- * example, a pathspec element may be marked as case-insensitive, but
- * the prefix part must always match literally, and a single stupid
- * string cannot express such a case.
- */
-static unsigned prefix_pathspec(struct pathspec_item *item,
-                               unsigned *p_short_magic,
-                               const char **raw, unsigned flags,
-                               const char *prefix, int prefixlen,
-                               const char *elt)
+static inline int get_literal_global(void)
 {
-       static int literal_global = -1;
-       static int glob_global = -1;
-       static int noglob_global = -1;
-       static int icase_global = -1;
-       unsigned magic = 0, short_magic = 0, global_magic = 0;
-       const char *copyfrom = elt, *long_magic_end = NULL;
-       char *match;
-       int i, pathspec_prefix = -1;
+       static int literal = -1;
+
+       if (literal < 0)
+               literal = git_env_bool(GIT_LITERAL_PATHSPECS_ENVIRONMENT, 0);
+
+       return literal;
+}
+
+static inline int get_glob_global(void)
+{
+       static int glob = -1;
+
+       if (glob < 0)
+               glob = git_env_bool(GIT_GLOB_PATHSPECS_ENVIRONMENT, 0);
+
+       return glob;
+}
+
+static inline int get_noglob_global(void)
+{
+       static int noglob = -1;
+
+       if (noglob < 0)
+               noglob = git_env_bool(GIT_NOGLOB_PATHSPECS_ENVIRONMENT, 0);
+
+       return noglob;
+}
+
+static inline int get_icase_global(void)
+{
+       static int icase = -1;
+
+       if (icase < 0)
+               icase = git_env_bool(GIT_ICASE_PATHSPECS_ENVIRONMENT, 0);
+
+       return icase;
+}
+
+static int get_global_magic(int element_magic)
+{
+       int global_magic = 0;
 
-       if (literal_global < 0)
-               literal_global = git_env_bool(GIT_LITERAL_PATHSPECS_ENVIRONMENT, 0);
-       if (literal_global)
+       if (get_literal_global())
                global_magic |= PATHSPEC_LITERAL;
 
-       if (glob_global < 0)
-               glob_global = git_env_bool(GIT_GLOB_PATHSPECS_ENVIRONMENT, 0);
-       if (glob_global)
+       /* --glob-pathspec is overridden by :(literal) */
+       if (get_glob_global() && !(element_magic & PATHSPEC_LITERAL))
                global_magic |= PATHSPEC_GLOB;
 
-       if (noglob_global < 0)
-               noglob_global = git_env_bool(GIT_NOGLOB_PATHSPECS_ENVIRONMENT, 0);
-
-       if (glob_global && noglob_global)
+       if (get_glob_global() && get_noglob_global())
                die(_("global 'glob' and 'noglob' pathspec settings are incompatible"));
 
-
-       if (icase_global < 0)
-               icase_global = git_env_bool(GIT_ICASE_PATHSPECS_ENVIRONMENT, 0);
-       if (icase_global)
+       if (get_icase_global())
                global_magic |= PATHSPEC_ICASE;
 
        if ((global_magic & PATHSPEC_LITERAL) &&
@@ -143,84 +149,198 @@ static unsigned prefix_pathspec(struct pathspec_item *item,
                die(_("global 'literal' pathspec setting is incompatible "
                      "with all other global pathspec settings"));
 
-       if (flags & PATHSPEC_LITERAL_PATH)
-               global_magic = 0;
+       /* --noglob-pathspec adds :(literal) _unless_ :(glob) is specified */
+       if (get_noglob_global() && !(element_magic & PATHSPEC_GLOB))
+               global_magic |= PATHSPEC_LITERAL;
 
-       if (elt[0] != ':' || literal_global ||
-           (flags & PATHSPEC_LITERAL_PATH)) {
-               ; /* nothing to do */
-       } else if (elt[1] == '(') {
-               /* longhand */
-               const char *nextat;
-               for (copyfrom = elt + 2;
-                    *copyfrom && *copyfrom != ')';
-                    copyfrom = nextat) {
-                       size_t len = strcspn(copyfrom, ",)");
-                       if (copyfrom[len] == ',')
-                               nextat = copyfrom + len + 1;
-                       else
-                               /* handle ')' and '\0' */
-                               nextat = copyfrom + len;
-                       if (!len)
-                               continue;
-                       for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++) {
-                               if (strlen(pathspec_magic[i].name) == len &&
-                                   !strncmp(pathspec_magic[i].name, copyfrom, len)) {
-                                       magic |= pathspec_magic[i].bit;
-                                       break;
-                               }
-                               if (starts_with(copyfrom, "prefix:")) {
-                                       char *endptr;
-                                       pathspec_prefix = strtol(copyfrom + 7,
-                                                                &endptr, 10);
-                                       if (endptr - copyfrom != len)
-                                               die(_("invalid parameter for pathspec magic 'prefix'"));
-                                       /* "i" would be wrong, but it does not matter */
-                                       break;
-                               }
+       return global_magic;
+}
+
+/*
+ * Parse the pathspec element looking for long magic
+ *
+ * saves all magic in 'magic'
+ * if prefix magic is used, save the prefix length in 'prefix_len'
+ * returns the position in 'elem' after all magic has been parsed
+ */
+static const char *parse_long_magic(unsigned *magic, int *prefix_len,
+                                   const char *elem)
+{
+       const char *pos;
+       const char *nextat;
+
+       for (pos = elem + 2; *pos && *pos != ')'; pos = nextat) {
+               size_t len = strcspn(pos, ",)");
+               int i;
+
+               if (pos[len] == ',')
+                       nextat = pos + len + 1; /* handle ',' */
+               else
+                       nextat = pos + len; /* handle ')' and '\0' */
+
+               if (!len)
+                       continue;
+
+               if (starts_with(pos, "prefix:")) {
+                       char *endptr;
+                       *prefix_len = strtol(pos + 7, &endptr, 10);
+                       if (endptr - pos != len)
+                               die(_("invalid parameter for pathspec magic 'prefix'"));
+                       continue;
+               }
+
+               for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++) {
+                       if (strlen(pathspec_magic[i].name) == len &&
+                           !strncmp(pathspec_magic[i].name, pos, len)) {
+                               *magic |= pathspec_magic[i].bit;
+                               break;
                        }
-                       if (ARRAY_SIZE(pathspec_magic) <= i)
-                               die(_("Invalid pathspec magic '%.*s' in '%s'"),
-                                   (int) len, copyfrom, elt);
                }
-               if (*copyfrom != ')')
-                       die(_("Missing ')' at the end of pathspec magic in '%s'"), elt);
-               long_magic_end = copyfrom;
-               copyfrom++;
-       } else {
-               /* shorthand */
-               for (copyfrom = elt + 1;
-                    *copyfrom && *copyfrom != ':';
-                    copyfrom++) {
-                       char ch = *copyfrom;
 
-                       if (!is_pathspec_magic(ch))
+               if (ARRAY_SIZE(pathspec_magic) <= i)
+                       die(_("Invalid pathspec magic '%.*s' in '%s'"),
+                           (int) len, pos, elem);
+       }
+
+       if (*pos != ')')
+               die(_("Missing ')' at the end of pathspec magic in '%s'"),
+                   elem);
+       pos++;
+
+       return pos;
+}
+
+/*
+ * Parse the pathspec element looking for short magic
+ *
+ * saves all magic in 'magic'
+ * returns the position in 'elem' after all magic has been parsed
+ */
+static const char *parse_short_magic(unsigned *magic, const char *elem)
+{
+       const char *pos;
+
+       for (pos = elem + 1; *pos && *pos != ':'; pos++) {
+               char ch = *pos;
+               int i;
+
+               if (!is_pathspec_magic(ch))
+                       break;
+
+               for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++) {
+                       if (pathspec_magic[i].mnemonic == ch) {
+                               *magic |= pathspec_magic[i].bit;
                                break;
-                       for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++)
-                               if (pathspec_magic[i].mnemonic == ch) {
-                                       short_magic |= pathspec_magic[i].bit;
-                                       break;
-                               }
-                       if (ARRAY_SIZE(pathspec_magic) <= i)
-                               die(_("Unimplemented pathspec magic '%c' in '%s'"),
-                                   ch, elt);
+                       }
                }
-               if (*copyfrom == ':')
-                       copyfrom++;
+
+               if (ARRAY_SIZE(pathspec_magic) <= i)
+                       die(_("Unimplemented pathspec magic '%c' in '%s'"),
+                           ch, elem);
        }
 
-       magic |= short_magic;
-       *p_short_magic = short_magic;
+       if (*pos == ':')
+               pos++;
 
-       /* --noglob-pathspec adds :(literal) _unless_ :(glob) is specified */
-       if (noglob_global && !(magic & PATHSPEC_GLOB))
-               global_magic |= PATHSPEC_LITERAL;
+       return pos;
+}
 
-       /* --glob-pathspec is overridden by :(literal) */
-       if ((global_magic & PATHSPEC_GLOB) && (magic & PATHSPEC_LITERAL))
-               global_magic &= ~PATHSPEC_GLOB;
+static const char *parse_element_magic(unsigned *magic, int *prefix_len,
+                                      const char *elem)
+{
+       if (elem[0] != ':' || get_literal_global())
+               return elem; /* nothing to do */
+       else if (elem[1] == '(')
+               /* longhand */
+               return parse_long_magic(magic, prefix_len, elem);
+       else
+               /* shorthand */
+               return parse_short_magic(magic, elem);
+}
+
+static void strip_submodule_slash_cheap(struct pathspec_item *item)
+{
+       if (item->len >= 1 && item->match[item->len - 1] == '/') {
+               int i = cache_name_pos(item->match, item->len - 1);
+
+               if (i >= 0 && S_ISGITLINK(active_cache[i]->ce_mode)) {
+                       item->len--;
+                       item->match[item->len] = '\0';
+               }
+       }
+}
+
+static void strip_submodule_slash_expensive(struct pathspec_item *item)
+{
+       int i;
+
+       for (i = 0; i < active_nr; i++) {
+               struct cache_entry *ce = active_cache[i];
+               int ce_len = ce_namelen(ce);
+
+               if (!S_ISGITLINK(ce->ce_mode))
+                       continue;
+
+               if (item->len <= ce_len || item->match[ce_len] != '/' ||
+                   memcmp(ce->name, item->match, ce_len))
+                       continue;
+
+               if (item->len == ce_len + 1) {
+                       /* strip trailing slash */
+                       item->len--;
+                       item->match[item->len] = '\0';
+               } else {
+                       die(_("Pathspec '%s' is in submodule '%.*s'"),
+                           item->original, ce_len, ce->name);
+               }
+       }
+}
+
+static void die_inside_submodule_path(struct pathspec_item *item)
+{
+       int i;
+
+       for (i = 0; i < active_nr; i++) {
+               struct cache_entry *ce = active_cache[i];
+               int ce_len = ce_namelen(ce);
+
+               if (!S_ISGITLINK(ce->ce_mode))
+                       continue;
+
+               if (item->len < ce_len ||
+                   !(item->match[ce_len] == '/' || item->match[ce_len] == '\0') ||
+                   memcmp(ce->name, item->match, ce_len))
+                       continue;
 
-       magic |= global_magic;
+               die(_("Pathspec '%s' is in submodule '%.*s'"),
+                   item->original, ce_len, ce->name);
+       }
+}
+
+/*
+ * Perform the initialization of a pathspec_item based on a pathspec element.
+ */
+static void init_pathspec_item(struct pathspec_item *item, unsigned flags,
+                              const char *prefix, int prefixlen,
+                              const char *elt)
+{
+       unsigned magic = 0, element_magic = 0;
+       const char *copyfrom = elt;
+       char *match;
+       int pathspec_prefix = -1;
+
+       /* PATHSPEC_LITERAL_PATH ignores magic */
+       if (flags & PATHSPEC_LITERAL_PATH) {
+               magic = PATHSPEC_LITERAL;
+       } else {
+               copyfrom = parse_element_magic(&element_magic,
+                                              &pathspec_prefix,
+                                              elt);
+               magic |= element_magic;
+               magic |= get_global_magic(element_magic);
+       }
+
+       item->magic = magic;
 
        if (pathspec_prefix >= 0 &&
            (prefixlen || (prefix && *prefix)))
@@ -229,6 +349,7 @@ static unsigned prefix_pathspec(struct pathspec_item *item,
        if ((magic & PATHSPEC_LITERAL) && (magic & PATHSPEC_GLOB))
                die(_("%s: 'literal' and 'glob' are incompatible"), elt);
 
+       /* Create match string which will be used for pathspec matching */
        if (pathspec_prefix >= 0) {
                match = xstrdup(copyfrom);
                prefixlen = pathspec_prefix;
@@ -236,69 +357,47 @@ static unsigned prefix_pathspec(struct pathspec_item *item,
                match = xstrdup(copyfrom);
                prefixlen = 0;
        } else {
-               match = prefix_path_gently(prefix, prefixlen, &prefixlen, copyfrom);
+               match = prefix_path_gently(prefix, prefixlen,
+                                          &prefixlen, copyfrom);
                if (!match)
                        die(_("%s: '%s' is outside repository"), elt, copyfrom);
        }
-       *raw = item->match = match;
+
+       item->match = match;
+       item->len = strlen(item->match);
+       item->prefix = prefixlen;
+
        /*
         * Prefix the pathspec (keep all magic) and assign to
         * original. Useful for passing to another command.
         */
-       if (flags & PATHSPEC_PREFIX_ORIGIN) {
+       if ((flags & PATHSPEC_PREFIX_ORIGIN) &&
+           prefixlen && !get_literal_global()) {
                struct strbuf sb = STRBUF_INIT;
-               if (prefixlen && !literal_global) {
-                       /* Preserve the actual prefix length of each pattern */
-                       if (short_magic)
-                               prefix_short_magic(&sb, prefixlen, short_magic);
-                       else if (long_magic_end) {
-                               strbuf_add(&sb, elt, long_magic_end - elt);
-                               strbuf_addf(&sb, ",prefix:%d)", prefixlen);
-                       } else
-                               strbuf_addf(&sb, ":(prefix:%d)", prefixlen);
-               }
+
+               /* Preserve the actual prefix length of each pattern */
+               prefix_magic(&sb, prefixlen, element_magic);
+
                strbuf_addstr(&sb, match);
                item->original = strbuf_detach(&sb, NULL);
-       } else
-               item->original = elt;
-       item->len = strlen(item->match);
-       item->prefix = prefixlen;
-
-       if ((flags & PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP) &&
-           (item->len >= 1 && item->match[item->len - 1] == '/') &&
-           (i = cache_name_pos(item->match, item->len - 1)) >= 0 &&
-           S_ISGITLINK(active_cache[i]->ce_mode)) {
-               item->len--;
-               match[item->len] = '\0';
+       } else {
+               item->original = xstrdup(elt);
        }
 
+       if (flags & PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP)
+               strip_submodule_slash_cheap(item);
+
        if (flags & PATHSPEC_STRIP_SUBMODULE_SLASH_EXPENSIVE)
-               for (i = 0; i < active_nr; i++) {
-                       struct cache_entry *ce = active_cache[i];
-                       int ce_len = ce_namelen(ce);
-
-                       if (!S_ISGITLINK(ce->ce_mode))
-                               continue;
-
-                       if (item->len <= ce_len || match[ce_len] != '/' ||
-                           memcmp(ce->name, match, ce_len))
-                               continue;
-                       if (item->len == ce_len + 1) {
-                               /* strip trailing slash */
-                               item->len--;
-                               match[item->len] = '\0';
-                       } else
-                               die (_("Pathspec '%s' is in submodule '%.*s'"),
-                                    elt, ce_len, ce->name);
-               }
+               strip_submodule_slash_expensive(item);
 
-       if (magic & PATHSPEC_LITERAL)
+       if (magic & PATHSPEC_LITERAL) {
                item->nowildcard_len = item->len;
-       else {
+       else {
                item->nowildcard_len = simple_length(item->match);
                if (item->nowildcard_len < prefixlen)
                        item->nowildcard_len = prefixlen;
        }
+
        item->flags = 0;
        if (magic & PATHSPEC_GLOB) {
                /*
@@ -313,9 +412,18 @@ static unsigned prefix_pathspec(struct pathspec_item *item,
        }
 
        /* sanity checks, pathspec matchers assume these are sane */
-       assert(item->nowildcard_len <= item->len &&
-              item->prefix         <= item->len);
-       return magic;
+       if (item->nowildcard_len > item->len ||
+           item->prefix         > item->len) {
+               /*
+                * This case can be triggered by the user pointing us to a
+                * pathspec inside a submodule, which is an input error.
+                * Detect that here and complain, but fallback in the
+                * non-submodule case to a BUG, as we have no idea what
+                * would trigger that.
+                */
+               die_inside_submodule_path(item);
+               die ("BUG: item->nowildcard_len > item->len || item->prefix > item->len)");
+       }
 }
 
 static int pathspec_item_cmp(const void *a_, const void *b_)
@@ -328,22 +436,22 @@ static int pathspec_item_cmp(const void *a_, const void *b_)
 }
 
 static void NORETURN unsupported_magic(const char *pattern,
-                                      unsigned magic,
-                                      unsigned short_magic)
+                                      unsigned magic)
 {
        struct strbuf sb = STRBUF_INIT;
-       int i, n;
-       for (n = i = 0; i < ARRAY_SIZE(pathspec_magic); i++) {
+       int i;
+       for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++) {
                const struct pathspec_magic *m = pathspec_magic + i;
                if (!(magic & m->bit))
                        continue;
                if (sb.len)
-                       strbuf_addch(&sb, ' ');
-               if (short_magic & m->bit)
-                       strbuf_addf(&sb, "'%c'", m->mnemonic);
+                       strbuf_addstr(&sb, ", ");
+
+               if (m->mnemonic)
+                       strbuf_addf(&sb, _("'%s' (mnemonic: '%c')"),
+                                   m->name, m->mnemonic);
                else
                        strbuf_addf(&sb, "'%s'", m->name);
-               n++;
        }
        /*
         * We may want to substitute "this command" with a command
@@ -381,8 +489,6 @@ void parse_pathspec(struct pathspec *pathspec,
 
        /* No arguments with prefix -> prefix pathspec */
        if (!entry) {
-               static const char *raw[2];
-
                if (flags & PATHSPEC_PREFER_FULL)
                        return;
 
@@ -390,14 +496,11 @@ void parse_pathspec(struct pathspec *pathspec,
                        die("BUG: PATHSPEC_PREFER_CWD requires arguments");
 
                pathspec->items = item = xcalloc(1, sizeof(*item));
-               item->match = prefix;
-               item->original = prefix;
+               item->match = xstrdup(prefix);
+               item->original = xstrdup(prefix);
                item->nowildcard_len = item->len = strlen(prefix);
                item->prefix = item->len;
-               raw[0] = prefix;
-               raw[1] = NULL;
                pathspec->nr = 1;
-               pathspec->_raw = raw;
                return;
        }
 
@@ -415,25 +518,17 @@ void parse_pathspec(struct pathspec *pathspec,
        pathspec->nr = n;
        ALLOC_ARRAY(pathspec->items, n);
        item = pathspec->items;
-       pathspec->_raw = argv;
        prefixlen = prefix ? strlen(prefix) : 0;
 
        for (i = 0; i < n; i++) {
-               unsigned short_magic;
                entry = argv[i];
 
-               item[i].magic = prefix_pathspec(item + i, &short_magic,
-                                               argv + i, flags,
-                                               prefix, prefixlen, entry);
-               if ((flags & PATHSPEC_LITERAL_PATH) &&
-                   !(magic_mask & PATHSPEC_LITERAL))
-                       item[i].magic |= PATHSPEC_LITERAL;
+               init_pathspec_item(item + i, flags, prefix, prefixlen, entry);
+
                if (item[i].magic & PATHSPEC_EXCLUDE)
                        nr_exclude++;
                if (item[i].magic & magic_mask)
-                       unsupported_magic(entry,
-                                         item[i].magic & magic_mask,
-                                         short_magic);
+                       unsupported_magic(entry, item[i].magic & magic_mask);
 
                if ((flags & PATHSPEC_SYMLINK_LEADING_PATH) &&
                    has_symlink_leading_path(item[i].match, item[i].len)) {
@@ -457,45 +552,29 @@ void parse_pathspec(struct pathspec *pathspec,
        }
 }
 
-/*
- * N.B. get_pathspec() is deprecated in favor of the "struct pathspec"
- * based interface - see pathspec.c:parse_pathspec().
- *
- * Arguments:
- *  - prefix - a path relative to the root of the working tree
- *  - pathspec - a list of paths underneath the prefix path
- *
- * Iterates over pathspec, prepending each path with prefix,
- * and return the resulting list.
- *
- * If pathspec is empty, return a singleton list containing prefix.
- *
- * If pathspec and prefix are both empty, return an empty list.
- *
- * This is typically used by built-in commands such as add.c, in order
- * to normalize argv arguments provided to the built-in into a list of
- * paths to process, all relative to the root of the working tree.
- */
-const char **get_pathspec(const char *prefix, const char **pathspec)
-{
-       struct pathspec ps;
-       parse_pathspec(&ps,
-                      PATHSPEC_ALL_MAGIC &
-                      ~(PATHSPEC_FROMTOP | PATHSPEC_LITERAL),
-                      PATHSPEC_PREFER_CWD,
-                      prefix, pathspec);
-       return ps._raw;
-}
-
 void copy_pathspec(struct pathspec *dst, const struct pathspec *src)
 {
+       int i;
+
        *dst = *src;
        ALLOC_ARRAY(dst->items, dst->nr);
        COPY_ARRAY(dst->items, src->items, dst->nr);
+
+       for (i = 0; i < dst->nr; i++) {
+               dst->items[i].match = xstrdup(src->items[i].match);
+               dst->items[i].original = xstrdup(src->items[i].original);
+       }
 }
 
 void clear_pathspec(struct pathspec *pathspec)
 {
+       int i;
+
+       for (i = 0; i < pathspec->nr; i++) {
+               free(pathspec->items[i].match);
+               free(pathspec->items[i].original);
+       }
        free(pathspec->items);
        pathspec->items = NULL;
+       pathspec->nr = 0;
 }
index 59809e4793a20e3030462ec23bff2b5358283282..49fd823ddfe9eb96c983f9f812f6ca1dcf062951 100644 (file)
 #define PATHSPEC_ONESTAR 1     /* the pathspec pattern satisfies GFNM_ONESTAR */
 
 struct pathspec {
-       const char **_raw; /* get_pathspec() result, not freed by clear_pathspec() */
        int nr;
        unsigned int has_wildcard:1;
        unsigned int recursive:1;
        unsigned magic;
        int max_depth;
        struct pathspec_item {
-               const char *match;
-               const char *original;
+               char *match;
+               char *original;
                unsigned magic;
                int len, prefix;
                int nowildcard_len;
index 2eca639cce3a084286418d56f2b48a4303c18dd7..7a9a7de91e6e84c429b0d485301c3500e404dd2f 100644 (file)
@@ -2285,7 +2285,8 @@ int index_name_is_other(const struct index_state *istate, const char *name,
        return 1;
 }
 
-void *read_blob_data_from_index(struct index_state *istate, const char *path, unsigned long *size)
+void *read_blob_data_from_index(const struct index_state *istate,
+                               const char *path, unsigned long *size)
 {
        int pos, len;
        unsigned long sz;
index ad6c5424edab2ae15ac17bfcf12ac4ee93b5aa3f..d5eaec73742e74738c522cdc36ba9bf811b158b2 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -1716,9 +1716,6 @@ static const char *branch_get_push_1(struct branch *branch, struct strbuf *err)
 {
        struct remote *remote;
 
-       if (!branch)
-               return error_buf(err, _("HEAD does not point to a branch"));
-
        remote = remote_get(pushremote_for_branch(branch, NULL));
        if (!remote)
                return error_buf(err,
@@ -1778,6 +1775,9 @@ static const char *branch_get_push_1(struct branch *branch, struct strbuf *err)
 
 const char *branch_get_push(struct branch *branch, struct strbuf *err)
 {
+       if (!branch)
+               return error_buf(err, _("HEAD does not point to a branch"));
+
        if (!branch->push_tracking_ref)
                branch->push_tracking_ref = branch_get_push_1(branch, err);
        return branch->push_tracking_ref;
index ca905a9e8038dda26f8c4e146049ad00ed1cd8df..73bfba7ef94e678afa830550199f14b6f5ca5ad0 100644 (file)
@@ -29,6 +29,8 @@ static int installed_child_cleanup_handler;
 
 static void cleanup_children(int sig, int in_signal)
 {
+       struct child_to_clean *children_to_wait_for = NULL;
+
        while (children_to_clean) {
                struct child_to_clean *p = children_to_clean;
                children_to_clean = p->next;
@@ -45,6 +47,23 @@ static void cleanup_children(int sig, int in_signal)
                }
 
                kill(p->pid, sig);
+
+               if (p->process->wait_after_clean) {
+                       p->next = children_to_wait_for;
+                       children_to_wait_for = p;
+               } else {
+                       if (!in_signal)
+                               free(p);
+               }
+       }
+
+       while (children_to_wait_for) {
+               struct child_to_clean *p = children_to_wait_for;
+               children_to_wait_for = p->next;
+
+               while (waitpid(p->pid, NULL, 0) < 0 && errno == EINTR)
+                       ; /* spin waiting for process exit or error */
+
                if (!in_signal)
                        free(p);
        }
index dd1c78c28db90b5261454dd8fae925faf4548e21..4fa8f65adbdcea4d007435d9c1d54c622df54474 100644 (file)
@@ -43,6 +43,7 @@ struct child_process {
        unsigned stdout_to_stderr:1;
        unsigned use_shell:1;
        unsigned clean_on_exit:1;
+       unsigned wait_after_clean:1;
        void (*clean_on_exit_handler)(struct child_process *process);
        void *clean_on_exit_handler_cbdata;
 };
diff --git a/setup.c b/setup.c
index fe572b82c355390e6101aea15d2d53ec5b3b1e15..1b534a750810f3773ba9c21a5ec221f54857349b 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -256,8 +256,10 @@ int get_common_dir_noenv(struct strbuf *sb, const char *gitdir)
                strbuf_addbuf(&path, &data);
                strbuf_addstr(sb, real_path(path.buf));
                ret = 1;
-       } else
+       } else {
                strbuf_addstr(sb, gitdir);
+       }
+
        strbuf_release(&data);
        strbuf_release(&path);
        return ret;
@@ -692,7 +694,7 @@ static const char *setup_discovered_git_dir(const char *gitdir,
        /* --work-tree is set without --git-dir; use discovered one */
        if (getenv(GIT_WORK_TREE_ENVIRONMENT) || git_work_tree_cfg) {
                if (offset != cwd->len && !is_absolute_path(gitdir))
-                       gitdir = xstrdup(real_path(gitdir));
+                       gitdir = real_pathdup(gitdir);
                if (chdir(cwd->buf))
                        die_errno("Could not come back to cwd");
                return setup_explicit_git_dir(gitdir, cwd, nongit_ok);
@@ -800,11 +802,12 @@ static int canonicalize_ceiling_entry(struct string_list_item *item,
                /* Keep entry but do not canonicalize it */
                return 1;
        } else {
-               const char *real_path = real_path_if_valid(ceil);
-               if (!real_path)
+               char *real_path = real_pathdup(ceil);
+               if (!real_path) {
                        return 0;
+               }
                free(item->string);
-               item->string = xstrdup(real_path);
+               item->string = real_path;
                return 1;
        }
 }
index 1eb47f61131a81b587dc4fe80e1b175f37ba0540..b5e827ac9e716db9fad69960058f1bf2fa50c9d3 100644 (file)
@@ -284,7 +284,7 @@ static int link_alt_odb_entry(const char *entry, const char *relative_base,
        struct strbuf pathbuf = STRBUF_INIT;
 
        if (!is_absolute_path(entry) && relative_base) {
-               strbuf_addstr(&pathbuf, real_path(relative_base));
+               strbuf_realpath(&pathbuf, relative_base, 1);
                strbuf_addch(&pathbuf, '/');
        }
        strbuf_addstr(&pathbuf, entry);
index ec13ab5a3dd82ac90c1c83d9e07dc8c045a992d2..4bf50f398a5bd705531e32f9329c0e3b7e630506 100644 (file)
@@ -379,7 +379,7 @@ static int parse_config(const char *var, const char *value, void *data)
        return ret;
 }
 
-static int gitmodule_sha1_from_commit(const unsigned char *treeish_name,
+int gitmodule_sha1_from_commit(const unsigned char *treeish_name,
                                      unsigned char *gitmodules_sha1,
                                      struct strbuf *rev)
 {
index 99df8e593cc7c274f88a73dfdd6524b7f661692c..70f19363fd8bf5b7f42e974dacdc3ed2cd58a96a 100644 (file)
@@ -29,6 +29,9 @@ const struct submodule *submodule_from_name(const unsigned char *commit_or_tree,
                const char *name);
 const struct submodule *submodule_from_path(const unsigned char *commit_or_tree,
                const char *path);
+extern int gitmodule_sha1_from_commit(const unsigned char *commit_sha1,
+                                     unsigned char *gitmodules_sha1,
+                                     struct strbuf *rev);
 void submodule_free(void);
 
 #endif /* SUBMODULE_CONFIG_H */
index 73521cdbb29abf7e3b1d9eb7293d16e94ec8ccce..4c4f033e8a0f9f23e759949209c323875cd119fc 100644 (file)
@@ -199,6 +199,56 @@ void gitmodules_config(void)
        }
 }
 
+void gitmodules_config_sha1(const unsigned char *commit_sha1)
+{
+       struct strbuf rev = STRBUF_INIT;
+       unsigned char sha1[20];
+
+       if (gitmodule_sha1_from_commit(commit_sha1, sha1, &rev)) {
+               git_config_from_blob_sha1(submodule_config, rev.buf,
+                                         sha1, NULL);
+       }
+       strbuf_release(&rev);
+}
+
+/*
+ * Determine if a submodule has been initialized at a given 'path'
+ */
+int is_submodule_initialized(const char *path)
+{
+       int ret = 0;
+       const struct submodule *module = NULL;
+
+       module = submodule_from_path(null_sha1, path);
+
+       if (module) {
+               char *key = xstrfmt("submodule.%s.url", module->name);
+               char *value = NULL;
+
+               ret = !git_config_get_string(key, &value);
+
+               free(value);
+               free(key);
+       }
+
+       return ret;
+}
+
+/*
+ * Determine if a submodule has been populated at a given 'path'
+ */
+int is_submodule_populated(const char *path)
+{
+       int ret = 0;
+       char *gitdir = xstrfmt("%s/.git", path);
+
+       if (resolve_gitdir(gitdir))
+               ret = 1;
+
+       free(gitdir);
+       return ret;
+}
+
 int parse_submodule_update_strategy(const char *value,
                struct submodule_update_strategy *dst)
 {
@@ -1093,45 +1143,64 @@ int submodule_uses_gitfile(const char *path)
        return 1;
 }
 
-int ok_to_remove_submodule(const char *path)
+/*
+ * Check if it is a bad idea to remove a submodule, i.e. if we'd lose data
+ * when doing so.
+ *
+ * Return 1 if we'd lose data, return 0 if the removal is fine,
+ * and negative values for errors.
+ */
+int bad_to_remove_submodule(const char *path, unsigned flags)
 {
        ssize_t len;
        struct child_process cp = CHILD_PROCESS_INIT;
-       const char *argv[] = {
-               "status",
-               "--porcelain",
-               "-u",
-               "--ignore-submodules=none",
-               NULL,
-       };
        struct strbuf buf = STRBUF_INIT;
-       int ok_to_remove = 1;
+       int ret = 0;
 
        if (!file_exists(path) || is_empty_dir(path))
-               return 1;
+               return 0;
 
        if (!submodule_uses_gitfile(path))
-               return 0;
+               return 1;
+
+       argv_array_pushl(&cp.args, "status", "--porcelain",
+                                  "--ignore-submodules=none", NULL);
+
+       if (flags & SUBMODULE_REMOVAL_IGNORE_UNTRACKED)
+               argv_array_push(&cp.args, "-uno");
+       else
+               argv_array_push(&cp.args, "-uall");
+
+       if (!(flags & SUBMODULE_REMOVAL_IGNORE_IGNORED_UNTRACKED))
+               argv_array_push(&cp.args, "--ignored");
 
-       cp.argv = argv;
        prepare_submodule_repo_env(&cp.env_array);
        cp.git_cmd = 1;
        cp.no_stdin = 1;
        cp.out = -1;
        cp.dir = path;
-       if (start_command(&cp))
-               die("Could not run 'git status --porcelain -uall --ignore-submodules=none' in submodule %s", path);
+       if (start_command(&cp)) {
+               if (flags & SUBMODULE_REMOVAL_DIE_ON_ERROR)
+                       die(_("could not start 'git status in submodule '%s'"),
+                               path);
+               ret = -1;
+               goto out;
+       }
 
        len = strbuf_read(&buf, cp.out, 1024);
        if (len > 2)
-               ok_to_remove = 0;
+               ret = 1;
        close(cp.out);
 
-       if (finish_command(&cp))
-               die("'git status --porcelain -uall --ignore-submodules=none' failed in submodule %s", path);
-
+       if (finish_command(&cp)) {
+               if (flags & SUBMODULE_REMOVAL_DIE_ON_ERROR)
+                       die(_("could not run 'git status in submodule '%s'"),
+                               path);
+               ret = -1;
+       }
+out:
        strbuf_release(&buf);
-       return ok_to_remove;
+       return ret;
 }
 
 static int find_first_merges(struct object_array *result, const char *path,
@@ -1310,7 +1379,8 @@ void prepare_submodule_repo_env(struct argv_array *out)
                if (strcmp(*var, CONFIG_DATA_ENVIRONMENT))
                        argv_array_push(out, *var);
        }
-       argv_array_push(out, "GIT_DIR=.git");
+       argv_array_pushf(out, "%s=%s", GIT_DIR_ENVIRONMENT,
+                        DEFAULT_GIT_DIR_ENVIRONMENT);
 }
 
 /*
@@ -1333,7 +1403,7 @@ static void relocate_single_git_dir_into_superproject(const char *prefix,
                /* If it is an actual gitfile, it doesn't need migration. */
                return;
 
-       real_old_git_dir = xstrdup(real_path(old_git_dir));
+       real_old_git_dir = real_pathdup(old_git_dir);
 
        sub = submodule_from_path(null_sha1, path);
        if (!sub)
@@ -1342,7 +1412,7 @@ static void relocate_single_git_dir_into_superproject(const char *prefix,
        new_git_dir = git_path("modules/%s", sub->name);
        if (safe_create_leading_directories_const(new_git_dir) < 0)
                die(_("could not create directory '%s'"), new_git_dir);
-       real_new_git_dir = xstrdup(real_path(new_git_dir));
+       real_new_git_dir = real_pathdup(new_git_dir);
 
        if (!prefix)
                prefix = get_super_prefix();
@@ -1379,8 +1449,8 @@ void absorb_git_dir_into_superproject(const char *prefix,
                goto out;
 
        /* Is it already absorbed into the superprojects git dir? */
-       real_sub_git_dir = xstrdup(real_path(sub_git_dir));
-       real_common_git_dir = xstrdup(real_path(get_git_common_dir()));
+       real_sub_git_dir = real_pathdup(sub_git_dir);
+       real_common_git_dir = real_pathdup(get_git_common_dir());
        if (!skip_prefix(real_sub_git_dir, real_common_git_dir, &v))
                relocate_single_git_dir_into_superproject(prefix, path);
 
index b7576d6f43fee1d7a537bb61cbe02e5780dffa38..b7fe4d20279dfe165338dc412595fe1ccf6ad73c 100644 (file)
@@ -30,52 +30,63 @@ struct submodule_update_strategy {
 };
 #define SUBMODULE_UPDATE_STRATEGY_INIT {SM_UPDATE_UNSPECIFIED, NULL}
 
-int is_staging_gitmodules_ok(void);
-int update_path_in_gitmodules(const char *oldpath, const char *newpath);
-int remove_path_from_gitmodules(const char *path);
-void stage_updated_gitmodules(void);
-void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt,
+extern int is_staging_gitmodules_ok(void);
+extern int update_path_in_gitmodules(const char *oldpath, const char *newpath);
+extern int remove_path_from_gitmodules(const char *path);
+extern void stage_updated_gitmodules(void);
+extern void set_diffopt_flags_from_submodule_config(struct diff_options *,
                const char *path);
-int submodule_config(const char *var, const char *value, void *cb);
-void gitmodules_config(void);
-int parse_submodule_update_strategy(const char *value,
+extern int submodule_config(const char *var, const char *value, void *cb);
+extern void gitmodules_config(void);
+extern void gitmodules_config_sha1(const unsigned char *commit_sha1);
+extern int is_submodule_initialized(const char *path);
+extern int is_submodule_populated(const char *path);
+extern int parse_submodule_update_strategy(const char *value,
                struct submodule_update_strategy *dst);
-const char *submodule_strategy_to_string(const struct submodule_update_strategy *s);
-void handle_ignore_submodules_arg(struct diff_options *diffopt, const char *);
-void show_submodule_summary(FILE *f, const char *path,
+extern const char *submodule_strategy_to_string(const struct submodule_update_strategy *s);
+extern void handle_ignore_submodules_arg(struct diff_options *, const char *);
+extern void show_submodule_summary(FILE *f, const char *path,
                const char *line_prefix,
                struct object_id *one, struct object_id *two,
                unsigned dirty_submodule, const char *meta,
                const char *del, const char *add, const char *reset);
-void show_submodule_inline_diff(FILE *f, const char *path,
+extern void show_submodule_inline_diff(FILE *f, const char *path,
                const char *line_prefix,
                struct object_id *one, struct object_id *two,
                unsigned dirty_submodule, const char *meta,
                const char *del, const char *add, const char *reset,
                const struct diff_options *opt);
-void set_config_fetch_recurse_submodules(int value);
-void check_for_new_submodule_commits(unsigned char new_sha1[20]);
-int fetch_populated_submodules(const struct argv_array *options,
+extern void set_config_fetch_recurse_submodules(int value);
+extern void check_for_new_submodule_commits(unsigned char new_sha1[20]);
+extern int fetch_populated_submodules(const struct argv_array *options,
                               const char *prefix, int command_line_option,
                               int quiet, int max_parallel_jobs);
-unsigned is_submodule_modified(const char *path, int ignore_untracked);
-int submodule_uses_gitfile(const char *path);
-int ok_to_remove_submodule(const char *path);
-int merge_submodule(unsigned char result[20], const char *path, const unsigned char base[20],
-                   const unsigned char a[20], const unsigned char b[20], int search);
-int find_unpushed_submodules(struct sha1_array *commits, const char *remotes_name,
-               struct string_list *needs_pushing);
+extern unsigned is_submodule_modified(const char *path, int ignore_untracked);
+extern int submodule_uses_gitfile(const char *path);
+
+#define SUBMODULE_REMOVAL_DIE_ON_ERROR (1<<0)
+#define SUBMODULE_REMOVAL_IGNORE_UNTRACKED (1<<1)
+#define SUBMODULE_REMOVAL_IGNORE_IGNORED_UNTRACKED (1<<2)
+extern int bad_to_remove_submodule(const char *path, unsigned flags);
+extern int merge_submodule(unsigned char result[20], const char *path,
+                          const unsigned char base[20],
+                          const unsigned char a[20],
+                          const unsigned char b[20], int search);
+extern int find_unpushed_submodules(struct sha1_array *commits,
+                                   const char *remotes_name,
+                                   struct string_list *needs_pushing);
 extern int push_unpushed_submodules(struct sha1_array *commits,
                                    const char *remotes_name,
                                    int dry_run);
-int parallel_submodules(void);
+extern void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir);
+extern int parallel_submodules(void);
 
 /*
  * Prepare the "env_array" parameter of a "struct child_process" for executing
  * a submodule by clearing any repo-specific envirionment variables, but
  * retaining any config in the environment.
  */
-void prepare_submodule_repo_env(struct argv_array *out);
+extern void prepare_submodule_repo_env(struct argv_array *out);
 
 #define ABSORB_GITDIR_RECURSE_SUBMODULES (1<<0)
 extern void absorb_git_dir_into_superproject(const char *prefix,
index 7214f5b33fa41fbb685e572ca24a0cb11337d251..623a32aa6e323d11267cb89af62d42c325a4dd75 100755 (executable)
@@ -60,4 +60,10 @@ test_expect_success '@{push} with push refspecs' '
        resolve topic@{push} refs/remotes/origin/magic/topic
 '
 
+test_expect_success 'resolving @{push} fails with a detached HEAD' '
+       git checkout HEAD^0 &&
+       test_when_finished "git checkout -" &&
+       test_must_fail git rev-parse @{push}
+'
+
 test_done
index 372307c21b983437b870ac65ff4f91d2c36fab30..0acf4b14614c750d3c2324d8a941aa77d5c7ad56 100755 (executable)
@@ -385,7 +385,7 @@ test_expect_success '--continue respects opts' '
        git cat-file commit HEAD~1 >picked_msg &&
        git cat-file commit HEAD~2 >unrelatedpick_msg &&
        git cat-file commit HEAD~3 >initial_msg &&
-       test_must_fail grep "cherry picked from" initial_msg &&
+       ! grep "cherry picked from" initial_msg &&
        grep "cherry picked from" unrelatedpick_msg &&
        grep "cherry picked from" picked_msg &&
        grep "cherry picked from" anotherpick_msg
@@ -426,9 +426,9 @@ test_expect_failure '--signoff is automatically propagated to resolved conflict'
        git cat-file commit HEAD~1 >picked_msg &&
        git cat-file commit HEAD~2 >unrelatedpick_msg &&
        git cat-file commit HEAD~3 >initial_msg &&
-       test_must_fail grep "Signed-off-by:" initial_msg &&
+       ! grep "Signed-off-by:" initial_msg &&
        grep "Signed-off-by:" unrelatedpick_msg &&
-       test_must_fail grep "Signed-off-by:" picked_msg &&
+       ! grep "Signed-off-by:" picked_msg &&
        grep "Signed-off-by:" anotherpick_msg
 '
 
index bcbb68065113f6a2032a3879fabb3353c42e79d0..5aa6db584cd0dbddf6060eeb46f4c1c9836bc935 100755 (executable)
@@ -569,26 +569,22 @@ test_expect_success 'rm of a conflicted unpopulated submodule succeeds' '
        test_cmp expect actual
 '
 
-test_expect_success 'rm of a populated submodule with a .git directory fails even when forced' '
+test_expect_success 'rm of a populated submodule with a .git directory migrates git dir' '
        git checkout -f master &&
        git reset --hard &&
        git submodule update &&
        (cd submod &&
                rm .git &&
                cp -R ../.git/modules/sub .git &&
-               GIT_WORK_TREE=. git config --unset core.worktree
+               GIT_WORK_TREE=. git config --unset core.worktree &&
+               rm -r ../.git/modules/sub
        ) &&
-       test_must_fail git rm submod &&
-       test -d submod &&
-       test -d submod/.git &&
-       git status -s -uno --ignore-submodules=none >actual &&
-       ! test -s actual &&
-       test_must_fail git rm -f submod &&
-       test -d submod &&
-       test -d submod/.git &&
+       git rm submod 2>output.err &&
+       ! test -d submod &&
+       ! test -d submod/.git &&
        git status -s -uno --ignore-submodules=none >actual &&
-       test -s actual &&
-       rm -rf submod
+       test -s actual &&
+       test_i18ngrep Migrating output.err
 '
 
 cat >expect.deepmodified <<EOF
@@ -667,24 +663,19 @@ test_expect_success 'rm of a populated nested submodule with a nested .git direc
        git submodule update --recursive &&
        (cd submod/subsubmod &&
                rm .git &&
-               cp -R ../../.git/modules/sub/modules/sub .git &&
+               mv ../../.git/modules/sub/modules/sub .git &&
                GIT_WORK_TREE=. git config --unset core.worktree
        ) &&
-       test_must_fail git rm submod &&
-       test -d submod &&
-       test -d submod/subsubmod/.git &&
-       git status -s -uno --ignore-submodules=none >actual &&
-       ! test -s actual &&
-       test_must_fail git rm -f submod &&
-       test -d submod &&
-       test -d submod/subsubmod/.git &&
+       git rm submod 2>output.err &&
+       ! test -d submod &&
+       ! test -d submod/subsubmod/.git &&
        git status -s -uno --ignore-submodules=none >actual &&
-       test -s actual &&
-       rm -rf submod
+       test -s actual &&
+       test_i18ngrep Migrating output.err
 '
 
 test_expect_success 'checking out a commit after submodule removal needs manual updates' '
-       git commit -m "submodule removal" submod &&
+       git commit -m "submodule removal" submod .gitmodules &&
        git checkout HEAD^ &&
        git submodule update &&
        git checkout -q HEAD^ &&
index e4e3e28fc788fd16c73298a0c385f1b39994941e..bada0cbd32f76418a76702fbbd5143004876d074 100755 (executable)
@@ -16,11 +16,15 @@ f() {
 }
 
 t() {
+       use_config=
+       git config --unset diff.interHunkContext
+
        case $# in
        4) hunks=$4; cmd="diff -U$3";;
        5) hunks=$5; cmd="diff -U$3 --inter-hunk-context=$4";;
+       6) hunks=$5; cmd="diff -U$3"; git config diff.interHunkContext $4; use_config="(diff.interHunkContext=$4) ";;
        esac
-       label="$cmd, $1 common $2"
+       label="$use_config$cmd, $1 common $2"
        file=f$1
        expected=expected.$file.$3.$hunks
 
@@ -89,4 +93,25 @@ t 9 lines    3               2
 t 9 lines      3       2       2
 t 9 lines      3       3       1
 
+#                                      use diff.interHunkContext?
+t 1 line       0       0       2       config
+t 1 line       0       1       1       config
+t 1 line       0       2       1       config
+t 9 lines      3       3       1       config
+t 2 lines      0       0       2       config
+t 2 lines      0       1       2       config
+t 2 lines      0       2       1       config
+t 3 lines      1       0       2       config
+t 3 lines      1       1       1       config
+t 3 lines      1       2       1       config
+t 9 lines      3       2       2       config
+t 9 lines      3       3       1       config
+
+test_expect_success 'diff.interHunkContext invalid' '
+       git config diff.interHunkContext asdf &&
+       test_must_fail git diff &&
+       git config diff.interHunkContext -1 &&
+       test_must_fail git diff
+'
+
 test_done
index 14744b2a4b1e646890176ec53970d36de6f8ec3b..55c78709978ff75cdbd794ee4c816dfd1eb55ef0 100755 (executable)
@@ -64,6 +64,12 @@ check_zip() {
                test_cmp_bin $original/nodiff.crlf $extracted/nodiff.crlf &&
                test_cmp_bin $original/nodiff.lf   $extracted/nodiff.lf
        "
+
+       test_expect_success UNZIP " validate that custom diff is unchanged " "
+               test_cmp_bin $original/custom.cr   $extracted/custom.cr &&
+               test_cmp_bin $original/custom.crlf $extracted/custom.crlf &&
+               test_cmp_bin $original/custom.lf   $extracted/custom.lf
+       "
 }
 
 test_expect_success \
@@ -78,6 +84,9 @@ test_expect_success \
      printf "text\r"   >a/nodiff.cr &&
      printf "text\r\n" >a/nodiff.crlf &&
      printf "text\n"   >a/nodiff.lf &&
+     printf "text\r"   >a/custom.cr &&
+     printf "text\r\n" >a/custom.crlf &&
+     printf "text\n"   >a/custom.lf &&
      printf "\0\r"     >a/binary.cr &&
      printf "\0\r\n"   >a/binary.crlf &&
      printf "\0\n"     >a/binary.lf &&
@@ -112,15 +121,20 @@ test_expect_success 'add files to repository' '
 test_expect_success 'setup export-subst and diff attributes' '
        echo "a/nodiff.* -diff" >>.git/info/attributes &&
        echo "a/diff.* diff" >>.git/info/attributes &&
+       echo "a/custom.* diff=custom" >>.git/info/attributes &&
+       git config diff.custom.binary true &&
        echo "substfile?" export-subst >>.git/info/attributes &&
        git log --max-count=1 "--pretty=format:A${SUBSTFORMAT}O" HEAD \
                >a/substfile1
 '
 
-test_expect_success \
-    'create bare clone' \
-    'git clone --bare . bare.git &&
-     cp .git/info/attributes bare.git/info/attributes'
+test_expect_success 'create bare clone' '
+       git clone --bare . bare.git &&
+       cp .git/info/attributes bare.git/info/attributes &&
+       # Recreate our changes to .git/config rather than just copying it, as
+       # we do not want to clobber core.bare or other settings.
+       git -C bare.git config diff.custom.binary true
+'
 
 test_expect_success \
     'remove ignored file' \
index b4c7a6ff6b72d5d06ce9fb13bbd74590ff459a0b..424bec7d77c2b3761669230bf30030883ce4c230 100755 (executable)
@@ -118,12 +118,10 @@ test_expect_success 'fetch (partial bitmap)' '
        test_cmp expect actual
 '
 
-test_expect_success 'incremental repack cannot create bitmaps' '
+test_expect_success 'incremental repack fails when bitmaps are requested' '
        test_commit more-1 &&
-       find .git/objects/pack -name "*.bitmap" >expect &&
-       git repack -d &&
-       find .git/objects/pack -name "*.bitmap" >actual &&
-       test_cmp expect actual
+       test_must_fail git repack -d 2>err &&
+       test_i18ngrep "Incremental repacks are incompatible with bitmap" err
 '
 
 test_expect_success 'incremental repack can disable bitmaps' '
index 9b19cff729381b3a8f81f113a5407ac9f9d97cda..49d3621a926786dfbfd71b205dfcb5dd3fa2d889 100755 (executable)
@@ -152,7 +152,7 @@ test_expect_success 'push with receive.fsck.missingEmail=warn' '
        git --git-dir=dst/.git config --add \
                receive.fsck.badDate warn &&
        git push --porcelain dst bogus >act 2>&1 &&
-       test_must_fail grep "missingEmail" act
+       ! grep "missingEmail" act
 '
 
 test_expect_success \
index 26b2cafc4795baa8b9377c767e4453079826c34f..0fc5a7c596b5d5e01ecb43f81d3d6eaafe611b7b 100755 (executable)
@@ -1004,7 +1004,7 @@ test_expect_success 'push --porcelain' '
 test_expect_success 'push --porcelain bad url' '
        mk_empty testrepo &&
        test_must_fail git push >.git/bar --porcelain asdfasdfasd refs/heads/master:refs/remotes/origin/master &&
-       test_must_fail grep -q Done .git/bar
+       ! grep -q Done .git/bar
 '
 
 test_expect_success 'push --porcelain rejected' '
diff --git a/t/t5580-clone-push-unc.sh b/t/t5580-clone-push-unc.sh
new file mode 100755 (executable)
index 0000000..b195f71
--- /dev/null
@@ -0,0 +1,48 @@
+#!/bin/sh
+
+test_description='various UNC path tests (Windows-only)'
+. ./test-lib.sh
+
+if ! test_have_prereq MINGW; then
+       skip_all='skipping UNC path tests, requires Windows'
+       test_done
+fi
+
+UNCPATH="$(pwd)"
+case "$UNCPATH" in
+[A-Z]:*)
+       # Use administrative share e.g. \\localhost\C$\git-sdk-64\usr\src\git
+       # (we use forward slashes here because MSYS2 and Git accept them, and
+       # they are easier on the eyes)
+       UNCPATH="//localhost/${UNCPATH%%:*}\$/${UNCPATH#?:}"
+       test -d "$UNCPATH" || {
+               skip_all='could not access administrative share; skipping'
+               test_done
+       }
+       ;;
+*)
+       skip_all='skipping UNC path tests, cannot determine current path as UNC'
+       test_done
+       ;;
+esac
+
+test_expect_success setup '
+       test_commit initial
+'
+
+test_expect_success clone '
+       git clone "file://$UNCPATH" clone
+'
+
+test_expect_success push '
+       (
+               cd clone &&
+               git checkout -b to-push &&
+               test_commit to-push &&
+               git push origin HEAD
+       ) &&
+       rev="$(git -C clone rev-parse --verify refs/heads/to-push)" &&
+       test "$rev" = "$(git rev-parse --verify refs/heads/to-push)"
+'
+
+test_done
index a43339420019718b76963d5ced92ebb981b0f0ed..4241ea5b32db4a0a85810dfe6421a161ce55fc46 100755 (executable)
@@ -151,7 +151,7 @@ test_expect_success 'clone --mirror does not repeat tags' '
        git clone --mirror src mirror2 &&
        (cd mirror2 &&
         git show-ref 2> clone.err > clone.out) &&
-       test_must_fail grep Duplicate mirror2/clone.err &&
+       ! grep Duplicate mirror2/clone.err &&
        grep some-tag mirror2/clone.out
 
 '
index 5e5370feb40c851fa29bdec17d687eb5f22502bb..8c2c6eaef83fe90d1faa088fe65ee4a851b1e471 100755 (executable)
@@ -407,7 +407,7 @@ test_expect_success 'good merge base when good and bad are siblings' '
        test_i18ngrep "merge base must be tested" my_bisect_log.txt &&
        grep $HASH4 my_bisect_log.txt &&
        git bisect good > my_bisect_log.txt &&
-       test_must_fail grep "merge base must be tested" my_bisect_log.txt &&
+       ! grep "merge base must be tested" my_bisect_log.txt &&
        grep $HASH6 my_bisect_log.txt &&
        git bisect reset
 '
diff --git a/t/t6134-pathspec-in-submodule.sh b/t/t6134-pathspec-in-submodule.sh
new file mode 100755 (executable)
index 0000000..fd401ca
--- /dev/null
@@ -0,0 +1,36 @@
+#!/bin/sh
+
+test_description='test case exclude pathspec'
+
+. ./test-lib.sh
+
+test_expect_success 'setup a submodule' '
+       test_create_repo pretzel &&
+       : >pretzel/a &&
+       git -C pretzel add a &&
+       git -C pretzel commit -m "add a file" -- a &&
+       git submodule add ./pretzel sub &&
+       git commit -a -m "add submodule" &&
+       git submodule deinit --all
+'
+
+cat <<EOF >expect
+fatal: Pathspec 'sub/a' is in submodule 'sub'
+EOF
+
+test_expect_success 'error message for path inside submodule' '
+       echo a >sub/a &&
+       test_must_fail git add sub/a 2>actual &&
+       test_cmp expect actual
+'
+
+cat <<EOF >expect
+fatal: Pathspec '.' is in submodule 'sub'
+EOF
+
+test_expect_success 'error message for path inside submodule from within submodule' '
+       test_must_fail git -C sub add . 2>actual &&
+       test_cmp expect actual
+'
+
+test_done
index 5d7d4146179bb43184f74dd646aacbba11cc8c8b..1762dfa6a306ca672e3d3430b020f1b01a16da7e 100755 (executable)
@@ -43,4 +43,29 @@ test_expect_success 'gc is not aborted due to a stale symref' '
        )
 '
 
+test_expect_success 'auto gc with too many loose objects does not attempt to create bitmaps' '
+       test_config gc.auto 3 &&
+       test_config gc.autodetach false &&
+       test_config pack.writebitmaps true &&
+       # We need to create two object whose sha1s start with 17
+       # since this is what git gc counts.  As it happens, these
+       # two blobs will do so.
+       test_commit 263 &&
+       test_commit 410 &&
+       # Our first gc will create a pack; our second will create a second pack
+       git gc --auto &&
+       ls .git/objects/pack | sort >existing_packs &&
+       test_commit 523 &&
+       test_commit 790 &&
+
+       git gc --auto 2>err &&
+       test_i18ngrep ! "^warning:" err &&
+       ls .git/objects/pack/ | sort >post_packs &&
+       comm -1 -3 existing_packs post_packs >new &&
+       comm -2 -3 existing_packs post_packs >del &&
+       test_line_count = 0 del && # No packs are deleted
+       test_line_count = 2 new # There is one new pack and its .idx
+'
+
+
 test_done
index 07869b0c09da1313dd5564c2318dc5d44818f3a9..1cfa8a21d23173e51e95906c00342fa13b85d955 100755 (executable)
@@ -149,11 +149,11 @@ test_expect_success '--force can create a tag with the name of one existing' '
        tag_exists mytag'
 
 test_expect_success '--force is moot with a non-existing tag name' '
+       test_when_finished git tag -d newtag forcetag &&
        git tag newtag >expect &&
        git tag --force forcetag >actual &&
        test_cmp expect actual
 '
-git tag -d newtag forcetag
 
 # deleting tags:
 
@@ -324,11 +324,9 @@ EOF
 '
 
 test_expect_success 'listing tags in column with column.*' '
-       git config column.tag row &&
-       git config column.ui dense &&
+       test_config column.tag row &&
+       test_config column.ui dense &&
        COLUMNS=40 git tag -l >actual &&
-       git config --unset column.ui &&
-       git config --unset column.tag &&
        cat >expected <<\EOF &&
 a1      aa1   cba     t210    t211
 v0.2.1  v1.0  v1.0.1  v1.1.3
@@ -341,9 +339,8 @@ test_expect_success 'listing tag with -n --column should fail' '
 '
 
 test_expect_success 'listing tags -n in column with column.ui ignored' '
-       git config column.ui "row dense" &&
+       test_config column.ui "row dense" &&
        COLUMNS=40 git tag -l -n >actual &&
-       git config --unset column.ui &&
        cat >expected <<\EOF &&
 a1              Foo
 aa1             Foo
@@ -1227,11 +1224,10 @@ test_expect_success GPG,RFC1991 \
 '
 
 # try to sign with bad user.signingkey
-git config user.signingkey BobTheMouse
 test_expect_success GPG \
        'git tag -s fails if gpg is misconfigured (bad key)' \
-       'test_must_fail git tag -s -m tail tag-gpg-failure'
-git config --unset user.signingkey
+       'test_config user.signingkey BobTheMouse &&
+       test_must_fail git tag -s -m tail tag-gpg-failure'
 
 # try to produce invalid signature
 test_expect_success GPG \
@@ -1511,7 +1507,7 @@ test_expect_success 'reverse lexical sort' '
 '
 
 test_expect_success 'configured lexical sort' '
-       git config tag.sort "v:refname" &&
+       test_config tag.sort "v:refname" &&
        git tag -l "foo*" >actual &&
        cat >expect <<-\EOF &&
        foo1.3
@@ -1522,6 +1518,7 @@ test_expect_success 'configured lexical sort' '
 '
 
 test_expect_success 'option override configured sort' '
+       test_config tag.sort "v:refname" &&
        git tag -l --sort=-refname "foo*" >actual &&
        cat >expect <<-\EOF &&
        foo1.6
@@ -1536,13 +1533,12 @@ test_expect_success 'invalid sort parameter on command line' '
 '
 
 test_expect_success 'invalid sort parameter in configuratoin' '
-       git config tag.sort "v:notvalid" &&
+       test_config tag.sort "v:notvalid" &&
        test_must_fail git tag -l "foo*"
 '
 
 test_expect_success 'version sort with prerelease reordering' '
-       git config --unset tag.sort &&
-       git config versionsort.prereleaseSuffix -rc &&
+       test_config versionsort.prereleaseSuffix -rc &&
        git tag foo1.6-rc1 &&
        git tag foo1.6-rc2 &&
        git tag -l --sort=version:refname "foo*" >actual &&
@@ -1557,6 +1553,7 @@ test_expect_success 'version sort with prerelease reordering' '
 '
 
 test_expect_success 'reverse version sort with prerelease reordering' '
+       test_config versionsort.prereleaseSuffix -rc &&
        git tag -l --sort=-version:refname "foo*" >actual &&
        cat >expect <<-\EOF &&
        foo1.10
@@ -1568,6 +1565,103 @@ test_expect_success 'reverse version sort with prerelease reordering' '
        test_cmp expect actual
 '
 
+test_expect_success 'version sort with prerelease reordering and common leading character' '
+       test_config versionsort.prereleaseSuffix -before &&
+       git tag foo1.7-before1 &&
+       git tag foo1.7 &&
+       git tag foo1.7-after1 &&
+       git tag -l --sort=version:refname "foo1.7*" >actual &&
+       cat >expect <<-\EOF &&
+       foo1.7-before1
+       foo1.7
+       foo1.7-after1
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'version sort with prerelease reordering, multiple suffixes and common leading character' '
+       test_config versionsort.prereleaseSuffix -before &&
+       git config --add versionsort.prereleaseSuffix -after &&
+       git tag -l --sort=version:refname "foo1.7*" >actual &&
+       cat >expect <<-\EOF &&
+       foo1.7-before1
+       foo1.7-after1
+       foo1.7
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'version sort with prerelease reordering, multiple suffixes match the same tag' '
+       test_config versionsort.prereleaseSuffix -bar &&
+       git config --add versionsort.prereleaseSuffix -foo-baz &&
+       git config --add versionsort.prereleaseSuffix -foo-bar &&
+       git tag foo1.8-foo-bar &&
+       git tag foo1.8-foo-baz &&
+       git tag foo1.8 &&
+       git tag -l --sort=version:refname "foo1.8*" >actual &&
+       cat >expect <<-\EOF &&
+       foo1.8-foo-baz
+       foo1.8-foo-bar
+       foo1.8
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'version sort with prerelease reordering, multiple suffixes match starting at the same position' '
+       test_config versionsort.prereleaseSuffix -pre &&
+       git config --add versionsort.prereleaseSuffix -prerelease &&
+       git tag foo1.9-pre1 &&
+       git tag foo1.9-pre2 &&
+       git tag foo1.9-prerelease1 &&
+       git tag -l --sort=version:refname "foo1.9*" >actual &&
+       cat >expect <<-\EOF &&
+       foo1.9-pre1
+       foo1.9-pre2
+       foo1.9-prerelease1
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'version sort with general suffix reordering' '
+       test_config versionsort.suffix -alpha &&
+       git config --add versionsort.suffix -beta &&
+       git config --add versionsort.suffix ""  &&
+       git config --add versionsort.suffix -gamma &&
+       git config --add versionsort.suffix -delta &&
+       git tag foo1.10-alpha &&
+       git tag foo1.10-beta &&
+       git tag foo1.10-gamma &&
+       git tag foo1.10-delta &&
+       git tag foo1.10-unlisted-suffix &&
+       git tag -l --sort=version:refname "foo1.10*" >actual &&
+       cat >expect <<-\EOF &&
+       foo1.10-alpha
+       foo1.10-beta
+       foo1.10
+       foo1.10-unlisted-suffix
+       foo1.10-gamma
+       foo1.10-delta
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'versionsort.suffix overrides versionsort.prereleaseSuffix' '
+       test_config versionsort.suffix -before &&
+       test_config versionsort.prereleaseSuffix -after &&
+       git tag -l --sort=version:refname "foo1.7*" >actual &&
+       cat >expect <<-\EOF &&
+       foo1.7-before1
+       foo1.7
+       foo1.7-after1
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'version sort with very long prerelease suffix' '
+       test_config versionsort.prereleaseSuffix -very-looooooooooooooooooooooooong-prerelease-suffix &&
+       git tag -l --sort=version:refname
+'
+
 run_with_limited_stack () {
        (ulimit -s 128 && "$@")
 }
@@ -1596,13 +1690,11 @@ EOF"
 
 test_expect_success '--format should list tags as per format given' '
        cat >expect <<-\EOF &&
-       refname : refs/tags/foo1.10
-       refname : refs/tags/foo1.3
-       refname : refs/tags/foo1.6
-       refname : refs/tags/foo1.6-rc1
-       refname : refs/tags/foo1.6-rc2
+       refname : refs/tags/v1.0
+       refname : refs/tags/v1.0.1
+       refname : refs/tags/v1.1.3
        EOF
-       git tag -l --format="refname : %(refname)" "foo*" >actual &&
+       git tag -l --format="refname : %(refname)" "v1*" >actual &&
        test_cmp expect actual
 '
 
index 64f322c4cc87493ebc060ae5837495563fe61958..725bbed1f866265e8030ab6e4007c04f9493b6b5 100755 (executable)
@@ -140,6 +140,23 @@ test_expect_success 'submodule update --init --recursive from subdirectory' '
        test_i18ncmp expect2 actual2
 '
 
+cat <<EOF >expect2
+Submodule 'foo/sub' ($pwd/withsubs/../rebasing) registered for path 'sub'
+EOF
+
+test_expect_success 'submodule update --init from and of subdirectory' '
+       git init withsubs &&
+       (cd withsubs &&
+        mkdir foo &&
+        git submodule add "$(pwd)/../rebasing" foo/sub &&
+        (cd foo &&
+         git submodule deinit -f sub &&
+         git submodule update --init sub 2>../../actual2
+        )
+       ) &&
+       test_i18ncmp expect2 actual2
+'
+
 apos="'";
 test_expect_success 'submodule update does not fetch already present commits' '
        (cd submodule &&
index d389ae5408ab783467f64368adc01325e258046b..eea36f1dbe37859c5b45ca18bb3c8f1080ff3f9a 100755 (executable)
@@ -134,12 +134,33 @@ test_expect_success 'reading of local configuration' '
                        "" submodule \
                                >actual &&
                test_cmp expect_local_path actual &&
-               git config submodule.a.url $old_a &&
-               git config submodule.submodule.url $old_submodule &&
+               git config submodule.a.url "$old_a" &&
+               git config submodule.submodule.url "$old_submodule" &&
                git config --unset submodule.a.path c
        )
 '
 
+cat >super/expect_url <<EOF
+Submodule url: '../submodule' for path 'b'
+Submodule url: 'git@somewhere.else.net:submodule.git' for path 'submodule'
+EOF
+
+test_expect_success 'reading of local configuration for uninitialized submodules' '
+       (
+               cd super &&
+               git submodule deinit -f b &&
+               old_submodule=$(git config submodule.submodule.url) &&
+               git config submodule.submodule.url git@somewhere.else.net:submodule.git &&
+               test-submodule-config --url \
+                       "" b \
+                       "" submodule \
+                               >actual &&
+               test_cmp expect_url actual &&
+               git config submodule.submodule.url "$old_submodule" &&
+               git submodule init b
+       )
+'
+
 cat >super/expect_fetchrecurse_die.err <<EOF
 fatal: bad submodule.submodule.fetchrecursesubmodules argument: blabla
 EOF
index 63d36fb28e44426708a743979c4bc341bdac714e..381b7df452e44622c46c8c3471f181b4568be10a 100755 (executable)
@@ -55,6 +55,22 @@ test_expect_success 'setup' '
        git rm file12 &&
        git commit -m "branch1 changes" &&
 
+       git checkout -b delete-base branch1 &&
+       mkdir -p a/a &&
+       (echo one; echo two; echo 3; echo 4) >a/a/file.txt &&
+       git add a/a/file.txt &&
+       git commit -m"base file" &&
+       git checkout -b move-to-b delete-base &&
+       mkdir -p b/b &&
+       git mv a/a/file.txt b/b/file.txt &&
+       (echo one; echo two; echo 4) >b/b/file.txt &&
+       git commit -a -m"move to b" &&
+       git checkout -b move-to-c delete-base &&
+       mkdir -p c/c &&
+       git mv a/a/file.txt c/c/file.txt &&
+       (echo one; echo two; echo 3) >c/c/file.txt &&
+       git commit -a -m"move to c" &&
+
        git checkout -b stash1 master &&
        echo stash1 change file11 >file11 &&
        git add file11 &&
@@ -86,6 +102,23 @@ test_expect_success 'setup' '
        git rm file11 &&
        git commit -m "master updates" &&
 
+       git clean -fdx &&
+       git checkout -b order-file-start master &&
+       echo start >a &&
+       echo start >b &&
+       git add a b &&
+       git commit -m start &&
+       git checkout -b order-file-side1 order-file-start &&
+       echo side1 >a &&
+       echo side1 >b &&
+       git add a b &&
+       git commit -m side1 &&
+       git checkout -b order-file-side2 order-file-start &&
+       echo side2 >a &&
+       echo side2 >b &&
+       git add a b &&
+       git commit -m side2 &&
+
        git config merge.tool mytool &&
        git config mergetool.mytool.cmd "cat \"\$REMOTE\" >\"\$MERGED\"" &&
        git config mergetool.mytool.trustExitCode true &&
@@ -94,7 +127,8 @@ test_expect_success 'setup' '
 '
 
 test_expect_success 'custom mergetool' '
-       git checkout -b test1 branch1 &&
+       test_when_finished "git reset --hard" &&
+       git checkout -b test$test_count branch1 &&
        git submodule update -N &&
        test_must_fail git merge master >/dev/null 2>&1 &&
        ( yes "" | git mergetool both >/dev/null 2>&1 ) &&
@@ -112,8 +146,13 @@ test_expect_success 'custom mergetool' '
 '
 
 test_expect_success 'mergetool crlf' '
+       test_when_finished "git reset --hard" &&
+       # This test_config line must go after the above reset line so that
+       # core.autocrlf is unconfigured before reset runs.  (The
+       # test_config command uses test_when_finished internally and
+       # test_when_finished is LIFO.)
        test_config core.autocrlf true &&
-       git checkout -b test2 branch1 &&
+       git checkout -b test$test_count branch1 &&
        test_must_fail git merge master >/dev/null 2>&1 &&
        ( yes "" | git mergetool file1 >/dev/null 2>&1 ) &&
        ( yes "" | git mergetool file2 >/dev/null 2>&1 ) &&
@@ -128,13 +167,12 @@ test_expect_success 'mergetool crlf' '
        test "$(printf x | cat subdir/file3 -)" = "$(printf "master new sub\r\nx")" &&
        git submodule update -N &&
        test "$(cat submod/bar)" = "master submodule" &&
-       git commit -m "branch1 resolved with mergetool - autocrlf" &&
-       test_config core.autocrlf false &&
-       git reset --hard
+       git commit -m "branch1 resolved with mergetool - autocrlf"
 '
 
 test_expect_success 'mergetool in subdir' '
-       git checkout -b test3 branch1 &&
+       test_when_finished "git reset --hard" &&
+       git checkout -b test$test_count branch1 &&
        git submodule update -N &&
        (
                cd subdir &&
@@ -145,8 +183,13 @@ test_expect_success 'mergetool in subdir' '
 '
 
 test_expect_success 'mergetool on file in parent dir' '
+       test_when_finished "git reset --hard" &&
+       git checkout -b test$test_count branch1 &&
+       git submodule update -N &&
        (
                cd subdir &&
+               test_must_fail git merge master >/dev/null 2>&1 &&
+               ( yes "" | git mergetool file3 >/dev/null 2>&1 ) &&
                ( yes "" | git mergetool ../file1 >/dev/null 2>&1 ) &&
                ( yes "" | git mergetool ../file2 ../spaced\ name >/dev/null 2>&1 ) &&
                ( yes "" | git mergetool ../both >/dev/null 2>&1 ) &&
@@ -161,7 +204,8 @@ test_expect_success 'mergetool on file in parent dir' '
 '
 
 test_expect_success 'mergetool skips autoresolved' '
-       git checkout -b test4 branch1 &&
+       test_when_finished "git reset --hard" &&
+       git checkout -b test$test_count branch1 &&
        git submodule update -N &&
        test_must_fail git merge master &&
        test -n "$(git ls-files -u)" &&
@@ -169,11 +213,12 @@ test_expect_success 'mergetool skips autoresolved' '
        ( yes "d" | git mergetool file12 >/dev/null 2>&1 ) &&
        ( yes "l" | git mergetool submod >/dev/null 2>&1 ) &&
        output="$(git mergetool --no-prompt)" &&
-       test "$output" = "No files need merging" &&
-       git reset --hard
+       test "$output" = "No files need merging"
 '
 
-test_expect_success 'mergetool merges all from subdir' '
+test_expect_success 'mergetool merges all from subdir (rerere disabled)' '
+       test_when_finished "git reset --hard" &&
+       git checkout -b test$test_count branch1 &&
        test_config rerere.enabled false &&
        (
                cd subdir &&
@@ -189,21 +234,41 @@ test_expect_success 'mergetool merges all from subdir' '
        )
 '
 
+test_expect_success 'mergetool merges all from subdir (rerere enabled)' '
+       test_when_finished "git reset --hard" &&
+       git checkout -b test$test_count branch1 &&
+       test_config rerere.enabled true &&
+       rm -rf .git/rr-cache &&
+       (
+               cd subdir &&
+               test_must_fail git merge master &&
+               ( yes "r" | git mergetool ../submod ) &&
+               ( yes "d" "d" | git mergetool --no-prompt ) &&
+               test "$(cat ../file1)" = "master updated" &&
+               test "$(cat ../file2)" = "master new" &&
+               test "$(cat file3)" = "master new sub" &&
+               ( cd .. && git submodule update -N ) &&
+               test "$(cat ../submod/bar)" = "master submodule" &&
+               git commit -m "branch2 resolved by mergetool from subdir"
+       )
+'
+
 test_expect_success 'mergetool skips resolved paths when rerere is active' '
+       test_when_finished "git reset --hard" &&
        test_config rerere.enabled true &&
        rm -rf .git/rr-cache &&
-       git checkout -b test5 branch1 &&
+       git checkout -b test$test_count branch1 &&
        git submodule update -N &&
        test_must_fail git merge master >/dev/null 2>&1 &&
        ( yes "l" | git mergetool --no-prompt submod >/dev/null 2>&1 ) &&
        ( yes "d" "d" | git mergetool --no-prompt >/dev/null 2>&1 ) &&
        git submodule update -N &&
        output="$(yes "n" | git mergetool --no-prompt)" &&
-       test "$output" = "No files need merging" &&
-       git reset --hard
+       test "$output" = "No files need merging"
 '
 
 test_expect_success 'conflicted stash sets up rerere'  '
+       test_when_finished "git reset --hard" &&
        test_config rerere.enabled true &&
        git checkout stash1 &&
        echo "Conflicting stash content" >file11 &&
@@ -231,67 +296,57 @@ test_expect_success 'conflicted stash sets up rerere'  '
 '
 
 test_expect_success 'mergetool takes partial path' '
-       git reset --hard &&
+       test_when_finished "git reset --hard" &&
        test_config rerere.enabled false &&
-       git checkout -b test12 branch1 &&
+       git checkout -b test$test_count branch1 &&
        git submodule update -N &&
        test_must_fail git merge master &&
 
        ( yes "" | git mergetool subdir ) &&
 
-       test "$(cat subdir/file3)" = "master new sub" &&
-       git reset --hard
+       test "$(cat subdir/file3)" = "master new sub"
 '
 
 test_expect_success 'mergetool delete/delete conflict' '
-       git checkout -b delete-base branch1 &&
-       mkdir -p a/a &&
-       (echo one; echo two; echo 3; echo 4) >a/a/file.txt &&
-       git add a/a/file.txt &&
-       git commit -m"base file" &&
-       git checkout -b move-to-b delete-base &&
-       mkdir -p b/b &&
-       git mv a/a/file.txt b/b/file.txt &&
-       (echo one; echo two; echo 4) >b/b/file.txt &&
-       git commit -a -m"move to b" &&
-       git checkout -b move-to-c delete-base &&
-       mkdir -p c/c &&
-       git mv a/a/file.txt c/c/file.txt &&
-       (echo one; echo two; echo 3) >c/c/file.txt &&
-       git commit -a -m"move to c" &&
+       test_when_finished "git reset --hard" &&
+       git checkout -b test$test_count move-to-c &&
        test_must_fail git merge move-to-b &&
        echo d | git mergetool a/a/file.txt &&
        ! test -f a/a/file.txt &&
-       git reset --hard HEAD &&
+       git reset --hard &&
        test_must_fail git merge move-to-b &&
        echo m | git mergetool a/a/file.txt &&
        test -f b/b/file.txt &&
-       git reset --hard HEAD &&
+       git reset --hard &&
        test_must_fail git merge move-to-b &&
        ! echo a | git mergetool a/a/file.txt &&
-       ! test -f a/a/file.txt &&
-       git reset --hard HEAD
+       ! test -f a/a/file.txt
 '
 
 test_expect_success 'mergetool produces no errors when keepBackup is used' '
+       test_when_finished "git reset --hard" &&
+       git checkout -b test$test_count move-to-c &&
        test_config mergetool.keepBackup true &&
        test_must_fail git merge move-to-b &&
        : >expect &&
        echo d | git mergetool a/a/file.txt 2>actual &&
        test_cmp expect actual &&
-       ! test -d a &&
-       git reset --hard HEAD
+       ! test -d a
 '
 
 test_expect_success 'mergetool honors tempfile config for deleted files' '
+       test_when_finished "git reset --hard" &&
+       git checkout -b test$test_count move-to-c &&
        test_config mergetool.keepTemporaries false &&
        test_must_fail git merge move-to-b &&
        echo d | git mergetool a/a/file.txt &&
-       ! test -d a &&
-       git reset --hard HEAD
+       ! test -d a
 '
 
 test_expect_success 'mergetool keeps tempfiles when aborting delete/delete' '
+       test_when_finished "git reset --hard" &&
+       test_when_finished "git clean -fdx" &&
+       git checkout -b test$test_count move-to-c &&
        test_config mergetool.keepTemporaries true &&
        test_must_fail git merge move-to-b &&
        ! (echo a; echo n) | git mergetool a/a/file.txt &&
@@ -302,18 +357,17 @@ test_expect_success 'mergetool keeps tempfiles when aborting delete/delete' '
        file_REMOTE_.txt
        EOF
        ls -1 a/a | sed -e "s/[0-9]*//g" >actual &&
-       test_cmp expect actual &&
-       git clean -fdx &&
-       git reset --hard HEAD
+       test_cmp expect actual
 '
 
 test_expect_success 'deleted vs modified submodule' '
-       git checkout -b test6 branch1 &&
+       test_when_finished "git reset --hard" &&
+       git checkout -b test$test_count branch1 &&
        git submodule update -N &&
        mv submod submod-movedaside &&
        git rm --cached submod &&
        git commit -m "Submodule deleted from branch" &&
-       git checkout -b test6.a test6 &&
+       git checkout -b test$test_count.a test$test_count &&
        test_must_fail git merge master &&
        test -n "$(git ls-files -u)" &&
        ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) &&
@@ -329,7 +383,7 @@ test_expect_success 'deleted vs modified submodule' '
        git commit -m "Merge resolved by keeping module" &&
 
        mv submod submod-movedaside &&
-       git checkout -b test6.b test6 &&
+       git checkout -b test$test_count.b test$test_count &&
        git submodule update -N &&
        test_must_fail git merge master &&
        test -n "$(git ls-files -u)" &&
@@ -343,9 +397,9 @@ test_expect_success 'deleted vs modified submodule' '
        git commit -m "Merge resolved by deleting module" &&
 
        mv submod-movedaside submod &&
-       git checkout -b test6.c master &&
+       git checkout -b test$test_count.c master &&
        git submodule update -N &&
-       test_must_fail git merge test6 &&
+       test_must_fail git merge test$test_count &&
        test -n "$(git ls-files -u)" &&
        ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) &&
        ( yes "" | git mergetool both >/dev/null 2>&1 ) &&
@@ -359,9 +413,9 @@ test_expect_success 'deleted vs modified submodule' '
        git commit -m "Merge resolved by deleting module" &&
        mv submod.orig submod &&
 
-       git checkout -b test6.d master &&
+       git checkout -b test$test_count.d master &&
        git submodule update -N &&
-       test_must_fail git merge test6 &&
+       test_must_fail git merge test$test_count &&
        test -n "$(git ls-files -u)" &&
        ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) &&
        ( yes "" | git mergetool both >/dev/null 2>&1 ) &&
@@ -372,19 +426,19 @@ test_expect_success 'deleted vs modified submodule' '
        test "$(cat submod/bar)" = "master submodule" &&
        output="$(git mergetool --no-prompt)" &&
        test "$output" = "No files need merging" &&
-       git commit -m "Merge resolved by keeping module" &&
-       git reset --hard HEAD
+       git commit -m "Merge resolved by keeping module"
 '
 
 test_expect_success 'file vs modified submodule' '
-       git checkout -b test7 branch1 &&
+       test_when_finished "git reset --hard" &&
+       git checkout -b test$test_count branch1 &&
        git submodule update -N &&
        mv submod submod-movedaside &&
        git rm --cached submod &&
        echo not a submodule >submod &&
        git add submod &&
        git commit -m "Submodule path becomes file" &&
-       git checkout -b test7.a branch1 &&
+       git checkout -b test$test_count.a branch1 &&
        test_must_fail git merge master &&
        test -n "$(git ls-files -u)" &&
        ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) &&
@@ -400,7 +454,7 @@ test_expect_success 'file vs modified submodule' '
        git commit -m "Merge resolved by keeping module" &&
 
        mv submod submod-movedaside &&
-       git checkout -b test7.b test7 &&
+       git checkout -b test$test_count.b test$test_count &&
        test_must_fail git merge master &&
        test -n "$(git ls-files -u)" &&
        ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) &&
@@ -413,11 +467,11 @@ test_expect_success 'file vs modified submodule' '
        test "$output" = "No files need merging" &&
        git commit -m "Merge resolved by keeping file" &&
 
-       git checkout -b test7.c master &&
+       git checkout -b test$test_count.c master &&
        rmdir submod && mv submod-movedaside submod &&
        test ! -e submod.orig &&
        git submodule update -N &&
-       test_must_fail git merge test7 &&
+       test_must_fail git merge test$test_count &&
        test -n "$(git ls-files -u)" &&
        ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) &&
        ( yes "" | git mergetool both >/dev/null 2>&1 ) &&
@@ -430,10 +484,10 @@ test_expect_success 'file vs modified submodule' '
        test "$output" = "No files need merging" &&
        git commit -m "Merge resolved by keeping file" &&
 
-       git checkout -b test7.d master &&
+       git checkout -b test$test_count.d master &&
        rmdir submod && mv submod.orig submod &&
        git submodule update -N &&
-       test_must_fail git merge test7 &&
+       test_must_fail git merge test$test_count &&
        test -n "$(git ls-files -u)" &&
        ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) &&
        ( yes "" | git mergetool both>/dev/null 2>&1 ) &&
@@ -448,7 +502,8 @@ test_expect_success 'file vs modified submodule' '
 '
 
 test_expect_success 'submodule in subdirectory' '
-       git checkout -b test10 branch1 &&
+       test_when_finished "git reset --hard" &&
+       git checkout -b test$test_count branch1 &&
        git submodule update -N &&
        (
                cd subdir &&
@@ -460,56 +515,57 @@ test_expect_success 'submodule in subdirectory' '
                git commit -m "add initial versions"
                )
        ) &&
+       test_when_finished "rm -rf subdir/subdir_module" &&
        git submodule add git://example.com/subsubmodule subdir/subdir_module &&
        git add subdir/subdir_module &&
        git commit -m "add submodule in subdirectory" &&
 
-       git checkout -b test10.a test10 &&
+       git checkout -b test$test_count.a test$test_count &&
        git submodule update -N &&
        (
        cd subdir/subdir_module &&
                git checkout -b super10.a &&
-               echo test10.a >file15 &&
+               echo test$test_count.a >file15 &&
                git add file15 &&
                git commit -m "on branch 10.a"
        ) &&
        git add subdir/subdir_module &&
-       git commit -m "change submodule in subdirectory on test10.a" &&
+       git commit -m "change submodule in subdirectory on test$test_count.a" &&
 
-       git checkout -b test10.b test10 &&
+       git checkout -b test$test_count.b test$test_count &&
        git submodule update -N &&
        (
                cd subdir/subdir_module &&
                git checkout -b super10.b &&
-               echo test10.b >file15 &&
+               echo test$test_count.b >file15 &&
                git add file15 &&
                git commit -m "on branch 10.b"
        ) &&
        git add subdir/subdir_module &&
-       git commit -m "change submodule in subdirectory on test10.b" &&
+       git commit -m "change submodule in subdirectory on test$test_count.b" &&
 
-       test_must_fail git merge test10.a >/dev/null 2>&1 &&
+       test_must_fail git merge test$test_count.a >/dev/null 2>&1 &&
        (
                cd subdir &&
                ( yes "l" | git mergetool subdir_module )
        ) &&
-       test "$(cat subdir/subdir_module/file15)" = "test10.b" &&
+       test "$(cat subdir/subdir_module/file15)" = "test$test_count.b" &&
        git submodule update -N &&
-       test "$(cat subdir/subdir_module/file15)" = "test10.b" &&
+       test "$(cat subdir/subdir_module/file15)" = "test$test_count.b" &&
        git reset --hard &&
        git submodule update -N &&
 
-       test_must_fail git merge test10.a >/dev/null 2>&1 &&
+       test_must_fail git merge test$test_count.a >/dev/null 2>&1 &&
        ( yes "r" | git mergetool subdir/subdir_module ) &&
-       test "$(cat subdir/subdir_module/file15)" = "test10.b" &&
+       test "$(cat subdir/subdir_module/file15)" = "test$test_count.b" &&
        git submodule update -N &&
-       test "$(cat subdir/subdir_module/file15)" = "test10.a" &&
-       git commit -m "branch1 resolved with mergetool" &&
-       rm -rf subdir/subdir_module
+       test "$(cat subdir/subdir_module/file15)" = "test$test_count.a" &&
+       git commit -m "branch1 resolved with mergetool"
 '
 
 test_expect_success 'directory vs modified submodule' '
-       git checkout -b test11 branch1 &&
+       test_when_finished "git reset --hard" &&
+       git checkout -b test$test_count branch1 &&
        mv submod submod-movedaside &&
        git rm --cached submod &&
        mkdir submod &&
@@ -523,7 +579,7 @@ test_expect_success 'directory vs modified submodule' '
        test "$(cat submod/file16)" = "not a submodule" &&
        rm -rf submod.orig &&
 
-       git reset --hard >/dev/null 2>&1 &&
+       git reset --hard &&
        test_must_fail git merge master &&
        test -n "$(git ls-files -u)" &&
        test ! -e submod.orig &&
@@ -535,58 +591,59 @@ test_expect_success 'directory vs modified submodule' '
        ( cd submod && git clean -f && git reset --hard ) &&
        git submodule update -N &&
        test "$(cat submod/bar)" = "master submodule" &&
-       git reset --hard >/dev/null 2>&1 && rm -rf submod-movedaside &&
+       git reset --hard &&
+       rm -rf submod-movedaside &&
 
-       git checkout -b test11.c master &&
+       git checkout -b test$test_count.c master &&
        git submodule update -N &&
-       test_must_fail git merge test11 &&
+       test_must_fail git merge test$test_count &&
        test -n "$(git ls-files -u)" &&
        ( yes "l" | git mergetool submod ) &&
        git submodule update -N &&
        test "$(cat submod/bar)" = "master submodule" &&
 
-       git reset --hard >/dev/null 2>&1 &&
+       git reset --hard &&
        git submodule update -N &&
-       test_must_fail git merge test11 &&
+       test_must_fail git merge test$test_count &&
        test -n "$(git ls-files -u)" &&
        test ! -e submod.orig &&
        ( yes "r" | git mergetool submod ) &&
        test "$(cat submod/file16)" = "not a submodule" &&
 
-       git reset --hard master >/dev/null 2>&1 &&
+       git reset --hard master &&
        ( cd submod && git clean -f && git reset --hard ) &&
        git submodule update -N
 '
 
 test_expect_success 'file with no base' '
-       git checkout -b test13 branch1 &&
+       test_when_finished "git reset --hard" &&
+       git checkout -b test$test_count branch1 &&
        test_must_fail git merge master &&
        git mergetool --no-prompt --tool mybase -- both &&
        >expected &&
-       test_cmp both expected &&
-       git reset --hard master >/dev/null 2>&1
+       test_cmp both expected
 '
 
 test_expect_success 'custom commands override built-ins' '
-       git checkout -b test14 branch1 &&
+       test_when_finished "git reset --hard" &&
+       git checkout -b test$test_count branch1 &&
        test_config mergetool.defaults.cmd "cat \"\$REMOTE\" >\"\$MERGED\"" &&
        test_config mergetool.defaults.trustExitCode true &&
        test_must_fail git merge master &&
        git mergetool --no-prompt --tool defaults -- both &&
        echo master both added >expected &&
-       test_cmp both expected &&
-       git reset --hard master >/dev/null 2>&1
+       test_cmp both expected
 '
 
 test_expect_success 'filenames seen by tools start with ./' '
-       git checkout -b test15 branch1 &&
+       test_when_finished "git reset --hard" &&
+       git checkout -b test$test_count branch1 &&
        test_config mergetool.writeToTemp false &&
        test_config mergetool.myecho.cmd "echo \"\$LOCAL\"" &&
        test_config mergetool.myecho.trustExitCode true &&
        test_must_fail git merge master &&
        git mergetool --no-prompt --tool myecho -- both >actual &&
-       grep ^\./both_LOCAL_ actual >/dev/null &&
-       git reset --hard master >/dev/null 2>&1
+       grep ^\./both_LOCAL_ actual >/dev/null
 '
 
 test_lazy_prereq MKTEMP '
@@ -596,53 +653,48 @@ test_lazy_prereq MKTEMP '
 '
 
 test_expect_success MKTEMP 'temporary filenames are used with mergetool.writeToTemp' '
-       git checkout -b test16 branch1 &&
+       test_when_finished "git reset --hard" &&
+       git checkout -b test$test_count branch1 &&
        test_config mergetool.writeToTemp true &&
        test_config mergetool.myecho.cmd "echo \"\$LOCAL\"" &&
        test_config mergetool.myecho.trustExitCode true &&
        test_must_fail git merge master &&
        git mergetool --no-prompt --tool myecho -- both >actual &&
-       test_must_fail grep ^\./both_LOCAL_ actual >/dev/null &&
-       grep /both_LOCAL_ actual >/dev/null &&
-       git reset --hard master >/dev/null 2>&1
+       ! grep ^\./both_LOCAL_ actual >/dev/null &&
+       grep /both_LOCAL_ actual >/dev/null
 '
 
 test_expect_success 'diff.orderFile configuration is honored' '
+       test_when_finished "git reset --hard" &&
+       git checkout -b test$test_count order-file-side2 &&
        test_config diff.orderFile order-file &&
        test_config mergetool.myecho.cmd "echo \"\$LOCAL\"" &&
        test_config mergetool.myecho.trustExitCode true &&
        echo b >order-file &&
        echo a >>order-file &&
-       git checkout -b order-file-start master &&
-       echo start >a &&
-       echo start >b &&
-       git add a b &&
-       git commit -m start &&
-       git checkout -b order-file-side1 order-file-start &&
-       echo side1 >a &&
-       echo side1 >b &&
-       git add a b &&
-       git commit -m side1 &&
-       git checkout -b order-file-side2 order-file-start &&
-       echo side2 >a &&
-       echo side2 >b &&
-       git add a b &&
-       git commit -m side2 &&
        test_must_fail git merge order-file-side1 &&
        cat >expect <<-\EOF &&
                Merging:
                b
                a
        EOF
+
+       # make sure "order-file" that is ambiguous between
+       # rev and path is understood correctly.
+       git branch order-file HEAD &&
+
        git mergetool --no-prompt --tool myecho >output &&
        git grep --no-index -h -A2 Merging: output >actual &&
-       test_cmp expect actual &&
-       git reset --hard >/dev/null
+       test_cmp expect actual
 '
 test_expect_success 'mergetool -Oorder-file is honored' '
+       test_when_finished "git reset --hard" &&
+       git checkout -b test$test_count order-file-side2 &&
        test_config diff.orderFile order-file &&
        test_config mergetool.myecho.cmd "echo \"\$LOCAL\"" &&
        test_config mergetool.myecho.trustExitCode true &&
+       echo b >order-file &&
+       echo a >>order-file &&
        test_must_fail git merge order-file-side1 &&
        cat >expect <<-\EOF &&
                Merging:
@@ -652,7 +704,7 @@ test_expect_success 'mergetool -Oorder-file is honored' '
        git mergetool -O/dev/null --no-prompt --tool myecho >output &&
        git grep --no-index -h -A2 Merging: output >actual &&
        test_cmp expect actual &&
-       git reset --hard >/dev/null 2>&1 &&
+       git reset --hard &&
 
        git config --unset diff.orderFile &&
        test_must_fail git merge order-file-side1 &&
@@ -663,8 +715,7 @@ test_expect_success 'mergetool -Oorder-file is honored' '
        EOF
        git mergetool -Oorder-file --no-prompt --tool myecho >output &&
        git grep --no-index -h -A2 Merging: output >actual &&
-       test_cmp expect actual &&
-       git reset --hard >/dev/null 2>&1
+       test_cmp expect actual
 '
 
 test_done
index de2405ccba29016d2b83e16482bcfcbe70429e46..19f0108f8a2c67c518a92d996be7a335dbb69af2 100755 (executable)
@@ -39,6 +39,10 @@ test_expect_success setup '
                echo "a+bc"
                echo "abc"
        } >ab &&
+       {
+               echo d &&
+               echo 0
+       } >d0 &&
        echo vvv >v &&
        echo ww w >w &&
        echo x x xx x >x &&
@@ -1105,36 +1109,36 @@ test_expect_success 'grep pattern with grep.patternType=fixed, =basic, =extended
 '
 
 test_expect_success 'grep -G -F -P -E pattern' '
-       >empty &&
-       test_must_fail git grep -G -F -P -E "a\x{2b}b\x{2a}c" ab >actual &&
-       test_cmp empty actual
+       echo "d0:d" >expected &&
+       git grep -G -F -P -E "[\d]" d0 >actual &&
+       test_cmp expected actual
 '
 
 test_expect_success 'grep pattern with grep.patternType=fixed, =basic, =perl, =extended' '
-       >empty &&
-       test_must_fail git \
+       echo "d0:d" >expected &&
+       git \
                -c grep.patterntype=fixed \
                -c grep.patterntype=basic \
                -c grep.patterntype=perl \
                -c grep.patterntype=extended \
-               grep "a\x{2b}b\x{2a}c" ab >actual &&
-       test_cmp empty actual
+               grep "[\d]" d0 >actual &&
+       test_cmp expected actual
 '
 
 test_expect_success LIBPCRE 'grep -G -F -E -P pattern' '
-       echo "ab:a+b*c" >expected &&
-       git grep -G -F -E -P "a\x{2b}b\x{2a}c" ab >actual &&
+       echo "d0:0" >expected &&
+       git grep -G -F -E -P "[\d]" d0 >actual &&
        test_cmp expected actual
 '
 
 test_expect_success LIBPCRE 'grep pattern with grep.patternType=fixed, =basic, =extended, =perl' '
-       echo "ab:a+b*c" >expected &&
+       echo "d0:0" >expected &&
        git \
                -c grep.patterntype=fixed \
                -c grep.patterntype=basic \
                -c grep.patterntype=extended \
                -c grep.patterntype=perl \
-               grep "a\x{2b}b\x{2a}c" ab >actual &&
+               grep "[\d]" d0 >actual &&
        test_cmp expected actual
 '
 
diff --git a/t/t7814-grep-recurse-submodules.sh b/t/t7814-grep-recurse-submodules.sh
new file mode 100755 (executable)
index 0000000..67247a0
--- /dev/null
@@ -0,0 +1,241 @@
+#!/bin/sh
+
+test_description='Test grep recurse-submodules feature
+
+This test verifies the recurse-submodules feature correctly greps across
+submodules.
+'
+
+. ./test-lib.sh
+
+test_expect_success 'setup directory structure and submodule' '
+       echo "foobar" >a &&
+       mkdir b &&
+       echo "bar" >b/b &&
+       git add a b &&
+       git commit -m "add a and b" &&
+       git init submodule &&
+       echo "foobar" >submodule/a &&
+       git -C submodule add a &&
+       git -C submodule commit -m "add a" &&
+       git submodule add ./submodule &&
+       git commit -m "added submodule"
+'
+
+test_expect_success 'grep correctly finds patterns in a submodule' '
+       cat >expect <<-\EOF &&
+       a:foobar
+       b/b:bar
+       submodule/a:foobar
+       EOF
+
+       git grep -e "bar" --recurse-submodules >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'grep and basic pathspecs' '
+       cat >expect <<-\EOF &&
+       submodule/a:foobar
+       EOF
+
+       git grep -e. --recurse-submodules -- submodule >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'grep and nested submodules' '
+       git init submodule/sub &&
+       echo "foobar" >submodule/sub/a &&
+       git -C submodule/sub add a &&
+       git -C submodule/sub commit -m "add a" &&
+       git -C submodule submodule add ./sub &&
+       git -C submodule add sub &&
+       git -C submodule commit -m "added sub" &&
+       git add submodule &&
+       git commit -m "updated submodule" &&
+
+       cat >expect <<-\EOF &&
+       a:foobar
+       b/b:bar
+       submodule/a:foobar
+       submodule/sub/a:foobar
+       EOF
+
+       git grep -e "bar" --recurse-submodules >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'grep and multiple patterns' '
+       cat >expect <<-\EOF &&
+       a:foobar
+       submodule/a:foobar
+       submodule/sub/a:foobar
+       EOF
+
+       git grep -e "bar" --and -e "foo" --recurse-submodules >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'grep and multiple patterns' '
+       cat >expect <<-\EOF &&
+       b/b:bar
+       EOF
+
+       git grep -e "bar" --and --not -e "foo" --recurse-submodules >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'basic grep tree' '
+       cat >expect <<-\EOF &&
+       HEAD:a:foobar
+       HEAD:b/b:bar
+       HEAD:submodule/a:foobar
+       HEAD:submodule/sub/a:foobar
+       EOF
+
+       git grep -e "bar" --recurse-submodules HEAD >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'grep tree HEAD^' '
+       cat >expect <<-\EOF &&
+       HEAD^:a:foobar
+       HEAD^:b/b:bar
+       HEAD^:submodule/a:foobar
+       EOF
+
+       git grep -e "bar" --recurse-submodules HEAD^ >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'grep tree HEAD^^' '
+       cat >expect <<-\EOF &&
+       HEAD^^:a:foobar
+       HEAD^^:b/b:bar
+       EOF
+
+       git grep -e "bar" --recurse-submodules HEAD^^ >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'grep tree and pathspecs' '
+       cat >expect <<-\EOF &&
+       HEAD:submodule/a:foobar
+       HEAD:submodule/sub/a:foobar
+       EOF
+
+       git grep -e "bar" --recurse-submodules HEAD -- submodule >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'grep tree and pathspecs' '
+       cat >expect <<-\EOF &&
+       HEAD:submodule/a:foobar
+       HEAD:submodule/sub/a:foobar
+       EOF
+
+       git grep -e "bar" --recurse-submodules HEAD -- "submodule*a" >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'grep tree and more pathspecs' '
+       cat >expect <<-\EOF &&
+       HEAD:submodule/a:foobar
+       EOF
+
+       git grep -e "bar" --recurse-submodules HEAD -- "submodul?/a" >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'grep tree and more pathspecs' '
+       cat >expect <<-\EOF &&
+       HEAD:submodule/sub/a:foobar
+       EOF
+
+       git grep -e "bar" --recurse-submodules HEAD -- "submodul*/sub/a" >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success !MINGW 'grep recurse submodule colon in name' '
+       git init parent &&
+       test_when_finished "rm -rf parent" &&
+       echo "foobar" >"parent/fi:le" &&
+       git -C parent add "fi:le" &&
+       git -C parent commit -m "add fi:le" &&
+
+       git init "su:b" &&
+       test_when_finished "rm -rf su:b" &&
+       echo "foobar" >"su:b/fi:le" &&
+       git -C "su:b" add "fi:le" &&
+       git -C "su:b" commit -m "add fi:le" &&
+
+       git -C parent submodule add "../su:b" "su:b" &&
+       git -C parent commit -m "add submodule" &&
+
+       cat >expect <<-\EOF &&
+       fi:le:foobar
+       su:b/fi:le:foobar
+       EOF
+       git -C parent grep -e "foobar" --recurse-submodules >actual &&
+       test_cmp expect actual &&
+
+       cat >expect <<-\EOF &&
+       HEAD:fi:le:foobar
+       HEAD:su:b/fi:le:foobar
+       EOF
+       git -C parent grep -e "foobar" --recurse-submodules HEAD >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'grep history with moved submoules' '
+       git init parent &&
+       test_when_finished "rm -rf parent" &&
+       echo "foobar" >parent/file &&
+       git -C parent add file &&
+       git -C parent commit -m "add file" &&
+
+       git init sub &&
+       test_when_finished "rm -rf sub" &&
+       echo "foobar" >sub/file &&
+       git -C sub add file &&
+       git -C sub commit -m "add file" &&
+
+       git -C parent submodule add ../sub dir/sub &&
+       git -C parent commit -m "add submodule" &&
+
+       cat >expect <<-\EOF &&
+       dir/sub/file:foobar
+       file:foobar
+       EOF
+       git -C parent grep -e "foobar" --recurse-submodules >actual &&
+       test_cmp expect actual &&
+
+       git -C parent mv dir/sub sub-moved &&
+       git -C parent commit -m "moved submodule" &&
+
+       cat >expect <<-\EOF &&
+       file:foobar
+       sub-moved/file:foobar
+       EOF
+       git -C parent grep -e "foobar" --recurse-submodules >actual &&
+       test_cmp expect actual &&
+
+       cat >expect <<-\EOF &&
+       HEAD^:dir/sub/file:foobar
+       HEAD^:file:foobar
+       EOF
+       git -C parent grep -e "foobar" --recurse-submodules HEAD^ >actual &&
+       test_cmp expect actual
+'
+
+test_incompatible_with_recurse_submodules ()
+{
+       test_expect_success "--recurse-submodules and $1 are incompatible" "
+               test_must_fail git grep -e. --recurse-submodules $1 2>actual &&
+               test_i18ngrep 'not supported with --recurse-submodules' actual
+       "
+}
+
+test_incompatible_with_recurse_submodules --untracked
+test_incompatible_with_recurse_submodules --no-index
+
+test_done
index ab79de95441f4f474525b48b14313f1d04172fce..380e1c1054de5d55a80cb558fea0791884ad7f38 100755 (executable)
@@ -86,4 +86,36 @@ test_expect_success 'blame with showEmail config true' '
        test_cmp expected_n result
 '
 
+test_expect_success 'set up abbrev tests' '
+       test_commit abbrev &&
+       sha1=$(git rev-parse --verify HEAD) &&
+       check_abbrev () {
+               expect=$1; shift
+               echo $sha1 | cut -c 1-$expect >expect &&
+               git blame "$@" abbrev.t >actual &&
+               perl -lne "/[0-9a-f]+/ and print \$&" <actual >actual.sha &&
+               test_cmp expect actual.sha
+       }
+'
+
+test_expect_success 'blame --abbrev=<n> works' '
+       # non-boundary commits get +1 for alignment
+       check_abbrev 31 --abbrev=30 HEAD &&
+       check_abbrev 30 --abbrev=30 ^HEAD
+'
+
+test_expect_success 'blame -l aligns regular and boundary commits' '
+       check_abbrev 40 -l HEAD &&
+       check_abbrev 39 -l ^HEAD
+'
+
+test_expect_success 'blame --abbrev=40 behaves like -l' '
+       check_abbrev 40 --abbrev=40 HEAD &&
+       check_abbrev 39 --abbrev=40 ^HEAD
+'
+
+test_expect_success '--no-abbrev works like --abbrev=40' '
+       check_abbrev 40 --no-abbrev
+'
+
 test_done
diff --git a/t/t8011-blame-split-file.sh b/t/t8011-blame-split-file.sh
new file mode 100755 (executable)
index 0000000..8311250
--- /dev/null
@@ -0,0 +1,117 @@
+#!/bin/sh
+
+test_description='
+The general idea is that we have a single file whose lines come from
+multiple other files, and those individual files were modified in the same
+commits. That means that we will see the same commit in multiple contexts,
+and each one should be attributed to the correct file.
+
+Note that we need to use "blame -C" to find the commit for all lines. We will
+not bother testing that the non-C case fails to find it. That is how blame
+behaves now, but it is not a property we want to make sure is retained.
+'
+. ./test-lib.sh
+
+# help avoid typing and reading long strings of similar lines
+# in the tests below
+generate_expect () {
+       while read nr data
+       do
+               i=0
+               while test $i -lt $nr
+               do
+                       echo $data
+                       i=$((i + 1))
+               done
+       done
+}
+
+test_expect_success 'setup split file case' '
+       # use lines long enough to trigger content detection
+       test_seq 1000 1010 >one &&
+       test_seq 2000 2010 >two &&
+       git add one two &&
+       test_commit base &&
+
+       sed "6s/^/modified /" <one >one.tmp &&
+       mv one.tmp one &&
+       sed "6s/^/modified /" <two >two.tmp &&
+       mv two.tmp two &&
+       git add -u &&
+       test_commit modified &&
+
+       cat one two >combined &&
+       git add combined &&
+       git rm one two &&
+       test_commit combined
+'
+
+test_expect_success 'setup simulated porcelain' '
+       # This just reads porcelain-ish output and tries
+       # to output the value of a given field for each line (either by
+       # reading the field that accompanies this line, or referencing
+       # the information found last time the commit was mentioned).
+       cat >read-porcelain.pl <<-\EOF
+       my $field = shift;
+       while (<>) {
+               if (/^[0-9a-f]{40} /) {
+                       flush();
+                       $hash = $&;
+               } elsif (/^$field (.*)/) {
+                       $cache{$hash} = $1;
+               }
+       }
+       flush();
+
+       sub flush {
+               return unless defined $hash;
+               if (defined $cache{$hash}) {
+                       print "$cache{$hash}\n";
+               } else {
+                       print "NONE\n";
+               }
+       }
+       EOF
+'
+
+for output in porcelain line-porcelain
+do
+       test_expect_success "generate --$output output" '
+               git blame --root -C --$output combined >output
+       '
+
+       test_expect_success "$output output finds correct commits" '
+               generate_expect >expect <<-\EOF &&
+               5 base
+               1 modified
+               10 base
+               1 modified
+               5 base
+               EOF
+               perl read-porcelain.pl summary <output >actual &&
+               test_cmp expect actual
+       '
+
+       test_expect_success "$output output shows correct filenames" '
+               generate_expect >expect <<-\EOF &&
+               11 one
+               11 two
+               EOF
+               perl read-porcelain.pl filename <output >actual &&
+               test_cmp expect actual
+       '
+
+       test_expect_success "$output output shows correct previous pointer" '
+               generate_expect >expect <<-EOF &&
+               5 NONE
+               1 $(git rev-parse modified^) one
+               10 NONE
+               1 $(git rev-parse modified^) two
+               5 NONE
+               EOF
+               perl read-porcelain.pl previous <output >actual &&
+               test_cmp expect actual
+       '
+done
+
+test_done
index 3dc4a3454d223d37e85de6fa4ae8656046370e99..0f398dd1603d941e158fd39faa0a218428224872 100755 (executable)
@@ -50,7 +50,7 @@ test_no_confirm () {
                --smtp-server="$(pwd)/fake.sendmail" \
                $@ \
                $patches >stdout &&
-               test_must_fail grep "Send this email" stdout &&
+               ! grep "Send this email" stdout &&
                >no_confirm_okay
 }
 
index 69a675052e2099f278c98ddf8bba12658741fba4..044f65e91660b79a89601c3613eb620807e48bb1 100755 (executable)
@@ -55,7 +55,7 @@ test_expect_success 'clone to target directory with --stdlayout' '
 test_expect_success 'init without -s/-T/-b/-t does not warn' '
        test ! -d trunk &&
        git svn init "$svnrepo"/project/trunk trunk 2>warning &&
-       test_must_fail grep -q prefix warning &&
+       ! grep -q prefix warning &&
        rm -rf trunk &&
        rm -f warning
        '
@@ -63,7 +63,7 @@ test_expect_success 'init without -s/-T/-b/-t does not warn' '
 test_expect_success 'clone without -s/-T/-b/-t does not warn' '
        test ! -d trunk &&
        git svn clone "$svnrepo"/project/trunk 2>warning &&
-       test_must_fail grep -q prefix warning &&
+       ! grep -q prefix warning &&
        rm -rf trunk &&
        rm -f warning
        '
@@ -86,7 +86,7 @@ EOF
 test_expect_success 'init with -s/-T/-b/-t assumes --prefix=origin/' '
        test ! -d project &&
        git svn init -s "$svnrepo"/project project 2>warning &&
-       test_must_fail grep -q prefix warning &&
+       ! grep -q prefix warning &&
        test_svn_configured_prefix "origin/" &&
        rm -rf project &&
        rm -f warning
@@ -95,7 +95,7 @@ test_expect_success 'init with -s/-T/-b/-t assumes --prefix=origin/' '
 test_expect_success 'clone with -s/-T/-b/-t assumes --prefix=origin/' '
        test ! -d project &&
        git svn clone -s "$svnrepo"/project 2>warning &&
-       test_must_fail grep -q prefix warning &&
+       ! grep -q prefix warning &&
        test_svn_configured_prefix "origin/" &&
        rm -rf project &&
        rm -f warning
@@ -104,7 +104,7 @@ test_expect_success 'clone with -s/-T/-b/-t assumes --prefix=origin/' '
 test_expect_success 'init with -s/-T/-b/-t and --prefix "" still works' '
        test ! -d project &&
        git svn init -s "$svnrepo"/project project --prefix "" 2>warning &&
-       test_must_fail grep -q prefix warning &&
+       ! grep -q prefix warning &&
        test_svn_configured_prefix "" &&
        rm -rf project &&
        rm -f warning
@@ -113,7 +113,7 @@ test_expect_success 'init with -s/-T/-b/-t and --prefix "" still works' '
 test_expect_success 'clone with -s/-T/-b/-t and --prefix "" still works' '
        test ! -d project &&
        git svn clone -s "$svnrepo"/project --prefix "" 2>warning &&
-       test_must_fail grep -q prefix warning &&
+       ! grep -q prefix warning &&
        test_svn_configured_prefix "" &&
        rm -rf project &&
        rm -f warning
index 0fe23128070745750b54843bfb05a7822f41c285..bda222aa0270f3a93fa494b308aa174ebc942eca 100755 (executable)
@@ -118,21 +118,21 @@ test_expect_success 'not preserving user with mixed authorship' '
                make_change_by_user usernamefile3 Derek derek@example.com &&
                P4EDITOR=cat P4USER=alice P4PASSWD=secret &&
                export P4EDITOR P4USER P4PASSWD &&
-               git p4 commit |\
-               grep "git author derek@example.com does not match" &&
+               git p4 commit >actual &&
+               grep "git author derek@example.com does not match" actual &&
 
                make_change_by_user usernamefile3 Charlie charlie@example.com &&
-               git p4 commit |\
-               grep "git author charlie@example.com does not match" &&
+               git p4 commit >actual &&
+               grep "git author charlie@example.com does not match" actual &&
 
                make_change_by_user usernamefile3 alice alice@example.com &&
-               git p4 commit |\
-               test_must_fail grep "git author.*does not match" &&
+               git p4 commit >actual &&
+               ! grep "git author.*does not match" actual &&
 
                git config git-p4.skipUserNameCheck true &&
                make_change_by_user usernamefile3 Charlie charlie@example.com &&
-               git p4 commit |\
-               test_must_fail grep "git author.*does not match" &&
+               git p4 commit >actual &&
+               ! grep "git author.*does not match" actual &&
 
                p4_check_commit_author usernamefile3 alice
        )
index c89992cf95c7fab5b876f98bb5518bfe39344f4a..e7e0268e985072b6a7d8c9448dc0c3d9a9057da9 100755 (executable)
@@ -141,7 +141,7 @@ test_expect_success 'detect copies' '
                git diff-tree -r -C HEAD &&
                git p4 submit &&
                p4 filelog //depot/file8 &&
-               p4 filelog //depot/file8 | test_must_fail grep -q "branch from" &&
+               ! p4 filelog //depot/file8 | grep -q "branch from" &&
 
                echo "file9" >>file2 &&
                git commit -a -m "Differentiate file2" &&
@@ -154,7 +154,7 @@ test_expect_success 'detect copies' '
                git config git-p4.detectCopies true &&
                git p4 submit &&
                p4 filelog //depot/file9 &&
-               p4 filelog //depot/file9 | test_must_fail grep -q "branch from" &&
+               ! p4 filelog //depot/file9 | grep -q "branch from" &&
 
                echo "file10" >>file2 &&
                git commit -a -m "Differentiate file2" &&
@@ -202,7 +202,7 @@ test_expect_success 'detect copies' '
                git config git-p4.detectCopies $(($level + 2)) &&
                git p4 submit &&
                p4 filelog //depot/file12 &&
-               p4 filelog //depot/file12 | test_must_fail grep -q "branch from" &&
+               ! p4 filelog //depot/file12 | grep -q "branch from" &&
 
                echo "file13" >>file2 &&
                git commit -a -m "Differentiate file2" &&
index 3e8799a6111b3c8672e4068450c9d225f3fcccc5..c86ba2eb897f4ee2e45dadd418587ee4cb76401e 100644 (file)
@@ -1214,7 +1214,7 @@ static int refs_from_alternate_cb(struct alternate_object_database *e,
        const struct ref *extra;
        struct alternate_refs_data *cb = data;
 
-       other = xstrdup(real_path(e->path));
+       other = real_pathdup(e->path);
        len = strlen(other);
 
        while (other[len-1] == '/')
index 828f4356be039bc07ec09665ac725341515a36ea..ff776056806dd40ddfb02f566b0cbd4222d0d970 100644 (file)
@@ -1004,6 +1004,19 @@ static enum interesting do_match(const struct name_entry *entry,
                                 */
                                if (ps->recursive && S_ISDIR(entry->mode))
                                        return entry_interesting;
+
+                               /*
+                                * When matching against submodules with
+                                * wildcard characters, ensure that the entry
+                                * at least matches up to the first wild
+                                * character.  More accurate matching can then
+                                * be performed in the submodule itself.
+                                */
+                               if (ps->recursive && S_ISGITLINK(entry->mode) &&
+                                   !ps_strncmp(item, match + baselen,
+                                               entry->path,
+                                               item->nowildcard_len - baselen))
+                                       return entry_interesting;
                        }
 
                        continue;
@@ -1040,6 +1053,21 @@ static enum interesting do_match(const struct name_entry *entry,
                        strbuf_setlen(base, base_offset + baselen);
                        return entry_interesting;
                }
+
+               /*
+                * When matching against submodules with
+                * wildcard characters, ensure that the entry
+                * at least matches up to the first wild
+                * character.  More accurate matching can then
+                * be performed in the submodule itself.
+                */
+               if (ps->recursive && S_ISGITLINK(entry->mode) &&
+                   !ps_strncmp(item, match, base->buf + base_offset,
+                               item->nowildcard_len)) {
+                       strbuf_setlen(base, base_offset + baselen);
+                       return entry_interesting;
+               }
+
                strbuf_setlen(base, base_offset + baselen);
 
                /*
index 7a6df99d10e1dbabc9f7df74316fd91bd55c1fd5..129d49ff4517ae0f6c843a9f53c1617f3c2cb3ca 100644 (file)
@@ -218,29 +218,42 @@ static void unlink_entry(const struct cache_entry *ce)
        schedule_dir_for_removal(ce->name, ce_namelen(ce));
 }
 
-static int check_updates(struct unpack_trees_options *o,
-                        const struct checkout *state)
+static struct progress *get_progress(struct unpack_trees_options *o)
 {
        unsigned cnt = 0, total = 0;
+       struct index_state *index = &o->result;
+
+       if (!o->update || !o->verbose_update)
+               return NULL;
+
+       for (; cnt < index->cache_nr; cnt++) {
+               const struct cache_entry *ce = index->cache[cnt];
+               if (ce->ce_flags & (CE_UPDATE | CE_WT_REMOVE))
+                       total++;
+       }
+
+       return start_progress_delay(_("Checking out files"),
+                                   total, 50, 1);
+}
+
+static int check_updates(struct unpack_trees_options *o)
+{
+       unsigned cnt = 0;
+       int errs = 0;
        struct progress *progress = NULL;
        struct index_state *index = &o->result;
+       struct checkout state = CHECKOUT_INIT;
        int i;
-       int errs = 0;
 
-       if (o->update && o->verbose_update) {
-               for (total = cnt = 0; cnt < index->cache_nr; cnt++) {
-                       const struct cache_entry *ce = index->cache[cnt];
-                       if (ce->ce_flags & (CE_UPDATE | CE_WT_REMOVE))
-                               total++;
-               }
+       state.force = 1;
+       state.quiet = 1;
+       state.refresh_cache = 1;
+       state.istate = index;
 
-               progress = start_progress_delay(_("Checking out files"),
-                                               total, 50, 1);
-               cnt = 0;
-       }
+       progress = get_progress(o);
 
        if (o->update)
-               git_attr_set_direction(GIT_ATTR_CHECKOUT, &o->result);
+               git_attr_set_direction(GIT_ATTR_CHECKOUT, index);
        for (i = 0; i < index->cache_nr; i++) {
                const struct cache_entry *ce = index->cache[i];
 
@@ -248,10 +261,9 @@ static int check_updates(struct unpack_trees_options *o,
                        display_progress(progress, ++cnt);
                        if (o->update && !o->dry_run)
                                unlink_entry(ce);
-                       continue;
                }
        }
-       remove_marked_cache_entries(&o->result);
+       remove_marked_cache_entries(index);
        remove_scheduled_dirs();
 
        for (i = 0; i < index->cache_nr; i++) {
@@ -264,7 +276,7 @@ static int check_updates(struct unpack_trees_options *o,
                        display_progress(progress, ++cnt);
                        ce->ce_flags &= ~CE_UPDATE;
                        if (o->update && !o->dry_run) {
-                               errs |= checkout_entry(ce, state, NULL);
+                               errs |= checkout_entry(ce, &state, NULL);
                        }
                }
        }
@@ -1094,14 +1106,9 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
        int i, ret;
        static struct cache_entry *dfc;
        struct exclude_list el;
-       struct checkout state = CHECKOUT_INIT;
 
        if (len > MAX_UNPACK_TREES)
                die("unpack_trees takes at most %d trees", MAX_UNPACK_TREES);
-       state.force = 1;
-       state.quiet = 1;
-       state.refresh_cache = 1;
-       state.istate = &o->result;
 
        memset(&el, 0, sizeof(el));
        if (!core_apply_sparse_checkout || !o->update)
@@ -1238,7 +1245,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
        }
 
        o->src_index = NULL;
-       ret = check_updates(o, &state) ? (-2) : 0;
+       ret = check_updates(o) ? (-2) : 0;
        if (o->dst_index) {
                if (!ret) {
                        if (!o->result.cache_tree)
index 80bfd109fa1285a24dbfa8e9aabdfc3b3bd59801..9f81dc1062bc1f7f243afa8c0404ff5100fc0ef8 100644 (file)
 static const struct string_list *prereleases;
 static int initialized;
 
+struct suffix_match {
+       int conf_pos;
+       int start;
+       int len;
+};
+
+static void find_better_matching_suffix(const char *tagname, const char *suffix,
+                                       int suffix_len, int start, int conf_pos,
+                                       struct suffix_match *match)
+{
+       /*
+        * A better match either starts earlier or starts at the same offset
+        * but is longer.
+        */
+       int end = match->len < suffix_len ? match->start : match->start-1;
+       int i;
+       for (i = start; i <= end; i++)
+               if (starts_with(tagname + i, suffix)) {
+                       match->conf_pos = conf_pos;
+                       match->start = i;
+                       match->len = suffix_len;
+                       break;
+               }
+}
+
 /*
- * p1 and p2 point to the first different character in two strings. If
- * either p1 or p2 starts with a prerelease suffix, it will be forced
- * to be on top.
- *
- * If both p1 and p2 start with (different) suffix, the order is
- * determined by config file.
+ * off is the offset of the first different character in the two strings
+ * s1 and s2. If either s1 or s2 contains a prerelease suffix containing
+ * that offset or a suffix ends right before that offset, then that
+ * string will be forced to be on top.
  *
- * Note that we don't have to deal with the situation when both p1 and
- * p2 start with the same suffix because the common part is already
- * consumed by the caller.
+ * If both s1 and s2 contain a (different) suffix around that position,
+ * their order is determined by the order of those two suffixes in the
+ * configuration.
+ * If any of the strings contains more than one different suffixes around
+ * that position, then that string is sorted according to the contained
+ * suffix which starts at the earliest offset in that string.
+ * If more than one different contained suffixes start at that earliest
+ * offset, then that string is sorted according to the longest of those
+ * suffixes.
  *
  * Return non-zero if *diff contains the return value for versioncmp()
  */
-static int swap_prereleases(const void *p1_,
-                           const void *p2_,
+static int swap_prereleases(const char *s1,
+                           const char *s2,
+                           int off,
                            int *diff)
 {
-       const char *p1 = p1_;
-       const char *p2 = p2_;
-       int i, i1 = -1, i2 = -1;
+       int i;
+       struct suffix_match match1 = { -1, off, -1 };
+       struct suffix_match match2 = { -1, off, -1 };
 
        for (i = 0; i < prereleases->nr; i++) {
                const char *suffix = prereleases->items[i].string;
-               if (i1 == -1 && starts_with(p1, suffix))
-                       i1 = i;
-               if (i2 == -1 && starts_with(p2, suffix))
-                       i2 = i;
+               int start, suffix_len = strlen(suffix);
+               if (suffix_len < off)
+                       start = off - suffix_len;
+               else
+                       start = 0;
+               find_better_matching_suffix(s1, suffix, suffix_len, start,
+                                           i, &match1);
+               find_better_matching_suffix(s2, suffix, suffix_len, start,
+                                           i, &match2);
        }
-       if (i1 == -1 && i2 == -1)
+       if (match1.conf_pos == -1 && match2.conf_pos == -1)
                return 0;
-       if (i1 >= 0 && i2 >= 0)
-               *diff = i1 - i2;
-       else if (i1 >= 0)
+       if (match1.conf_pos == match2.conf_pos)
+               /* Found the same suffix in both, e.g. "-rc" in "v1.0-rcX"
+                * and "v1.0-rcY": the caller should decide based on "X"
+                * and "Y". */
+               return 0;
+
+       if (match1.conf_pos >= 0 && match2.conf_pos >= 0)
+               *diff = match1.conf_pos - match2.conf_pos;
+       else if (match1.conf_pos >= 0)
                *diff = -1;
-       else /* if (i2 >= 0) */
+       else /* if (match2.conf_pos >= 0) */
                *diff = 1;
        return 1;
 }
@@ -118,10 +159,18 @@ int versioncmp(const char *s1, const char *s2)
        }
 
        if (!initialized) {
+               const struct string_list *deprecated_prereleases;
                initialized = 1;
-               prereleases = git_config_get_value_multi("versionsort.prereleasesuffix");
+               prereleases = git_config_get_value_multi("versionsort.suffix");
+               deprecated_prereleases = git_config_get_value_multi("versionsort.prereleasesuffix");
+               if (prereleases) {
+                       if (deprecated_prereleases)
+                               warning("ignoring versionsort.prereleasesuffix because versionsort.suffix is set");
+               } else
+                       prereleases = deprecated_prereleases;
        }
-       if (prereleases && swap_prereleases(p1 - 1, p2 - 1, &diff))
+       if (prereleases && swap_prereleases(s1, s2, (const char *) p1 - s1 - 1,
+                                           &diff))
                return diff;
 
        state = result_type[state * 3 + (((c2 == '0') + (isdigit (c2) != 0)))];
index 828fd7a0ad4243749acffb9ef7885e95b8bb2bd0..53b4771c04a2b9de15379fdd213f20f213b1be60 100644 (file)
@@ -255,7 +255,7 @@ struct worktree *find_worktree(struct worktree **list,
                return wt;
 
        arg = prefix_filename(prefix, strlen(prefix), arg);
-       path = xstrdup(real_path(arg));
+       path = real_pathdup(arg);
        for (; *list; list++)
                if (!fspathcmp(path, real_path((*list)->path)))
                        break;