diff options
88 files changed, 4186 insertions, 2642 deletions
@@ -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> diff --git a/Documentation/Makefile b/Documentation/Makefile index b43d66eae6..a9fb497b83 100644 --- a/Documentation/Makefile +++ b/Documentation/Makefile @@ -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 index 0000000000..74b193f1a8 --- /dev/null +++ b/Documentation/RelNotes/2.11.1.txt @@ -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. diff --git a/Documentation/RelNotes/2.12.0.txt b/Documentation/RelNotes/2.12.0.txt index 2a19064f6e..efff5264d8 100644 --- a/Documentation/RelNotes/2.12.0.txt +++ b/Documentation/RelNotes/2.12.0.txt @@ -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). diff --git a/Documentation/git-grep.txt b/Documentation/git-grep.txt index 0ecea6e491..71f32f3508 100644 --- a/Documentation/git-grep.txt +++ b/Documentation/git-grep.txt @@ -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. diff --git a/Documentation/git-gui.txt b/Documentation/git-gui.txt index c1a3e8bf07..5f93f8003d 100644 --- a/Documentation/git-gui.txt +++ b/Documentation/git-gui.txt @@ -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:: diff --git a/Documentation/git-p4.txt b/Documentation/git-p4.txt index bae862ddcb..7436c64a95 100644 --- a/Documentation/git-p4.txt +++ b/Documentation/git-p4.txt @@ -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 ~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt index b6c6326cdc..7241e96893 100644 --- a/Documentation/git-rev-parse.txt +++ b/Documentation/git-rev-parse.txt @@ -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:: diff --git a/Documentation/git.txt b/Documentation/git.txt index ba222f68cc..4f208fab92 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -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] diff --git a/Documentation/giteveryday.txt b/Documentation/giteveryday.txt index 35473ad02f..10c8ff93c0 100644 --- a/Documentation/giteveryday.txt +++ b/Documentation/giteveryday.txt @@ -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]] diff --git a/Documentation/gitk.txt b/Documentation/gitk.txt index e382dd96df..ca96c281d1 100644 --- a/Documentation/gitk.txt +++ b/Documentation/gitk.txt @@ -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. diff --git a/Documentation/technical/api-setup.txt b/Documentation/technical/api-setup.txt index 540e455689..eb1fa9853e 100644 --- a/Documentation/technical/api-setup.txt +++ b/Documentation/technical/api-setup.txt @@ -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 diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt index 5e07454572..bc29298678 100644 --- a/Documentation/user-manual.txt +++ b/Documentation/user-manual.txt @@ -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: @@ -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,$^) \ @@ -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; } /* diff --git a/archive-zip.c b/archive-zip.c index 9db47357b0..b429a8d974 100644 --- a/archive-zip.c +++ b/archive-zip.c @@ -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); diff --git a/builtin/blame.c b/builtin/blame.c index ab54a5c1f4..126b8c9e5b 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -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 @@ parse_done: } 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); diff --git a/builtin/gc.c b/builtin/gc.c index 069950d0b4..331f219260 100644 --- a/builtin/gc.c +++ b/builtin/gc.c @@ -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)) diff --git a/builtin/grep.c b/builtin/grep.c index 8887b6addb..2c727ef499 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -18,12 +18,22 @@ #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(); diff --git a/builtin/init-db.c b/builtin/init-db.c index 2399b97d90..76d68fad00 100644 --- a/builtin/init-db.c +++ b/builtin/init-db.c @@ -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) diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c index 0e30d86230..d7ebeb4ce6 100644 --- a/builtin/ls-tree.c +++ b/builtin/ls-tree.c @@ -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++) diff --git a/builtin/mv.c b/builtin/mv.c index 43adf92ba6..61d20037ad 100644 --- a/builtin/mv.c +++ b/builtin/mv.c @@ -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]); diff --git a/builtin/repack.c b/builtin/repack.c index 80dd06b4a2..677bc7c81a 100644 --- a/builtin/repack.c +++ b/builtin/repack.c @@ -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()); diff --git a/builtin/rm.c b/builtin/rm.c index 7f15a3d7f8..452170a3ab 100644 --- a/builtin/rm.c +++ b/builtin/rm.c @@ -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(); } @@ -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 *); @@ -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 index 4e23c650fe..0000000000 --- a/contrib/gitview/gitview +++ /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 index 7b5f9002b9..0000000000 --- a/contrib/gitview/gitview.txt +++ /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 @@ -16,11 +16,6 @@ #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); diff --git a/environment.c b/environment.c index 4bce3eebfa..8a83101d04 100644 --- a/environment.c +++ b/environment.c @@ -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) diff --git a/git-mergetool.sh b/git-mergetool.sh index e52b4e4f24..c062e3de3a 100755 --- a/git-mergetool.sh +++ b/git-mergetool.sh @@ -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 @@ -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 diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index b0a6f2b7ba..4734094a3f 100644 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -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 \ @@ -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) diff --git a/gitk-git/Makefile b/gitk-git/Makefile index 5acdc900ab..5bdd52a6eb 100644 --- a/gitk-git/Makefile +++ b/gitk-git/Makefile @@ -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 diff --git a/gitk-git/gitk b/gitk-git/gitk index 805a1c7030..a14d7a16b2 100755 --- a/gitk-git/gitk +++ b/gitk-git/gitk @@ -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 diff --git a/gitk-git/po/bg.po b/gitk-git/po/bg.po index 99aa77aa63..407d5550b1 100644 --- a/gitk-git/po/bg.po +++ b/gitk-git/po/bg.po @@ -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" "Използвайте и разпространявайте при условията на ОПЛ на ГНУ" diff --git a/gitk-git/po/ca.po b/gitk-git/po/ca.po index 5ad066f7ce..87dfc18b44 100644 --- a/gitk-git/po/ca.po +++ b/gitk-git/po/ca.po @@ -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" diff --git a/gitk-git/po/de.po b/gitk-git/po/de.po index bde749ed8a..5db3824828 100644 --- a/gitk-git/po/de.po +++ b/gitk-git/po/de.po @@ -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" diff --git a/gitk-git/po/es.po b/gitk-git/po/es.po index ddcb0a5f68..fef3bbafee 100644 --- a/gitk-git/po/es.po +++ b/gitk-git/po/es.po @@ -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)" diff --git a/gitk-git/po/fr.po b/gitk-git/po/fr.po index c44f994fa5..e4fac932e5 100644 --- a/gitk-git/po/fr.po +++ b/gitk-git/po/fr.po @@ -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" diff --git a/gitk-git/po/hu.po b/gitk-git/po/hu.po index 66fd75ba5b..79ec5a5656 100644 --- a/gitk-git/po/hu.po +++ b/gitk-git/po/hu.po @@ -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" diff --git a/gitk-git/po/it.po b/gitk-git/po/it.po index b5f002db7d..b58d23eb2b 100644 --- a/gitk-git/po/it.po +++ b/gitk-git/po/it.po @@ -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" diff --git a/gitk-git/po/ja.po b/gitk-git/po/ja.po index f143753db0..ca3c29b457 100644 --- a/gitk-git/po/ja.po +++ b/gitk-git/po/ja.po @@ -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 に従ってください" diff --git a/gitk-git/po/pt_br.po b/gitk-git/po/pt_br.po index 3f78f1b748..1feb34854b 100644 --- a/gitk-git/po/pt_br.po +++ b/gitk-git/po/pt_br.po @@ -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 index 0000000000..f680ea86aa --- /dev/null +++ b/gitk-git/po/pt_pt.po @@ -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:" diff --git a/gitk-git/po/ru.po b/gitk-git/po/ru.po index 17ed026aa7..9b08c263ea 100644 --- a/gitk-git/po/ru.po +++ b/gitk-git/po/ru.po @@ -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 "Отбор лучшего для этого коммита" +msgstr "Копировать этот коммит в текущую ветку" -#: 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 "Возврат этого коммита" +msgstr "Обратить изменения этого коммита" -#: 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 "Копирование изменений" +msgstr "Копирование коммита" -#: 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 "Отбор лучшего невозможен из-за изменений в файле «%s».\nЗакомитьте, сбросьте или спрячьте изменения и повторите операцию." +msgstr "Копирование коммита невозможно из-за изменений в файле «%s».\nЗакоммитьте, сбросьте или спрячьте изменения и повторите операцию." -#: 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 "Возврат изменений" +msgstr "Обращение изменений" -#: 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 "Возврат изменений коммита не удался из-за локальных изменений в указанных файлах: %s\nЗакоммитьте, сбросьте или спрячьте изменения и повторите операцию." -#: 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:" diff --git a/gitk-git/po/sv.po b/gitk-git/po/sv.po index d9d4e87a44..2a06fe5bbc 100644 --- a/gitk-git/po/sv.po +++ b/gitk-git/po/sv.po @@ -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" diff --git a/gitk-git/po/vi.po b/gitk-git/po/vi.po index 8966812368..5967498660 100644 --- a/gitk-git/po/vi.po +++ b/gitk-git/po/vi.po @@ -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" @@ -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) @@ -161,6 +161,7 @@ struct grep_source { GREP_SOURCE_SHA1, GREP_SOURCE_FILE, GREP_SOURCE_BUF, + GREP_SOURCE_SUBMODULE, } type; void *identifier; diff --git a/pathspec.c b/pathspec.c index 22ca74a126..7ababb3159 100644 --- a/pathspec.c +++ b/pathspec.c @@ -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; } diff --git a/pathspec.h b/pathspec.h index 59809e4793..49fd823ddf 100644 --- a/pathspec.h +++ b/pathspec.h @@ -19,15 +19,14 @@ #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; diff --git a/read-cache.c b/read-cache.c index 2eca639cce..7a9a7de91e 100644 --- a/read-cache.c +++ b/read-cache.c @@ -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; @@ -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; diff --git a/run-command.c b/run-command.c index ca905a9e80..73bfba7ef9 100644 --- a/run-command.c +++ b/run-command.c @@ -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); } diff --git a/run-command.h b/run-command.h index dd1c78c28d..4fa8f65adb 100644 --- a/run-command.h +++ b/run-command.h @@ -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; }; @@ -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; } } diff --git a/sha1_file.c b/sha1_file.c index 1eb47f6113..b5e827ac9e 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -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); diff --git a/submodule-config.c b/submodule-config.c index ec13ab5a3d..4bf50f398a 100644 --- a/submodule-config.c +++ b/submodule-config.c @@ -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) { diff --git a/submodule-config.h b/submodule-config.h index 99df8e593c..70f19363fd 100644 --- a/submodule-config.h +++ b/submodule-config.h @@ -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 */ diff --git a/submodule.c b/submodule.c index 73521cdbb2..4c4f033e8a 100644 --- a/submodule.c +++ b/submodule.c @@ -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); diff --git a/submodule.h b/submodule.h index b7576d6f43..b7fe4d2027 100644 --- a/submodule.h +++ b/submodule.h @@ -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, diff --git a/t/t1514-rev-parse-push.sh b/t/t1514-rev-parse-push.sh index 7214f5b33f..623a32aa6e 100755 --- a/t/t1514-rev-parse-push.sh +++ b/t/t1514-rev-parse-push.sh @@ -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 diff --git a/t/t3510-cherry-pick-sequence.sh b/t/t3510-cherry-pick-sequence.sh index 372307c21b..0acf4b1461 100755 --- a/t/t3510-cherry-pick-sequence.sh +++ b/t/t3510-cherry-pick-sequence.sh @@ -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 ' diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh index bcbb680651..5aa6db584c 100755 --- a/t/t3600-rm.sh +++ b/t/t3600-rm.sh @@ -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^ && diff --git a/t/t5003-archive-zip.sh b/t/t5003-archive-zip.sh index 14744b2a4b..55c7870997 100755 --- a/t/t5003-archive-zip.sh +++ b/t/t5003-archive-zip.sh @@ -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' \ diff --git a/t/t5310-pack-bitmaps.sh b/t/t5310-pack-bitmaps.sh index b4c7a6ff6b..424bec7d77 100755 --- a/t/t5310-pack-bitmaps.sh +++ b/t/t5310-pack-bitmaps.sh @@ -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' ' diff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh index 9b19cff729..49d3621a92 100755 --- a/t/t5504-fetch-receive-strict.sh +++ b/t/t5504-fetch-receive-strict.sh @@ -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 \ diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh index 26b2cafc47..0fc5a7c596 100755 --- a/t/t5516-fetch-push.sh +++ b/t/t5516-fetch-push.sh @@ -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 index 0000000000..b195f71ea9 --- /dev/null +++ b/t/t5580-clone-push-unc.sh @@ -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 diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh index a433394200..4241ea5b32 100755 --- a/t/t5601-clone.sh +++ b/t/t5601-clone.sh @@ -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 ' diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh index 5e5370feb4..8c2c6eaef8 100755 --- a/t/t6030-bisect-porcelain.sh +++ b/t/t6030-bisect-porcelain.sh @@ -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 index 0000000000..fd401ca605 --- /dev/null +++ b/t/t6134-pathspec-in-submodule.sh @@ -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 diff --git a/t/t6500-gc.sh b/t/t6500-gc.sh index 5d7d414617..1762dfa6a3 100755 --- a/t/t6500-gc.sh +++ b/t/t6500-gc.sh @@ -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 diff --git a/t/t7610-mergetool.sh b/t/t7610-mergetool.sh index 63d36fb28e..381b7df452 100755 --- a/t/t7610-mergetool.sh +++ b/t/t7610-mergetool.sh @@ -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 diff --git a/t/t7810-grep.sh b/t/t7810-grep.sh index de2405ccba..19f0108f8a 100755 --- a/t/t7810-grep.sh +++ b/t/t7810-grep.sh @@ -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 index 0000000000..67247a01d6 --- /dev/null +++ b/t/t7814-grep-recurse-submodules.sh @@ -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 diff --git a/t/t8002-blame.sh b/t/t8002-blame.sh index ab79de9544..380e1c1054 100755 --- a/t/t8002-blame.sh +++ b/t/t8002-blame.sh @@ -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 index 0000000000..831125047b --- /dev/null +++ b/t/t8011-blame-split-file.sh @@ -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 diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh index 3dc4a3454d..0f398dd160 100755 --- a/t/t9001-send-email.sh +++ b/t/t9001-send-email.sh @@ -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 } diff --git a/t/t9117-git-svn-init-clone.sh b/t/t9117-git-svn-init-clone.sh index 69a675052e..044f65e916 100755 --- a/t/t9117-git-svn-init-clone.sh +++ b/t/t9117-git-svn-init-clone.sh @@ -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 diff --git a/t/t9813-git-p4-preserve-users.sh b/t/t9813-git-p4-preserve-users.sh index 0fe2312807..bda222aa02 100755 --- a/t/t9813-git-p4-preserve-users.sh +++ b/t/t9813-git-p4-preserve-users.sh @@ -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 ) diff --git a/t/t9814-git-p4-rename.sh b/t/t9814-git-p4-rename.sh index c89992cf95..e7e0268e98 100755 --- a/t/t9814-git-p4-rename.sh +++ b/t/t9814-git-p4-rename.sh @@ -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" && diff --git a/transport.c b/transport.c index 3e8799a611..c86ba2eb89 100644 --- a/transport.c +++ b/transport.c @@ -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] == '/') diff --git a/tree-walk.c b/tree-walk.c index 828f4356be..ff77605680 100644 --- a/tree-walk.c +++ b/tree-walk.c @@ -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 @@ match_wildcards: 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); /* diff --git a/unpack-trees.c b/unpack-trees.c index 7a6df99d10..129d49ff45 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -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) diff --git a/worktree.c b/worktree.c index 828fd7a0ad..53b4771c04 100644 --- a/worktree.c +++ b/worktree.c @@ -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; |