diff options
352 files changed, 30427 insertions, 5773 deletions
diff --git a/.gitignore b/.gitignore index c8c13f5503..bac60ce31a 100644 --- a/.gitignore +++ b/.gitignore @@ -35,6 +35,7 @@ git-diff-files git-diff-index git-diff-tree git-describe +git-fast-export git-fast-import git-fetch git-fetch--tool @@ -109,7 +110,6 @@ git-rev-list git-rev-parse git-revert git-rm -git-runstatus git-send-email git-send-pack git-sh-setup @@ -6,6 +6,7 @@ # Aneesh Kumar K.V <aneesh.kumar@gmail.com> +Brian M. Carlson <sandals@crustytoothpaste.ath.cx> Chris Shoemaker <c.shoemaker@cox.net> Dana L. How <danahow@gmail.com> Dana L. How <how@deathvalley.cswitch.com> diff --git a/Documentation/CodingGuidelines b/Documentation/CodingGuidelines new file mode 100644 index 0000000000..3b042db624 --- /dev/null +++ b/Documentation/CodingGuidelines @@ -0,0 +1,112 @@ +Like other projects, we also have some guidelines to keep to the +code. For git in general, three rough rules are: + + - Most importantly, we never say "It's in POSIX; we'll happily + ignore your needs should your system not conform to it." + We live in the real world. + + - However, we often say "Let's stay away from that construct, + it's not even in POSIX". + + - In spite of the above two rules, we sometimes say "Although + this is not in POSIX, it (is so convenient | makes the code + much more readable | has other good characteristics) and + practically all the platforms we care about support it, so + let's use it". + + Again, we live in the real world, and it is sometimes a + judgement call, the decision based more on real world + constraints people face than what the paper standard says. + + +As for more concrete guidelines, just imitate the existing code +(this is a good guideline, no matter which project you are +contributing to). But if you must have a list of rules, +here they are. + +For shell scripts specifically (not exhaustive): + + - We prefer $( ... ) for command substitution; unlike ``, it + properly nests. It should have been the way Bourne spelled + it from day one, but unfortunately isn't. + + - We use ${parameter-word} and its [-=?+] siblings, and their + colon'ed "unset or null" form. + + - We use ${parameter#word} and its [#%] siblings, and their + doubled "longest matching" form. + + - We use Arithmetic Expansion $(( ... )). + + - No "Substring Expansion" ${parameter:offset:length}. + + - No shell arrays. + + - No strlen ${#parameter}. + + - No regexp ${parameter/pattern/string}. + + - We do not use Process Substitution <(list) or >(list). + + - We prefer "test" over "[ ... ]". + + - We do not write the noiseword "function" in front of shell + functions. + +For C programs: + + - We use tabs to indent, and interpret tabs as taking up to + 8 spaces. + + - We try to keep to at most 80 characters per line. + + - When declaring pointers, the star sides with the variable + name, i.e. "char *string", not "char* string" or + "char * string". This makes it easier to understand code + like "char *string, c;". + + - We avoid using braces unnecessarily. I.e. + + if (bla) { + x = 1; + } + + is frowned upon. A gray area is when the statement extends + over a few lines, and/or you have a lengthy comment atop of + it. Also, like in the Linux kernel, if there is a long list + of "else if" statements, it can make sense to add braces to + single line blocks. + + - Try to make your code understandable. You may put comments + in, but comments invariably tend to stale out when the code + they were describing changes. Often splitting a function + into two makes the intention of the code much clearer. + + - Double negation is often harder to understand than no negation + at all. + + - Some clever tricks, like using the !! operator with arithmetic + constructs, can be extremely confusing to others. Avoid them, + unless there is a compelling reason to use them. + + - Use the API. No, really. We have a strbuf (variable length + string), several arrays with the ALLOC_GROW() macro, a + path_list for sorted string lists, a hash map (mapping struct + objects) named "struct decorate", amongst other things. + + - When you come up with an API, document it. + + - The first #include in C files, except in platform specific + compat/ implementations, should be git-compat-util.h or another + header file that includes it, such as cache.h or builtin.h. + + - If you are planning a new command, consider writing it in shell + or perl first, so that changes in semantics can be easily + changed and discussed. Many git commands started out like + that, and a few are still scripts. + + - Avoid introducing a new dependency into git. This means you + usually should stay away from scripting languages not already + used in the git core command set (unless your command is clearly + separate from it, such as an importer to convert random-scm-X + repositories to git). diff --git a/Documentation/Makefile b/Documentation/Makefile index 39ec0ede02..1b5802456d 100644 --- a/Documentation/Makefile +++ b/Documentation/Makefile @@ -37,9 +37,6 @@ man7dir=$(mandir)/man7 ASCIIDOC=asciidoc ASCIIDOC_EXTRA = -ifdef ASCIIDOC8 -ASCIIDOC_EXTRA += -a asciidoc7compatible -endif INSTALL?=install RM ?= rm -f DOC_REF = origin/man @@ -48,10 +45,20 @@ infodir?=$(prefix)/share/info MAKEINFO=makeinfo INSTALL_INFO=install-info DOCBOOK2X_TEXI=docbook2x-texi +ifndef PERL_PATH + PERL_PATH = /usr/bin/perl +endif -include ../config.mak.autogen -include ../config.mak +ifdef ASCIIDOC8 +ASCIIDOC_EXTRA += -a asciidoc7compatible +endif +ifdef DOCBOOK_XSL_172 +ASCIIDOC_EXTRA += -a docbook-xsl-172 +endif + # # Please note that there is a minor bug in asciidoc. # The version after 6.0.3 _will_ include the patch found here: @@ -75,16 +82,16 @@ man7: $(DOC_MAN7) info: git.info install: man - $(INSTALL) -d -m755 $(DESTDIR)$(man1dir) - $(INSTALL) -d -m755 $(DESTDIR)$(man5dir) - $(INSTALL) -d -m755 $(DESTDIR)$(man7dir) - $(INSTALL) -m644 $(DOC_MAN1) $(DESTDIR)$(man1dir) - $(INSTALL) -m644 $(DOC_MAN5) $(DESTDIR)$(man5dir) - $(INSTALL) -m644 $(DOC_MAN7) $(DESTDIR)$(man7dir) + $(INSTALL) -d -m 755 $(DESTDIR)$(man1dir) + $(INSTALL) -d -m 755 $(DESTDIR)$(man5dir) + $(INSTALL) -d -m 755 $(DESTDIR)$(man7dir) + $(INSTALL) -m 644 $(DOC_MAN1) $(DESTDIR)$(man1dir) + $(INSTALL) -m 644 $(DOC_MAN5) $(DESTDIR)$(man5dir) + $(INSTALL) -m 644 $(DOC_MAN7) $(DESTDIR)$(man7dir) install-info: info - $(INSTALL) -d -m755 $(DESTDIR)$(infodir) - $(INSTALL) -m644 git.info $(DESTDIR)$(infodir) + $(INSTALL) -d -m 755 $(DESTDIR)$(infodir) + $(INSTALL) -m 644 git.info $(DESTDIR)$(infodir) if test -r $(DESTDIR)$(infodir)/dir; then \ $(INSTALL_INFO) --info-dir=$(DESTDIR)$(infodir) git.info ;\ else \ @@ -101,7 +108,7 @@ install-info: info # doc.dep : $(wildcard *.txt) build-docdep.perl $(RM) $@+ $@ - perl ./build-docdep.perl >$@+ + $(PERL_PATH) ./build-docdep.perl >$@+ mv $@+ $@ -include doc.dep @@ -118,9 +125,9 @@ cmds_txt = cmds-ancillaryinterrogators.txt \ $(cmds_txt): cmd-list.made -cmd-list.made: cmd-list.perl $(MAN1_TXT) +cmd-list.made: cmd-list.perl ../command-list.txt $(MAN1_TXT) $(RM) $@ - perl ./cmd-list.perl + $(PERL_PATH) ./cmd-list.perl ../command-list.txt date >$@ git.7 git.html: git.txt @@ -157,7 +164,7 @@ user-manual.html: user-manual.xml git.info: user-manual.xml $(RM) $@ $*.texi $*.texi+ $(DOCBOOK2X_TEXI) user-manual.xml --to-stdout >$*.texi+ - perl fix-texi.perl <$*.texi+ >$*.texi + $(PERL_PATH) fix-texi.perl <$*.texi+ >$*.texi $(MAKEINFO) --no-split $*.texi $(RM) $*.texi $*.texi+ diff --git a/Documentation/RelNotes-1.5.3.5.txt b/Documentation/RelNotes-1.5.3.5.txt index 4e46d2c2a2..7ff1d5d0d1 100644 --- a/Documentation/RelNotes-1.5.3.5.txt +++ b/Documentation/RelNotes-1.5.3.5.txt @@ -63,8 +63,8 @@ Fixes since v1.5.3.4 * Git segfaulted when reading an invalid .gitattributes file. Fixed. - * post-receive-email example hook fixed was fixed for - non-fast-forward updates. + * post-receive-email example hook was fixed for non-fast-forward + updates. * Documentation updates for supported (but previously undocumented) options of "git-archive" and "git-reflog". @@ -90,5 +90,5 @@ Fixes since v1.5.3.4 * "git-send-pack $remote frotz" segfaulted when there is nothing named 'frotz' on the local end. - * "git-rebase -interactive" did not handle its "--strategy" option + * "git-rebase --interactive" did not handle its "--strategy" option properly. diff --git a/Documentation/RelNotes-1.5.3.6.txt b/Documentation/RelNotes-1.5.3.6.txt new file mode 100644 index 0000000000..069a2b2cf9 --- /dev/null +++ b/Documentation/RelNotes-1.5.3.6.txt @@ -0,0 +1,48 @@ +GIT v1.5.3.6 Release Notes +========================== + +Fixes since v1.5.3.5 +-------------------- + + * git-cvsexportcommit handles root commits better. + + * git-svn dcommit used to clobber when sending a series of + patches. + + * git-svn dcommit failed after attempting to rebase when + started with a dirty index; now it stops upfront. + + * git-grep sometimes refused to work when your index was + unmerged. + + * "git-grep -A1 -B2" acted as if it was told to run "git -A1 -B21". + + * git-hash-object did not honor configuration variables, such as + core.compression. + + * git-index-pack choked on a huge pack on 32-bit machines, even when + large file offsets are supported. + + * atom feeds from git-web said "10" for the month of November. + + * a memory leak in commit walker was plugged. + + * When git-send-email inserted the original author's From: + address in body, it did not mark the message with + Content-type: as needed. + + * git-revert and git-cherry-pick incorrectly refused to start + when the work tree was dirty. + + * git-clean did not honor core.excludesfile configuration. + + * git-add mishandled ".gitignore" files when applying them to + subdirectories. + + * While importing a too branchy history, git-fastimport did not + honor delta depth limit properly. + + * Support for zlib implementations that lack ZLIB_VERNUM and definition + of deflateBound() has been added. + + * Quite a lot of documentation clarifications. diff --git a/Documentation/RelNotes-1.5.3.7.txt b/Documentation/RelNotes-1.5.3.7.txt new file mode 100644 index 0000000000..2f690616c8 --- /dev/null +++ b/Documentation/RelNotes-1.5.3.7.txt @@ -0,0 +1,45 @@ +GIT v1.5.3.7 Release Notes +========================== + +Fixes since v1.5.3.6 +-------------------- + + * git-send-email added 8-bit contents to the payload without + marking it as 8-bit in a CTE header. + + * "git-bundle create a.bndl HEAD" dereferenced the symref and + did not record the ref as 'HEAD'; this prevented a bundle + from being used as a normal source of git-clone. + + * The code to reject nonsense command line of the form + "git-commit -a paths..." and "git-commit --interactive + paths..." were broken. + + * Adding a signature that is not ASCII-only to an original + commit that is ASCII-only would make the result non-ASCII. + "git-format-patch -s" did not mark such a message correctly + with MIME encoding header. + + * git-add sometimes did not mark the resulting index entry + stat-clean. This affected only cases when adding the + contents with the same length as the previously staged + contents, and the previous staging made the index entry + "racily clean". + + * git-commit did not honor GIT_INDEX_FILE the user had in the + environment. + + * When checking out a revision, git-checkout did not report where the + updated HEAD is if you happened to have a file called HEAD in the + work tree. + + * "git-rev-list --objects" mishandled a tree that points at a + submodule. + + * "git cvsimport" was not ready for packed refs that "git gc" can + produce and gave incorrect results. + + * Many scripted Porcelains were confused when you happened to have a + file called "HEAD" in your work tree. + +Also it contains updates to the user manual and documentation. diff --git a/Documentation/RelNotes-1.5.4.txt b/Documentation/RelNotes-1.5.4.txt index 133fa64d22..c1ebd6997a 100644 --- a/Documentation/RelNotes-1.5.4.txt +++ b/Documentation/RelNotes-1.5.4.txt @@ -1,50 +1,253 @@ GIT v1.5.4 Release Notes ======================== +Removal +------- + + * "git svnimport" was removed in favor of "git svn". It is still there + in the source tree (contrib/examples) but unsupported. + + +Deprecation notices +------------------- + + * Next feature release of git (this change is scheduled for v1.5.5 but + it could slip) will by default install dashed form of commands + (e.g. "git-commit") outside of users' normal $PATH, and will install + only selected commands ("git" itself, and "gitk") in $PATH. This + implies: + + - Using dashed form of git commands (e.g. "git-commit") from the + command line has been informally deprecated since early 2006, but + now it officially is, and will be removed in the future. Use + dashless form (e.g. "git commit") instead. + + - Using dashed from from your scripts, without first prepending the + return value from "git --exec-path" to the scripts' PATH, has been + informally deprecated since early 2006, but now it officially is. + + - Use of dashed form with "PATH=$(git --exec-path):$PATH; export + PATH" early in your script is not deprecated with this change. + + Users are strongly encouraged to adjust their habits and scripts now + to prepare for this. + + * The post-receive hook was introduced in March 2007 to supersede + post-update hook, primarily to overcome the command line length + limitation of the latter. Use of post-update hook will be deprecated + in future versions of git, perhaps in v1.5.5. + + * "git lost-found" was deprecated in favor of "git fsck"'s --lost-found + option, and will be removed in the future. + + * "git peek-remote" is deprecated, as "git ls-remote" was written in C + and works for all transports, and will be removed in the future. + + Updates since v1.5.3 -------------------- * Comes with much improved gitk. - * git-reset is now built-in. + * Comes with "git gui" 0.9.1 with i18n. + + * gitk is now merged as a subdirectory of git.git project, in + preparation for its i18n. - * git-send-email can optionally talk over ssmtp and use SMTP-AUTH. + * progress display from many commands are a lot nicer to the eye. + Transfer commands show throughput data. - * git-rebase learned --whitespace option. + * many commands that pay attention to per-directory .gitignore now do + so lazily, which makes the usual case go much faster. - * git-remote knows --mirror mode. + * Output processing for '--pretty=format:<user format>' has been + optimized. - * git-merge can call the "post-merge" hook. + * Rename detection of diff family, while detecting exact matches, has + been greatly optimized. - * git-pack-objects can optionally run deltification with multiple threads. + * Rename detection of diff family tries to make more naturally looking + pairing. Earlier if more than one identical rename sources were + found in the preimage, they were picked pretty much at random. + + * Value "true" for color.diff and color.status configuration used to + mean "always" (even when the output is not going to a terminal). + This has been corrected to mean the same thing as "auto". + + * HTTP proxy can be specified per remote repository using + remote.*.httpproxy configuration, or global http.proxy configuration + variable. + + * Various Perforce importer updates. - * git-archive can optionally substitute keywords in files marked with + * Example update and post-receive hooks have been improved. + + * Any command that wants to take a commit object name can now use + ":/string" syntax to name a commit. + + * "git reset" is now built-in and its output can be squelched with -q. + + * "git send-email" can optionally talk over ssmtp and use SMTP-AUTH. + + * "git rebase" learned --whitespace option. + + * In "git rebase", when you decide not to replay a particular change + after the command stopped with a conflict, you can say "git rebase + --skip" without first running "git reset --hard", as the command now + runs it for you. + + * "git rebase --interactive" mode can now work on detached HEAD. + + * "git rebase" now detaches head during its operation, so after a + successful "git rebase" operation, the reflog entry branch@{1} for + the current branch points at the commit before the rebase was + started. + + * "git rebase -i" also triggers rerere to help your repeated merges. + + * "git merge" can call the "post-merge" hook. + + * "git pack-objects" can optionally run deltification with multiple + threads. + + * "git archive" can optionally substitute keywords in files marked with export-subst attribute. - * git-for-each-ref learned %(xxxdate:<dateformat>) syntax to - show the various date fields in different formats. + * "git cherry-pick" made a misguided attempt to repeat the original + command line in the generated log message, when told to cherry-pick a + commit by naming a tag that points at it. It does not anymore. - * git-gc --auto is a low-impact way to automatically run a - variant of git-repack that does not lose unreferenced objects - (read: safer than the usual one) after the user accumulates - too many loose objects. + * "git for-each-ref" learned %(xxxdate:<dateformat>) syntax to show the + various date fields in different formats. - * git-push has been rewritten in C. + * "git gc --auto" is a low-impact way to automatically run a variant of + "git repack" that does not lose unreferenced objects (read: safer + than the usual one) after the user accumulates too many loose + objects. - * git-push learned --dry-run option to show what would happen - if a push is run. + * "git clean" has been rewritten in C. - * git-remote learned "rm" subcommand. + * You need to explicitly set clean.requireForce to "false" to allow + "git clean" without -f to do any damage (lack of the configuration + variable used to mean "do not require -f option to lose untracked + files", but we now use the safer default). - * git-rebase --interactive mode can now work on detached HEAD. + * "git push" learned --dry-run option to show what would happen if a + push is run. - * git-cvsserver can be run via git-shell. + * "git push" does not update a tracking ref on the local side when the + remote refused to update the corresponding ref. - * git-am and git-rebase are far less verbose. + * "git push" learned --mirror option. This is to push the local refs + one-to-one to the remote, and deletes refs from the remote that do + not exist anymore in the repository on the pushing side. - * git-pull learned to pass --[no-]ff option to underlying git-merge. + * "git push" can remove a corrupt ref at the remote site with the usual + ":ref" refspec. + + * "git remote" knows --mirror mode. This is to set up configuration to + push into a remote repository to store local branch heads to the same + branch on the remote side, and remove branch heads locally removed + from local repository at the same time. Suitable for pushing into a + back-up repository. + + * "git remote" learned "rm" subcommand. + + * "git cvsserver" can be run via "git shell". + + * "git cvsserver" acts more like receive-pack by running post-receive + and post-update hooks. + + * "git am" and "git rebase" are far less verbose. + + * "git pull" learned to pass --[no-]ff option to underlying "git + merge". + + * "git pull --rebase" is a different way to integrate what you fetched + into your current branch. + + * "git fast-export" produces datastream that can be fed to fast-import + to reproduce the history recorded in a git repository. + + * "git add -i" takes pathspecs to limit the set of files to work on. + + * "git add -p" is a short-hand to go directly to the selective patch + subcommand in the interactive command loop and to exit when done. + + * "git add -i" UI has been colorized. + + * "git commit --allow-empty" allows you to create a single-parent + commit that records the same tree as its parent, overriding the usual + safety valve. + + * "git commit --amend" can amend a merge that does not change the tree + from its first parent. + + * "git commit" has been rewritten in C. + + * "git stash random-text" does not create a new stash anymore. It was + a UI mistake. Use "git stash save random-text", or "git stash" + (without extra args) for that. + + * "git prune --expire <time>" can exempt young loose objects from + getting pruned. + + * "git branch --contains <commit>" can list branches that are + descendants of a given commit. + + * "git log" learned --early-output option to help interactive GUI + implementations. + + * "git bisect" learned "skip" action to mark untestable commits. + + * "git format-patch" learned "format.numbered" configuration variable + to automatically turn --numbered option on when more than one commits + are formatted. + + * "git ls-files" learned "--exclude-standard" to use the canned set of + exclude files. + + * "git tag -a -f existing" begins the editor session using the existing + annotation message. + + * "git tag -m one -m bar" (multiple -m options) behaves similarly to + "git commit"; the parameters to -m options are formatted as separate + paragraphs. + + * "git cvsexportcommit" learned -w option to specify and switch to the + CVS working directory. + + * "git checkout" from a subdirectory learned to use "../path" to allow + checking out a path outside the current directory without cd'ing up. + + * "git send-email --dry-run" shows full headers for easier diagnosis. + + * "git merge-ours" is now built-in. + + * "git svn" learned "info" and "show-externals" subcommands. + + * "git svn" run from a subdirectory failed to read settings from the + .git/config. + + * "git svn" learned --use-log-author option, which picks up more + descriptive name from From: and Signed-off-by: lines in the commit + message. + + * "git status" from a subdirectory now shows relative paths which makes + copy-and-pasting for git-checkout/git-add/git-rm easier. + + * "git checkout" from and to detached HEAD leaves a bit more + information in the reflog. + + * In addition there are quite a few internal clean-ups. Notably + + - many fork/exec have been replaced with run-command API, + brought from the msysgit effort. + + - introduction and more use of the option parser API. + + - enhancement and more use of the strbuf API. - * Various Perforce importer updates. Fixes since v1.5.3 ------------------ @@ -52,8 +255,17 @@ Fixes since v1.5.3 All of the fixes in v1.5.3 maintenance series are included in this release, unless otherwise noted. +These fixes are only in v1.5.4 and not backported to v1.5.3 maintenance +series. + + * "git svn" talking with the SVN over http will correctly quote branch + and project names. + + * "git config" did not work correctly on platforms that define + REG_NOMATCH to an even number. + -- exec >/var/tmp/1 -O=v1.5.3.4-450-g952a9e5 +O=v1.5.3.7-1111-gd9f4059 echo O=`git describe refs/heads/master` git shortlog --no-merges $O..refs/heads/master ^refs/heads/maint diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches index 61635bf04d..de08d094e3 100644 --- a/Documentation/SubmittingPatches +++ b/Documentation/SubmittingPatches @@ -10,7 +10,7 @@ Checklist (and a short version for the impatient): - the first line of the commit message should be a short description and should skip the full stop - if you want your work included in git.git, add a - "Signed-off-by: Your Name <your@email.com>" line to the + "Signed-off-by: Your Name <you@example.com>" line to the commit message (or just use the option "-s" when committing) to confirm that you agree to the Developer's Certificate of Origin @@ -20,9 +20,6 @@ Checklist (and a short version for the impatient): Patch: - use "git format-patch -M" to create the patch - - send your patch to <git@vger.kernel.org>. If you use - git-send-email(1), please test it first by sending - email to yourself. - do not PGP sign your patch - do not attach your patch, but read in the mail body, unless you cannot teach your mailer to @@ -31,13 +28,15 @@ Checklist (and a short version for the impatient): corrupt whitespaces. - provide additional information (which is unsuitable for the commit message) between the "---" and the diffstat - - send the patch to the list (git@vger.kernel.org) and the - maintainer (gitster@pobox.com). - if you change, add, or remove a command line option or make some other user interface change, the associated documentation should be updated as well. - if your name is not writable in ASCII, make sure that you send off a message in the correct encoding. + - send the patch to the list (git@vger.kernel.org) and the + maintainer (gitster@pobox.com). If you use + git-send-email(1), please test it first by sending + email to yourself. Long version: diff --git a/Documentation/asciidoc.conf b/Documentation/asciidoc.conf index af5b1558a6..99d8874aa0 100644 --- a/Documentation/asciidoc.conf +++ b/Documentation/asciidoc.conf @@ -23,7 +23,9 @@ ifdef::backend-docbook[] endif::backend-docbook[] ifdef::backend-docbook[] +ifndef::docbook-xsl-172[] # "unbreak" docbook-xsl v1.68 for manpages. v1.69 works with or without this. +# v1.72 breaks with this because it replaces dots not in roff requests. [listingblock] <example><title>{title}</title> <literallayout> @@ -36,6 +38,7 @@ ifdef::doctype-manpage[] endif::doctype-manpage[] </literallayout> {title#}</example> +endif::docbook-xsl-172[] endif::backend-docbook[] ifdef::doctype-manpage[] diff --git a/Documentation/cmd-list.perl b/Documentation/cmd-list.perl index 8d21d423e5..c2d55cdb5e 100755 --- a/Documentation/cmd-list.perl +++ b/Documentation/cmd-list.perl @@ -3,7 +3,8 @@ use File::Compare qw(compare); sub format_one { - my ($out, $name) = @_; + my ($out, $nameattr) = @_; + my ($name, $attr) = @$nameattr; my ($state, $description); $state = 0; open I, '<', "$name.txt" or die "No such file $name.txt"; @@ -26,8 +27,11 @@ sub format_one { die "No description found in $name.txt"; } if (my ($verify_name, $text) = ($description =~ /^($name) - (.*)/)) { - print $out "gitlink:$name\[1\]::\n"; - print $out "\t$text.\n\n"; + print $out "gitlink:$name\[1\]::\n\t"; + if ($attr =~ / deprecated /) { + print $out "(deprecated) "; + } + print $out "$text.\n\n"; } else { die "Description does not match $name: $description"; @@ -35,12 +39,13 @@ sub format_one { } my %cmds = (); -while (<DATA>) { +for (sort <>) { next if /^#/; chomp; - my ($name, $cat) = /^(\S+)\s+(.*)$/; - push @{$cmds{$cat}}, $name; + my ($name, $cat, $attr) = /^(\S+)\s+(.*?)(?:\s+(.*))?$/; + $attr = '' unless defined $attr; + push @{$cmds{$cat}}, [$name, " $attr "]; } for my $cat (qw(ancillaryinterrogators @@ -67,136 +72,3 @@ for my $cat (qw(ancillaryinterrogators rename "$out+", "$out"; } } - -# The following list is sorted with "sort -d" to make it easier -# to find entry in the resulting git.html manual page. -__DATA__ -git-add mainporcelain -git-am mainporcelain -git-annotate ancillaryinterrogators -git-apply plumbingmanipulators -git-archimport foreignscminterface -git-archive mainporcelain -git-bisect mainporcelain -git-blame ancillaryinterrogators -git-branch mainporcelain -git-bundle mainporcelain -git-cat-file plumbinginterrogators -git-check-attr purehelpers -git-checkout mainporcelain -git-checkout-index plumbingmanipulators -git-check-ref-format purehelpers -git-cherry ancillaryinterrogators -git-cherry-pick mainporcelain -git-citool mainporcelain -git-clean mainporcelain -git-clone mainporcelain -git-commit mainporcelain -git-commit-tree plumbingmanipulators -git-config ancillarymanipulators -git-count-objects ancillaryinterrogators -git-cvsexportcommit foreignscminterface -git-cvsimport foreignscminterface -git-cvsserver foreignscminterface -git-daemon synchingrepositories -git-describe mainporcelain -git-diff mainporcelain -git-diff-files plumbinginterrogators -git-diff-index plumbinginterrogators -git-diff-tree plumbinginterrogators -git-fast-import ancillarymanipulators -git-fetch mainporcelain -git-fetch-pack synchingrepositories -git-filter-branch ancillarymanipulators -git-fmt-merge-msg purehelpers -git-for-each-ref plumbinginterrogators -git-format-patch mainporcelain -git-fsck ancillaryinterrogators -git-gc mainporcelain -git-get-tar-commit-id ancillaryinterrogators -git-grep mainporcelain -git-gui mainporcelain -git-hash-object plumbingmanipulators -git-http-fetch synchelpers -git-http-push synchelpers -git-imap-send foreignscminterface -git-index-pack plumbingmanipulators -git-init mainporcelain -git-instaweb ancillaryinterrogators -gitk mainporcelain -git-local-fetch synchingrepositories -git-log mainporcelain -git-lost-found ancillarymanipulators -git-ls-files plumbinginterrogators -git-ls-remote plumbinginterrogators -git-ls-tree plumbinginterrogators -git-mailinfo purehelpers -git-mailsplit purehelpers -git-merge mainporcelain -git-merge-base plumbinginterrogators -git-merge-file plumbingmanipulators -git-merge-index plumbingmanipulators -git-merge-one-file purehelpers -git-mergetool ancillarymanipulators -git-merge-tree ancillaryinterrogators -git-mktag plumbingmanipulators -git-mktree plumbingmanipulators -git-mv mainporcelain -git-name-rev plumbinginterrogators -git-pack-objects plumbingmanipulators -git-pack-redundant plumbinginterrogators -git-pack-refs ancillarymanipulators -git-parse-remote synchelpers -git-patch-id purehelpers -git-peek-remote purehelpers -git-prune ancillarymanipulators -git-prune-packed plumbingmanipulators -git-pull mainporcelain -git-push mainporcelain -git-quiltimport foreignscminterface -git-read-tree plumbingmanipulators -git-rebase mainporcelain -git-receive-pack synchelpers -git-reflog ancillarymanipulators -git-relink ancillarymanipulators -git-remote ancillarymanipulators -git-repack ancillarymanipulators -git-request-pull foreignscminterface -git-rerere ancillaryinterrogators -git-reset mainporcelain -git-revert mainporcelain -git-rev-list plumbinginterrogators -git-rev-parse ancillaryinterrogators -git-rm mainporcelain -git-runstatus ancillaryinterrogators -git-send-email foreignscminterface -git-send-pack synchingrepositories -git-shell synchelpers -git-shortlog mainporcelain -git-show mainporcelain -git-show-branch ancillaryinterrogators -git-show-index plumbinginterrogators -git-show-ref plumbinginterrogators -git-sh-setup purehelpers -git-ssh-fetch synchingrepositories -git-ssh-upload synchingrepositories -git-stash mainporcelain -git-status mainporcelain -git-stripspace purehelpers -git-submodule mainporcelain -git-svn foreignscminterface -git-symbolic-ref plumbingmanipulators -git-tag mainporcelain -git-tar-tree plumbinginterrogators -git-unpack-file plumbinginterrogators -git-unpack-objects plumbingmanipulators -git-update-index plumbingmanipulators -git-update-ref plumbingmanipulators -git-update-server-info synchingrepositories -git-upload-archive synchelpers -git-upload-pack synchelpers -git-var plumbinginterrogators -git-verify-pack plumbinginterrogators -git-verify-tag ancillaryinterrogators -git-whatchanged ancillaryinterrogators -git-write-tree plumbingmanipulators diff --git a/Documentation/config.txt b/Documentation/config.txt index 0e711374dd..fabe7f859f 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -226,13 +226,15 @@ core.compression:: An integer -1..9, indicating a default compression level. -1 is the zlib default. 0 means no compression, and 1..9 are various speed/size tradeoffs, 9 being slowest. + If set, this provides a default to other compression variables, + such as 'core.loosecompression' and 'pack.compression'. core.loosecompression:: An integer -1..9, indicating the compression level for objects that are not in a pack file. -1 is the zlib default. 0 means no compression, and 1..9 are various speed/size tradeoffs, 9 being slowest. If not set, defaults to core.compression. If that is - not set, defaults to 0 (best speed). + not set, defaults to 1 (best speed). core.packedGitWindowSize:: Number of bytes of a pack file to map into memory in a @@ -358,14 +360,21 @@ branch.<name>.mergeoptions:: option values containing whitespace characters are currently not supported. +branch.<name>.rebase:: + When true, rebase the branch <name> on top of the fetched branch, + instead of merging the default branch from the default remote. + *NOTE*: this is a possibly dangerous operation; do *not* use + it unless you understand the implications (see gitlink:git-rebase[1] + for details). + clean.requireForce:: - A boolean to make git-clean do nothing unless given -f or -n. Defaults - to false. + A boolean to make git-clean do nothing unless given -f + or -n. Defaults to true. color.branch:: A boolean to enable/disable color in the output of - gitlink:git-branch[1]. May be set to `true` (or `always`), - `false` (or `never`) or `auto`, in which case colors are used + gitlink:git-branch[1]. May be set to `always`, + `false` (or `never`) or `auto` (or `true`), in which case colors are used only when the output is to a terminal. Defaults to false. color.branch.<slot>:: @@ -383,9 +392,9 @@ second is the background. The position of the attribute, if any, doesn't matter. color.diff:: - When true (or `always`), always use colors in patch. - When false (or `never`), never. When set to `auto`, use - colors only when the output is to the terminal. + When set to `always`, always use colors in patch. + When false (or `never`), never. When set to `true` or `auto`, use + colors only when the output is to the terminal. Defaults to false. color.diff.<slot>:: Use customized color for diff colorization. `<slot>` specifies @@ -396,14 +405,26 @@ color.diff.<slot>:: whitespace errors). The values of these variables may be specified as in color.branch.<slot>. +color.interactive:: + When set to `always`, always use colors in `git add --interactive`. + When false (or `never`), never. When set to `true` or `auto`, use + colors only when the output is to the terminal. Defaults to false. + +color.interactive.<slot>:: + Use customized color for `git add --interactive` + output. `<slot>` may be `prompt`, `header`, or `help`, for + three distinct types of normal output from interactive + programs. The values of these variables may be specified as + in color.branch.<slot>. + color.pager:: A boolean to enable/disable colored output when the pager is in use (default is true). color.status:: A boolean to enable/disable color in the output of - gitlink:git-status[1]. May be set to `true` (or `always`), - `false` (or `never`) or `auto`, in which case colors are used + gitlink:git-status[1]. May be set to `always`, + `false` (or `never`) or `auto` (or `true`), in which case colors are used only when the output is to a terminal. Defaults to false. color.status.<slot>:: @@ -446,6 +467,12 @@ fetch.unpackLimit:: pack from a push can make the push operation complete faster, especially on slow filesystems. +format.numbered:: + A boolean which can enable sequence numbers in patch subjects. + Seting this option to "auto" will enable it only if there is + more than one patch. See --numbered option in + gitlink:git-format-patch[1]. + format.headers:: Additional email headers to include in a patch to be submitted by mail. See gitlink:git-format-patch[1]. @@ -506,7 +533,9 @@ gc.rerereunresolved:: rerere.enabled:: Activate recording of resolved conflicts, so that identical conflict hunks can be resolved automatically, should they - be encountered again. See gitlink:git-rerere[1]. + be encountered again. gitlink:git-rerere[1] command is by + default enabled, but can be disabled by setting this option to + false. gitcvs.enabled:: Whether the CVS server interface is enabled for this repository. @@ -549,6 +578,11 @@ specified as 'gitcvs.<access_method>.<varname>' (where 'access_method' is one of "ext" and "pserver") to make them apply only for the given access method. +http.proxy:: + Override the HTTP proxy, normally configured using the 'http_proxy' + environment variable (see gitlink:curl[1]). This can be overridden + on a per-remote basis; see remote.<name>.proxy + http.sslVerify:: Whether to verify the SSL certificate when fetching or pushing over HTTPS. Can be overridden by the 'GIT_SSL_NO_VERIFY' environment @@ -656,7 +690,9 @@ pack.compression:: in a pack file. -1 is the zlib default. 0 means no compression, and 1..9 are various speed/size tradeoffs, 9 being slowest. If not set, defaults to core.compression. If that is - not set, defaults to -1. + not set, defaults to -1, the zlib default, which is "a default + compromise between speed and compression (currently equivalent + to level 6)." pack.deltaCacheSize:: The maximum memory in bytes used for caching deltas in @@ -675,6 +711,15 @@ pack.threads:: machines. The required amount of memory for the delta search window is however multiplied by the number of threads. +pack.indexVersion:: + Specify the default pack index version. Valid values are 1 for + legacy pack index used by Git versions prior to 1.5.2, and 2 for + the new pack index with capabilities for packs larger than 4 GB + as well as proper protection against the repacking of corrupted + packs. Version 2 is selected and this config option ignored + whenever the corresponding pack is larger than 2 GB. Otherwise + the default is 1. + pull.octopus:: The default merge strategy to use when pulling multiple branches at once. @@ -686,6 +731,11 @@ remote.<name>.url:: The URL of a remote repository. See gitlink:git-fetch[1] or gitlink:git-push[1]. +remote.<name>.proxy:: + For remotes that require curl (http, https and ftp), the URL to + the proxy to use for that remote. Set to the empty string to + disable proxying for that remote. + remote.<name>.fetch:: The default set of "refspec" for gitlink:git-fetch[1]. See gitlink:git-fetch[1]. @@ -726,6 +776,12 @@ showbranch.default:: The default set of branches for gitlink:git-show-branch[1]. See gitlink:git-show-branch[1]. +status.relativePaths:: + By default, gitlink:git-status[1] shows paths relative to the + current directory. Setting this variable to `false` shows paths + relative to the repository root (this was the default for git + prior to v1.5.4). + tar.umask:: This variable can be used to restrict the permission bits of tar archive entries. The default is 0002, which turns off the diff --git a/Documentation/core-tutorial.txt b/Documentation/core-tutorial.txt index 99817c5337..bd6cd41245 100644 --- a/Documentation/core-tutorial.txt +++ b/Documentation/core-tutorial.txt @@ -931,12 +931,13 @@ Another useful tool, especially if you do not always work in X-Window environment, is `git show-branch`. ------------------------------------------------ -$ git show-branch --topo-order master mybranch +$ git-show-branch --topo-order --more=1 master mybranch * [master] Merge work in mybranch ! [mybranch] Some work. -- - [master] Merge work in mybranch *+ [mybranch] Some work. +* [master^] Some fun. ------------------------------------------------ The first two lines indicate that it is showing the two branches @@ -954,10 +955,22 @@ because `mybranch` has not been merged to incorporate these commits from the master branch. The string inside brackets before the commit log message is a short name you can use to name the commit. In the above example, 'master' and 'mybranch' -are branch heads. 'master~1' is the first parent of 'master' +are branch heads. 'master^' is the first parent of 'master' branch head. Please see 'git-rev-parse' documentation if you see more complex cases. +[NOTE] +Without the '--more=1' option, 'git-show-branch' would not output the +'[master^]' commit, as '[mybranch]' commit is a common ancestor of +both 'master' and 'mybranch' tips. Please see 'git-show-branch' +documentation for details. + +[NOTE] +If there were more commits on the 'master' branch after the merge, the +merge commit itself would not be shown by 'git-show-branch' by +default. You would need to provide '--sparse' option to make the +merge commit visible in this case. + Now, let's pretend you are the one who did all the work in `mybranch`, and the fruit of your hard work has finally been merged to the `master` branch. Let's go back to `mybranch`, and run @@ -1077,11 +1090,6 @@ server like git Native transport does. Any stock HTTP server that does not even support directory index would suffice. But you must prepare your repository with `git-update-server-info` to help dumb transport downloaders. -+ -There are (confusingly enough) `git-ssh-fetch` and `git-ssh-upload` -programs, which are 'commit walkers'; they outlived their -usefulness when git Native and SSH transports were introduced, -and are not used by `git pull` or `git push` scripts. Once you fetch from the remote repository, you `merge` that with your current branch. @@ -1144,7 +1152,7 @@ back to the earlier repository with "hello" and "example" file, and bring ourselves back to the pre-merge state: ------------ -$ git show-branch --more=3 master mybranch +$ git show-branch --more=2 master mybranch ! [master] Merge work in mybranch * [mybranch] Merge work in mybranch -- @@ -1207,7 +1215,7 @@ $ git-read-tree -m -u $mb HEAD mybranch This is the same `git-read-tree` command we have already seen, but it takes three trees, unlike previous examples. This reads the contents of each tree into different 'stage' in the index -file (the first tree goes to stage 1, the second stage 2, +file (the first tree goes to stage 1, the second to stage 2, etc.). After reading three trees into three stages, the paths that are the same in all three stages are 'collapsed' into stage 0. Also paths that are the same in two of three stages are diff --git a/Documentation/diff-format.txt b/Documentation/diff-format.txt index 9709c35c98..2c3a4c433b 100644 --- a/Documentation/diff-format.txt +++ b/Documentation/diff-format.txt @@ -83,161 +83,4 @@ Note that 'combined diff' lists only files which were modified from all parents. -Generating patches with -p --------------------------- - -When "git-diff-index", "git-diff-tree", or "git-diff-files" are run -with a '-p' option, or "git diff" without the '--raw' option, they -do not produce the output described above; instead they produce a -patch file. You can customize the creation of such patches via the -GIT_EXTERNAL_DIFF and the GIT_DIFF_OPTS environment variables. - -What the -p option produces is slightly different from the traditional -diff format. - -1. It is preceded with a "git diff" header, that looks like - this: - - diff --git a/file1 b/file2 -+ -The `a/` and `b/` filenames are the same unless rename/copy is -involved. Especially, even for a creation or a deletion, -`/dev/null` is _not_ used in place of `a/` or `b/` filenames. -+ -When rename/copy is involved, `file1` and `file2` show the -name of the source file of the rename/copy and the name of -the file that rename/copy produces, respectively. - -2. It is followed by one or more extended header lines: - - old mode <mode> - new mode <mode> - deleted file mode <mode> - new file mode <mode> - copy from <path> - copy to <path> - rename from <path> - rename to <path> - similarity index <number> - dissimilarity index <number> - index <hash>..<hash> <mode> - -3. TAB, LF, double quote and backslash characters in pathnames - are represented as `\t`, `\n`, `\"` and `\\`, respectively. - If there is need for such substitution then the whole - pathname is put in double quotes. - -The similarity index is the percentage of unchanged lines, and -the dissimilarity index is the percentage of changed lines. It -is a rounded down integer, followed by a percent sign. The -similarity index value of 100% is thus reserved for two equal -files, while 100% dissimilarity means that no line from the old -file made it into the new one. - - -combined diff format --------------------- - -"git-diff-tree", "git-diff-files" and "git-diff" can take '-c' or -'--cc' option to produce 'combined diff', which looks like this: - ------------- -diff --combined describe.c -index fabadb8,cc95eb0..4866510 ---- a/describe.c -+++ b/describe.c -@@@ -98,20 -98,12 +98,20 @@@ - return (a_date > b_date) ? -1 : (a_date == b_date) ? 0 : 1; - } - -- static void describe(char *arg) - -static void describe(struct commit *cmit, int last_one) -++static void describe(char *arg, int last_one) - { - + unsigned char sha1[20]; - + struct commit *cmit; - struct commit_list *list; - static int initialized = 0; - struct commit_name *n; - - + if (get_sha1(arg, sha1) < 0) - + usage(describe_usage); - + cmit = lookup_commit_reference(sha1); - + if (!cmit) - + usage(describe_usage); - + - if (!initialized) { - initialized = 1; - for_each_ref(get_name); ------------- - -1. It is preceded with a "git diff" header, that looks like - this (when '-c' option is used): - - diff --combined file -+ -or like this (when '--cc' option is used): - - diff --c file - -2. It is followed by one or more extended header lines - (this example shows a merge with two parents): - - index <hash>,<hash>..<hash> - mode <mode>,<mode>..<mode> - new file mode <mode> - deleted file mode <mode>,<mode> -+ -The `mode <mode>,<mode>..<mode>` line appears only if at least one of -the <mode> is different from the rest. Extended headers with -information about detected contents movement (renames and -copying detection) are designed to work with diff of two -<tree-ish> and are not used by combined diff format. - -3. It is followed by two-line from-file/to-file header - - --- a/file - +++ b/file -+ -Similar to two-line header for traditional 'unified' diff -format, `/dev/null` is used to signal created or deleted -files. - -4. Chunk header format is modified to prevent people from - accidentally feeding it to `patch -p1`. Combined diff format - was created for review of merge commit changes, and was not - meant for apply. The change is similar to the change in the - extended 'index' header: - - @@@ <from-file-range> <from-file-range> <to-file-range> @@@ -+ -There are (number of parents + 1) `@` characters in the chunk -header for combined diff format. - -Unlike the traditional 'unified' diff format, which shows two -files A and B with a single column that has `-` (minus -- -appears in A but removed in B), `+` (plus -- missing in A but -added to B), or `" "` (space -- unchanged) prefix, this format -compares two or more files file1, file2,... with one file X, and -shows how X differs from each of fileN. One column for each of -fileN is prepended to the output line to note how X's line is -different from it. - -A `-` character in the column N means that the line appears in -fileN but it does not appear in the result. A `+` character -in the column N means that the line appears in the last file, -and fileN does not have that line (in other words, the line was -added, from the point of view of that parent). - -In the above example output, the function signature was changed -from both files (hence two `-` removals from both file1 and -file2, plus `++` to mean one line that was added does not appear -in either file1 nor file2). Also two other lines are the same -from file1 but do not appear in file2 (hence prefixed with ` +`). - -When shown by `git diff-tree -c`, it compares the parents of a -merge commit with the merge result (i.e. file1..fileN are the -parents). When shown by `git diff-files -c`, it compares the -two unresolved merge parents with the working tree file -(i.e. file1 is stage 2 aka "our version", file2 is stage 3 aka -"their version"). +include::diff-generate-patch.txt[] diff --git a/Documentation/diff-generate-patch.txt b/Documentation/diff-generate-patch.txt new file mode 100644 index 0000000000..029c5f2b82 --- /dev/null +++ b/Documentation/diff-generate-patch.txt @@ -0,0 +1,161 @@ +Generating patches with -p +-------------------------- + +When "git-diff-index", "git-diff-tree", or "git-diff-files" are run +with a '-p' option, "git diff" without the '--raw' option, or +"git log" with the "-p" option, they +do not produce the output described above; instead they produce a +patch file. You can customize the creation of such patches via the +GIT_EXTERNAL_DIFF and the GIT_DIFF_OPTS environment variables. + +What the -p option produces is slightly different from the traditional +diff format. + +1. It is preceded with a "git diff" header, that looks like + this: + + diff --git a/file1 b/file2 ++ +The `a/` and `b/` filenames are the same unless rename/copy is +involved. Especially, even for a creation or a deletion, +`/dev/null` is _not_ used in place of `a/` or `b/` filenames. ++ +When rename/copy is involved, `file1` and `file2` show the +name of the source file of the rename/copy and the name of +the file that rename/copy produces, respectively. + +2. It is followed by one or more extended header lines: + + old mode <mode> + new mode <mode> + deleted file mode <mode> + new file mode <mode> + copy from <path> + copy to <path> + rename from <path> + rename to <path> + similarity index <number> + dissimilarity index <number> + index <hash>..<hash> <mode> + +3. TAB, LF, double quote and backslash characters in pathnames + are represented as `\t`, `\n`, `\"` and `\\`, respectively. + If there is need for such substitution then the whole + pathname is put in double quotes. + +The similarity index is the percentage of unchanged lines, and +the dissimilarity index is the percentage of changed lines. It +is a rounded down integer, followed by a percent sign. The +similarity index value of 100% is thus reserved for two equal +files, while 100% dissimilarity means that no line from the old +file made it into the new one. + + +combined diff format +-------------------- + +"git-diff-tree", "git-diff-files" and "git-diff" can take '-c' or +'--cc' option to produce 'combined diff'. For showing a merge commit +with "git log -p", this is the default format. +A 'combined diff' format looks like this: + +------------ +diff --combined describe.c +index fabadb8,cc95eb0..4866510 +--- a/describe.c ++++ b/describe.c +@@@ -98,20 -98,12 +98,20 @@@ + return (a_date > b_date) ? -1 : (a_date == b_date) ? 0 : 1; + } + +- static void describe(char *arg) + -static void describe(struct commit *cmit, int last_one) +++static void describe(char *arg, int last_one) + { + + unsigned char sha1[20]; + + struct commit *cmit; + struct commit_list *list; + static int initialized = 0; + struct commit_name *n; + + + if (get_sha1(arg, sha1) < 0) + + usage(describe_usage); + + cmit = lookup_commit_reference(sha1); + + if (!cmit) + + usage(describe_usage); + + + if (!initialized) { + initialized = 1; + for_each_ref(get_name); +------------ + +1. It is preceded with a "git diff" header, that looks like + this (when '-c' option is used): + + diff --combined file ++ +or like this (when '--cc' option is used): + + diff --c file + +2. It is followed by one or more extended header lines + (this example shows a merge with two parents): + + index <hash>,<hash>..<hash> + mode <mode>,<mode>..<mode> + new file mode <mode> + deleted file mode <mode>,<mode> ++ +The `mode <mode>,<mode>..<mode>` line appears only if at least one of +the <mode> is different from the rest. Extended headers with +information about detected contents movement (renames and +copying detection) are designed to work with diff of two +<tree-ish> and are not used by combined diff format. + +3. It is followed by two-line from-file/to-file header + + --- a/file + +++ b/file ++ +Similar to two-line header for traditional 'unified' diff +format, `/dev/null` is used to signal created or deleted +files. + +4. Chunk header format is modified to prevent people from + accidentally feeding it to `patch -p1`. Combined diff format + was created for review of merge commit changes, and was not + meant for apply. The change is similar to the change in the + extended 'index' header: + + @@@ <from-file-range> <from-file-range> <to-file-range> @@@ ++ +There are (number of parents + 1) `@` characters in the chunk +header for combined diff format. + +Unlike the traditional 'unified' diff format, which shows two +files A and B with a single column that has `-` (minus -- +appears in A but removed in B), `+` (plus -- missing in A but +added to B), or `" "` (space -- unchanged) prefix, this format +compares two or more files file1, file2,... with one file X, and +shows how X differs from each of fileN. One column for each of +fileN is prepended to the output line to note how X's line is +different from it. + +A `-` character in the column N means that the line appears in +fileN but it does not appear in the result. A `+` character +in the column N means that the line appears in the last file, +and fileN does not have that line (in other words, the line was +added, from the point of view of that parent). + +In the above example output, the function signature was changed +from both files (hence two `-` removals from both file1 and +file2, plus `++` to mean one line that was added does not appear +in either file1 nor file2). Also two other lines are the same +from file1 but do not appear in file2 (hence prefixed with ` +`). + +When shown by `git diff-tree -c`, it compares the parents of a +merge commit with the merge result (i.e. file1..fileN are the +parents). When shown by `git diff-files -c`, it compares the +two unresolved merge parents with the working tree file +(i.e. file1 is stage 2 aka "our version", file2 is stage 3 aka +"their version"). diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt index b1f528ae88..d0154bbc0a 100644 --- a/Documentation/diff-options.txt +++ b/Documentation/diff-options.txt @@ -1,5 +1,27 @@ +// Please don't remove this comment as asciidoc behaves badly when +// the first non-empty line is ifdef/ifndef. The symptom is that +// without this comment the <git-diff-core> attribute conditionally +// defined below ends up being defined unconditionally. +// Last checked with asciidoc 7.0.2. + +ifndef::git-format-patch[] +ifndef::git-diff[] +ifndef::git-log[] +:git-diff-core: 1 +endif::git-log[] +endif::git-diff[] +endif::git-format-patch[] + +ifdef::git-format-patch[] -p:: - Generate patch (see section on generating patches) + Generate patches without diffstat. +endif::git-format-patch[] + +ifndef::git-format-patch[] +-p:: + Generate patch (see section on generating patches). + {git-diff? This is the default.} +endif::git-format-patch[] -u:: Synonym for "-p". @@ -13,6 +35,7 @@ --raw:: Generate the raw format. + {git-diff-core? This is the default.} --patch-with-raw:: Synonym for "-p --raw". @@ -41,6 +64,7 @@ --patch-with-stat:: Synonym for "-p --stat". + {git-format-patch? This is the default.} -z:: NUL-line termination on output. This affects the --raw diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt index 963e1ab1e2..bf94cd43bd 100644 --- a/Documentation/git-add.txt +++ b/Documentation/git-add.txt @@ -8,7 +8,7 @@ git-add - Add file contents to the index SYNOPSIS -------- [verse] -'git-add' [-n] [-v] [-f] [--interactive | -i] [-u] [--refresh] +'git-add' [-n] [-v] [-f] [--interactive | -i] [--patch | -p] [-u] [--refresh] [--] <filepattern>... DESCRIPTION @@ -61,7 +61,14 @@ OPTIONS -i, \--interactive:: Add modified contents in the working tree interactively to - the index. + the index. Optional path arguments may be supplied to limit + operation to a subset of the working tree. See ``Interactive + mode'' for details. + +-p, \--patch: + Similar to Interactive mode but the initial command loop is + bypassed and the 'patch' subcommand is invoked using each of + the specified filepatterns before exiting. -u:: Update only files that git already knows about. This is similar @@ -210,6 +217,8 @@ patch:: k - do not decide on this hunk now, and view the previous undecided hunk K - do not decide on this hunk now, and view the previous hunk + s - split the current hunk into smaller hunks + ? - print help + After deciding the fate for all hunks, if there is any hunk that was chosen, the index is updated with the selected hunks. @@ -224,6 +233,7 @@ See Also -------- gitlink:git-status[1] gitlink:git-rm[1] +gitlink:git-reset[1] gitlink:git-mv[1] gitlink:git-commit[1] gitlink:git-update-index[1] diff --git a/Documentation/git-bisect.txt b/Documentation/git-bisect.txt index 4795349c10..8b9d61a8a4 100644 --- a/Documentation/git-bisect.txt +++ b/Documentation/git-bisect.txt @@ -92,7 +92,16 @@ During the bisection process, you can say $ git bisect visualize ------------ -to see the currently remaining suspects in `gitk`. +to see the currently remaining suspects in `gitk`. `visualize` is a bit +too long to type and `view` is provided as a synonym. + +If `DISPLAY` environment variable is not set, `git log` is used +instead. You can even give command line options such as `-p` and +`--stat`. + +------------ +$ git bisect view --stat +------------ Bisect log and bisect replay ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt index 5e81aa4ee1..d3f21c7975 100644 --- a/Documentation/git-branch.txt +++ b/Documentation/git-branch.txt @@ -10,6 +10,7 @@ SYNOPSIS [verse] 'git-branch' [--color | --no-color] [-r | -a] [-v [--abbrev=<length> | --no-abbrev]] + [--contains <commit>] 'git-branch' [--track | --no-track] [-l] [-f] <branchname> [<start-point>] 'git-branch' (-m | -M) [<oldbranch>] <newbranch> 'git-branch' (-d | -D) [-r] <branchname>... @@ -20,6 +21,9 @@ With no arguments given a list of existing branches will be shown, the current branch will be highlighted with an asterisk. Option `-r` causes the remote-tracking branches to be listed, and option `-a` shows both. +With `--contains <commit>`, shows only the branches that +contains the named commit (in other words, the branches whose +tip commits are descendant of the named commit). In its second form, a new branch named <branchname> will be created. It will start out with a head equal to the one given as <start-point>. @@ -45,17 +49,22 @@ to happen. With a `-d` or `-D` option, `<branchname>` will be deleted. You may specify more than one branch for deletion. If the branch currently -has a reflog then the reflog will also be deleted. Use -r together with -d -to delete remote-tracking branches. +has a reflog then the reflog will also be deleted. + +Use -r together with -d to delete remote-tracking branches. Note, that it +only makes sense to delete remote-tracking branches if they no longer exist +in remote repository or if gitlink:git-fetch[1] was configured not to fetch +them again. See also 'prune' subcommand of gitlink:git-remote[1] for way to +clean up all obsolete remote-tracking branches. OPTIONS ------- -d:: - Delete a branch. The branch must be fully merged. + Delete a branch. The branch must be fully merged in HEAD. -D:: - Delete a branch irrespective of its index status. + Delete a branch irrespective of its merged status. -l:: Create the branch's reflog. This activates recording of @@ -105,7 +114,7 @@ OPTIONS '--track' were given. --no-track:: - When -b is given and a branch is created off a remote branch, + When a branch is created off a remote branch, set up configuration so that git-pull will not retrieve data from the remote branch, ignoring the branch.autosetupmerge configuration variable. @@ -153,9 +162,11 @@ $ git branch -d -r origin/todo origin/html origin/man <1> $ git branch -D test <2> ------------ + -<1> Delete remote-tracking branches "todo", "html", "man" -<2> Delete "test" branch even if the "master" branch does not have all -commits from test branch. +<1> Delete remote-tracking branches "todo", "html", "man". Next 'fetch' or +'pull' will create them again unless you configure them not to. See +gitlink:git-fetch[1]. +<2> Delete "test" branch even if the "master" branch (or whichever branch is +currently checked out) does not have all commits from test branch. Notes diff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt index 76a2edfd9b..937c4a7926 100644 --- a/Documentation/git-cherry-pick.txt +++ b/Documentation/git-cherry-pick.txt @@ -7,7 +7,7 @@ git-cherry-pick - Apply the change introduced by an existing commit SYNOPSIS -------- -'git-cherry-pick' [--edit] [-n] [-x] <commit> +'git-cherry-pick' [--edit] [-n] [-m parent-number] [-x] <commit> DESCRIPTION ----------- @@ -44,6 +44,13 @@ OPTIONS described above, and `-r` was to disable it. Now the default is not to do `-x` so this option is a no-op. +-m parent-number|--mainline parent-number:: + Usually you cannot revert a merge because you do not know which + side of the merge should be considered the mainline. This + option specifies the parent number (starting from 1) of + the mainline and allows cherry-pick to replay the change + relative to the specified parent. + -n|--no-commit:: Usually the command automatically creates a commit with a commit log message stating which commit was diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt index cca14d6b5d..c90bcece24 100644 --- a/Documentation/git-clone.txt +++ b/Documentation/git-clone.txt @@ -12,7 +12,7 @@ SYNOPSIS 'git-clone' [--template=<template_directory>] [-l] [-s] [--no-hardlinks] [-q] [-n] [--bare] [-o <name>] [-u <upload-pack>] [--reference <repository>] - [--depth <depth>] <repository> [<directory>] + [--depth <depth>] [--] <repository> [<directory>] DESCRIPTION ----------- @@ -130,6 +130,7 @@ OPTIONS for "host.xz:foo/.git"). Cloning into an existing directory is not allowed. +:git-clone: 1 include::urls.txt[] Examples diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt index e54fb12103..4261384158 100644 --- a/Documentation/git-commit.txt +++ b/Documentation/git-commit.txt @@ -10,7 +10,7 @@ SYNOPSIS [verse] 'git-commit' [-a | --interactive] [-s] [-v] [-u] [(-c | -C) <commit> | -F <file> | -m <msg> | --amend] - [--no-verify] [-e] [--author <author>] + [--allow-empty] [--no-verify] [-e] [--author <author>] [--] [[-i | -o ]<file>...] DESCRIPTION @@ -86,9 +86,15 @@ OPTIONS Add Signed-off-by line at the end of the commit message. --no-verify:: - This option bypasses the pre-commit hook. + This option bypasses the pre-commit and commit-msg hooks. See also link:hooks.html[hooks]. +--allow-empty:: + Usually recording a commit that has the exact same tree as its + sole parent commit is a mistake, and the command prevents you + from making such a commit. This option bypasses the safety, and + is primarily for use by foreign scm interface scripts. + -e|--edit:: The message taken from file with `-F`, command line with `-m`, and from file with `-C` are usually used as the @@ -154,10 +160,13 @@ EXAMPLES -------- When recording your own work, the contents of modified files in your working tree are temporarily stored to a staging area -called the "index" with gitlink:git-add[1]. Removal -of a file is staged with gitlink:git-rm[1]. After building the -state to be committed incrementally with these commands, `git -commit` (without any pathname parameter) is used to record what +called the "index" with gitlink:git-add[1]. A file can be +reverted back, only in the index but not in the working tree, +to that of the last commit with `git-reset HEAD -- <file>`, +which effectively reverts `git-add` and prevents the changes to +this file from participating in the next commit. After building +the state to be committed incrementally with these commands, +`git commit` (without any pathname parameter) is used to record what has been staged so far. This is the most basic form of the command. An example: diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt index a592b61e2f..98509b4f44 100644 --- a/Documentation/git-config.txt +++ b/Documentation/git-config.txt @@ -20,6 +20,8 @@ SYNOPSIS 'git-config' [<file-option>] --rename-section old_name new_name 'git-config' [<file-option>] --remove-section name 'git-config' [<file-option>] [-z|--null] -l | --list +'git-config' [<file-option>] --get-color name [default] +'git-config' [<file-option>] --get-colorbool name [stdout-is-tty] DESCRIPTION ----------- @@ -134,6 +136,21 @@ See also <<FILES>>. output without getting confused e.g. by values that contain line breaks. +--get-colorbool name [stdout-is-tty]:: + + Find the color setting for `name` (e.g. `color.diff`) and output + "true" or "false". `stdout-is-tty` should be either "true" or + "false", and is taken into account when configuration says + "auto". If `stdout-is-tty` is missing, then checks the standard + output of the command itself, and exits with status 0 if color + is to be used, or exits with status 1 otherwise. + +--get-color name default:: + + Find the color configured for `name` (e.g. `color.diff.new`) and + output it as the ANSI color escape sequence to the standard + output. The optional `default` parameter is used instead, if + there is no color configured for `name`. [[FILES]] FILES @@ -292,6 +309,15 @@ To add a new proxy, without altering any of the existing ones, use % git config core.gitproxy '"proxy-command" for example.com' ------------ +An example to use customized color from the configuration in your +script: + +------------ +#!/bin/sh +WS=$(git config --get-color color.diff.whitespace "blue reverse") +RESET=$(git config --get-color "" "reset") +echo "${WS}your whitespace color or blue reverse${RESET}" +------------ include::config.txt[] diff --git a/Documentation/git-cvsexportcommit.txt b/Documentation/git-cvsexportcommit.txt index c3922f9238..3f9d2295d3 100644 --- a/Documentation/git-cvsexportcommit.txt +++ b/Documentation/git-cvsexportcommit.txt @@ -8,7 +8,7 @@ git-cvsexportcommit - Export a single commit to a CVS checkout SYNOPSIS -------- -'git-cvsexportcommit' [-h] [-u] [-v] [-c] [-P] [-p] [-a] [-d cvsroot] [-f] [-m msgprefix] [PARENTCOMMIT] COMMITID +'git-cvsexportcommit' [-h] [-u] [-v] [-c] [-P] [-p] [-a] [-d cvsroot] [-w cvsworkdir] [-f] [-m msgprefix] [PARENTCOMMIT] COMMITID DESCRIPTION @@ -16,8 +16,9 @@ DESCRIPTION Exports a commit from GIT to a CVS checkout, making it easier to merge patches from a git repository into a CVS repository. -Execute it from the root of the CVS working copy. GIT_DIR must be defined. -See examples below. +Specify the name of a CVS checkout using the -w switch or execute it +from the root of the CVS working copy. In the latter case GIT_DIR must +be defined. See examples below. It does its best to do the safe thing, it will check that the files are unchanged and up to date in the CVS checkout, and it will not autocommit @@ -61,6 +62,11 @@ OPTIONS -u:: Update affected files from CVS repository before attempting export. +-w:: + Specify the location of the CVS checkout to use for the export. This + option does not require GIT_DIR to be set before execution if the + current directory is within a git repository. + -v:: Verbose. @@ -76,6 +82,12 @@ $ git-cvsexportcommit -v <commit-sha1> $ cvs commit -F .msg <files> ------------ +Merge one patch into CVS (-c and -w options). The working directory is within the Git Repo:: ++ +------------ + $ git-cvsexportcommit -v -c -w ~/project_cvs_checkout <commit-sha1> +------------ + Merge pending patches into CVS automatically -- only if you really know what you are doing:: + ------------ @@ -86,11 +98,11 @@ $ git-cherry cvshead myhead | sed -n 's/^+ //p' | xargs -l1 git-cvsexportcommit Author ------ -Written by Martin Langhoff <martin@catalyst.net.nz> +Written by Martin Langhoff <martin@catalyst.net.nz> and others. Documentation -------------- -Documentation by Martin Langhoff <martin@catalyst.net.nz> +Documentation by Martin Langhoff <martin@catalyst.net.nz> and others. GIT --- diff --git a/Documentation/git-diff.txt b/Documentation/git-diff.txt index 11c4216c4a..2808a5ec44 100644 --- a/Documentation/git-diff.txt +++ b/Documentation/git-diff.txt @@ -75,6 +75,7 @@ and the range notations ("<commit>..<commit>" and OPTIONS ------- +:git-diff: 1 include::diff-options.txt[] <path>...:: diff --git a/Documentation/git-fast-export.txt b/Documentation/git-fast-export.txt new file mode 100644 index 0000000000..fd3d571464 --- /dev/null +++ b/Documentation/git-fast-export.txt @@ -0,0 +1,83 @@ +git-fast-export(1) +================== + +NAME +---- +git-fast-export - Git data exporter + + +SYNOPSIS +-------- +'git-fast-export [options]' | 'git-fast-import' + +DESCRIPTION +----------- +This program dumps the given revisions in a form suitable to be piped +into gitlink:git-fast-import[1]. + +You can use it as a human readable bundle replacement (see +gitlink:git-bundle[1]), or as a kind of an interactive +gitlink:git-filter-branch[1]. + + +OPTIONS +------- +--progress=<n>:: + Insert 'progress' statements every <n> objects, to be shown by + gitlink:git-fast-import[1] during import. + +--signed-tags=(verbatim|warn|strip|abort):: + Specify how to handle signed tags. Since any transformation + after the export can change the tag names (which can also happen + when excluding revisions) the signatures will not match. ++ +When asking to 'abort' (which is the default), this program will die +when encountering a signed tag. With 'strip', the tags will be made +unsigned, with 'verbatim', they will be silently exported +and with 'warn', they will be exported, but you will see a warning. + + +EXAMPLES +-------- + +------------------------------------------------------------------- +$ git fast-export --all | (cd /empty/repository && git fast-import) +------------------------------------------------------------------- + +This will export the whole repository and import it into the existing +empty repository. Except for reencoding commits that are not in +UTF-8, it would be a one-to-one mirror. + +----------------------------------------------------- +$ git fast-export master~5..master | + sed "s|refs/heads/master|refs/heads/other|" | + git fast-import +----------------------------------------------------- + +This makes a new branch called 'other' from 'master~5..master' +(i.e. if 'master' has linear history, it will take the last 5 commits). + +Note that this assumes that none of the blobs and commit messages +referenced by that revision range contains the string +'refs/heads/master'. + + +Limitations +----------- + +Since gitlink:git-fast-import[1] cannot tag trees, you will not be +able to export the linux-2.6.git repository completely, as it contains +a tag referencing a tree instead of a commit. + + +Author +------ +Written by Johannes E. Schindelin <johannes.schindelin@gmx.de>. + +Documentation +-------------- +Documentation by Johannes E. Schindelin <johannes.schindelin@gmx.de>. + +GIT +--- +Part of the gitlink:git[7] suite diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt index f0617efa0a..6fb9429851 100644 --- a/Documentation/git-format-patch.txt +++ b/Documentation/git-format-patch.txt @@ -9,9 +9,10 @@ git-format-patch - Prepare patches for e-mail submission SYNOPSIS -------- [verse] -'git-format-patch' [-n | -k] [-o <dir> | --stdout] [--thread] +'git-format-patch' [-k] [-o <dir> | --stdout] [--thread] [--attach[=<boundary>] | --inline[=<boundary>]] [-s | --signoff] [<common diff options>] + [-n | --numbered | -N | --no-numbered] [--start-number <n>] [--numbered-files] [--in-reply-to=Message-Id] [--suffix=.<sfx>] [--ignore-if-in-upstream] @@ -65,6 +66,7 @@ reference. OPTIONS ------- +:git-format-patch: 1 include::diff-options.txt[] -<n>:: @@ -77,6 +79,9 @@ include::diff-options.txt[] -n|--numbered:: Name output in '[PATCH n/m]' format. +-N|--no-numbered:: + Name output in '[PATCH]' format. + --start-number <n>:: Start numbering the patches at <n> instead of 1. @@ -142,15 +147,16 @@ not add any suffix. CONFIGURATION ------------- -You can specify extra mail header lines to be added to each -message in the repository configuration. You can also specify -new defaults for the subject prefix and file suffix. +You can specify extra mail header lines to be added to each message +in the repository configuration, new defaults for the subject prefix +and file suffix, and number patches when outputting more than one. ------------ [format] headers = "Organization: git-foo\n" subjectprefix = CHANGE suffix = .txt + numbered = auto ------------ diff --git a/Documentation/git-get-tar-commit-id.txt b/Documentation/git-get-tar-commit-id.txt index 9b5f86fc30..60d1c52f44 100644 --- a/Documentation/git-get-tar-commit-id.txt +++ b/Documentation/git-get-tar-commit-id.txt @@ -3,7 +3,7 @@ git-get-tar-commit-id(1) NAME ---- -git-get-tar-commit-id - Extract commit ID from an archive created using git-tar-tree +git-get-tar-commit-id - Extract commit ID from an archive created using git-archive SYNOPSIS @@ -14,12 +14,12 @@ SYNOPSIS DESCRIPTION ----------- Acts as a filter, extracting the commit ID stored in archives created by -git-tar-tree. It reads only the first 1024 bytes of input, thus its +gitlink:git-archive[1]. It reads only the first 1024 bytes of input, thus its runtime is not influenced by the size of <tarfile> very much. If no commit ID is found, git-get-tar-commit-id quietly exists with a return code of 1. This can happen if <tarfile> had not been created -using git-tar-tree or if the first parameter of git-tar-tree had been +using git-archive or if the first parameter of git-archive had been a tree ID instead of a commit ID or tag. diff --git a/Documentation/git-help.txt b/Documentation/git-help.txt new file mode 100644 index 0000000000..7ddbf467df --- /dev/null +++ b/Documentation/git-help.txt @@ -0,0 +1,48 @@ +git-help(1) +=========== + +NAME +---- +git-help - display help information about git + +SYNOPSIS +-------- +'git help' [-a|--all] [COMMAND] + +DESCRIPTION +----------- + +With no options and no COMMAND given, the synopsis of the 'git' +command and a list of the most commonly used git commands are printed +on the standard output. + +If the option '--all' or '-a' is given, then all available commands are +printed on the standard output. + +If a git command is named, a manual page for that command is brought +up. The 'man' program is used by default for this purpose. + +Note that 'git --help ...' is identical as 'git help ...' because the +former is internally converted into the latter. + +OPTIONS +------- +-a|--all:: + + Prints all the available commands on the standard output. This + option superseeds any other option. + +Author +------ +Written by Junio C Hamano <gitster@pobox.com> and the git-list +<git@vger.kernel.org>. + +Documentation +------------- +Initial documentation was part of the gitlink:git[7] man page. +Christian Couder <chriscool@tuxfamily.org> extracted and rewrote it a +little. Maintenance is done by the git-list <git@vger.kernel.org>. + +GIT +--- +Part of the gitlink:git[7] suite diff --git a/Documentation/git-local-fetch.txt b/Documentation/git-local-fetch.txt deleted file mode 100644 index e830deeff3..0000000000 --- a/Documentation/git-local-fetch.txt +++ /dev/null @@ -1,66 +0,0 @@ -git-local-fetch(1) -================== - -NAME ----- -git-local-fetch - Duplicate another git repository on a local system - - -SYNOPSIS --------- -[verse] -'git-local-fetch' [-c] [-t] [-a] [-d] [-v] [-w filename] [--recover] [-l] [-s] [-n] - commit-id path - -DESCRIPTION ------------ -THIS COMMAND IS DEPRECATED. - -Duplicates another git repository on a local system. - -OPTIONS -------- --c:: - Get the commit objects. --t:: - Get trees associated with the commit objects. --a:: - Get all the objects. --v:: - Report what is downloaded. --s:: - Instead of regular file-to-file copying use symbolic links to the objects - in the remote repository. --l:: - Before attempting symlinks (if -s is specified) or file-to-file copying the - remote objects, try to hardlink the remote objects into the local - repository. --n:: - Never attempt to file-to-file copy remote objects. Only useful with - -s or -l command-line options. - --w <filename>:: - Writes the commit-id into the filename under $GIT_DIR/refs/<filename> on - the local end after the transfer is complete. - ---stdin:: - Instead of a commit id on the command line (which is not expected in this - case), 'git-local-fetch' expects lines on stdin in the format - - <commit-id>['\t'<filename-as-in--w>] - ---recover:: - Verify that everything reachable from target is fetched. Used after - an earlier fetch is interrupted. - -Author ------- -Written by Junio C Hamano <junkio@cox.net> - -Documentation --------------- -Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>. - -GIT ---- -Part of the gitlink:git[7] suite diff --git a/Documentation/git-log.txt b/Documentation/git-log.txt index 5ec547cc0c..5920d1799a 100644 --- a/Documentation/git-log.txt +++ b/Documentation/git-log.txt @@ -27,6 +27,9 @@ OPTIONS include::pretty-options.txt[] +:git-log: 1 +include::diff-options.txt[] + -<n>:: Limits the number of commits to show. @@ -43,9 +46,6 @@ include::pretty-options.txt[] commit. This option gives a better overview of the evolution of a particular branch. --p:: - Show the change the commit introduces in a patch form. - -g, \--walk-reflogs:: Show commits as they were recorded in the reflog. The log contains a record about how the tip of a reference was changed. @@ -78,6 +78,7 @@ include::pretty-options.txt[] include::pretty-formats.txt[] +include::diff-generate-patch.txt[] Examples -------- diff --git a/Documentation/git-lost-found.txt b/Documentation/git-lost-found.txt index bc739117be..7f808fcd76 100644 --- a/Documentation/git-lost-found.txt +++ b/Documentation/git-lost-found.txt @@ -11,6 +11,10 @@ SYNOPSIS DESCRIPTION ----------- + +*NOTE*: this command is deprecated. Use gitlink:git-fsck[1] with +the option '--lost-found' instead. + Finds dangling commits and tags from the object database, and creates refs to them in the .git/lost-found/ directory. Commits and tags that dereference to commits are stored in .git/lost-found/commit, diff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt index 9e454f0a4d..2ec0c0d270 100644 --- a/Documentation/git-ls-files.txt +++ b/Documentation/git-ls-files.txt @@ -15,6 +15,7 @@ SYNOPSIS [-x <pattern>|--exclude=<pattern>] [-X <file>|--exclude-from=<file>] [--exclude-per-directory=<file>] + [--exclude-standard] [--error-unmatch] [--with-tree=<tree-ish>] [--full-name] [--abbrev] [--] [<file>]\* @@ -77,6 +78,10 @@ OPTIONS read additional exclude patterns that apply only to the directory and its subdirectories in <file>. +--exclude-standard:: + Add the standard git exclusions: .git/info/exclude, .gitignore + in each directory, and the user's global exclusion file. + --error-unmatch:: If any <file> does not appear in the index, treat this as an error (return 1). diff --git a/Documentation/git-peek-remote.txt b/Documentation/git-peek-remote.txt index abc171266a..38a5325af7 100644 --- a/Documentation/git-peek-remote.txt +++ b/Documentation/git-peek-remote.txt @@ -12,8 +12,7 @@ SYNOPSIS DESCRIPTION ----------- -Lists the references the remote repository has, and optionally -stores them in the local repository under the same name. +This command is deprecated; use `git-ls-remote` instead. OPTIONS ------- diff --git a/Documentation/git-prune.txt b/Documentation/git-prune.txt index 0ace233d18..9835bdb878 100644 --- a/Documentation/git-prune.txt +++ b/Documentation/git-prune.txt @@ -8,7 +8,7 @@ git-prune - Prune all unreachable objects from the object database SYNOPSIS -------- -'git-prune' [-n] [--] [<head>...] +'git-prune' [-n] [--expire <expire>] [--] [<head>...] DESCRIPTION ----------- @@ -31,6 +31,9 @@ OPTIONS \--:: Do not interpret any more arguments as options. +\--expire <time>:: + Only expire loose objects older than <time>. + <head>...:: In addition to objects reachable from any of our references, keep objects diff --git a/Documentation/git-pull.txt b/Documentation/git-pull.txt index e1eb2c1d00..d4d26afea0 100644 --- a/Documentation/git-pull.txt +++ b/Documentation/git-pull.txt @@ -33,6 +33,16 @@ include::urls-remotes.txt[] include::merge-strategies.txt[] +\--rebase:: + Instead of a merge, perform a rebase after fetching. + *NOTE:* This is a potentially _dangerous_ mode of operation. + It rewrites history, which does not bode well when you + published that history already. Do *not* use this option + unless you have read gitlink:git-rebase[1] carefully. + +\--no-rebase:: + Override earlier \--rebase. + DEFAULT BEHAVIOUR ----------------- diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt index e5dd4c1066..b8003c63c7 100644 --- a/Documentation/git-push.txt +++ b/Documentation/git-push.txt @@ -10,7 +10,7 @@ SYNOPSIS -------- [verse] 'git-push' [--all] [--dry-run] [--tags] [--receive-pack=<git-receive-pack>] - [--repo=all] [-f | --force] [-v] [<repository> <refspec>...] + [--repo=all] [-f | --force] [-v | --verbose] [<repository> <refspec>...] DESCRIPTION ----------- @@ -63,6 +63,14 @@ the remote repository. Instead of naming each ref to push, specifies that all refs under `$GIT_DIR/refs/heads/` be pushed. +\--mirror:: + Instead of naming each ref to push, specifies that all + refs under `$GIT_DIR/refs/heads/` and `$GIT_DIR/refs/tags/` + be mirrored to the remote repository. Newly created local + refs will be pushed to the remote end, locally updated refs + will be force updated on the remote end, and deleted refs + will be removed from the remote end. + \--dry-run:: Do everything except actually send the updates. @@ -95,7 +103,7 @@ the remote repository. transfer spends extra cycles to minimize the number of objects to be sent and meant to be used on slower connection. --v:: +-v, \--verbose:: Run verbosely. include::urls-remotes.txt[] diff --git a/Documentation/git-reflog.txt b/Documentation/git-reflog.txt index 5c7316ceb8..25003c3866 100644 --- a/Documentation/git-reflog.txt +++ b/Documentation/git-reflog.txt @@ -19,7 +19,7 @@ depending on the subcommand: git reflog expire [--dry-run] [--stale-fix] [--verbose] [--expire=<time>] [--expire-unreachable=<time>] [--all] <refs>... -git reflog [show] [log-options] +git reflog [show] [log-options] [<ref>] Reflog is a mechanism to record when the tip of branches are updated. This command is to manage the information recorded in it. @@ -32,9 +32,16 @@ directly by the end users -- instead, see gitlink:git-gc[1]. The subcommand "show" (which is also the default, in the absence of any subcommands) will take all the normal log options, and show the log of -`HEAD`, which will cover all recent actions, including branch switches. -It is basically an alias for 'git log -g --abbrev-commit ---pretty=oneline', see gitlink:git-log[1]. +the reference provided in the command-line (or `HEAD`, by default). +The reflog will cover all recent actions (HEAD reflog records branch switching +as well). It is an alias for 'git log -g --abbrev-commit --pretty=oneline'; +see gitlink:git-log[1]. + +The reflog is useful in various git commands, to specify the old value +of a reference. For example, `HEAD@\{2\}` means "where HEAD used to be +two moves ago", `master@\{one.week.ago\}` means "where master used to +point to one week ago", and so on. See gitlink:git-rev-parse[1] for +more details. OPTIONS diff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt index 027ba11bdb..4b263c249c 100644 --- a/Documentation/git-remote.txt +++ b/Documentation/git-remote.txt @@ -79,7 +79,7 @@ caution. Fetch updates for a named set of remotes in the repository as defined by remotes.<group>. If a named group is not specified on the command line, the configuration parameter remotes.default will get used; if -remotes.default is not defined, all remotes which do not the +remotes.default is not defined, all remotes which do not have the configuration parameter remote.<name>.skipDefaultUpdate set to true will be updated. (See gitlink:git-config[1]). @@ -101,7 +101,7 @@ $ git remote origin $ git branch -r origin/master -$ git remote add linux-nfs git://linux-nfs.org/pub/nfs-2.6.git +$ git remote add linux-nfs git://linux-nfs.org/pub/linux/nfs-2.6.git $ git remote linux-nfs origin diff --git a/Documentation/git-rerere.txt b/Documentation/git-rerere.txt index c4d4263238..8ce492c8f2 100644 --- a/Documentation/git-rerere.txt +++ b/Documentation/git-rerere.txt @@ -22,10 +22,6 @@ automerge results and corresponding hand-resolve results on the initial manual merge, and later by noticing the same automerge results and applying the previously recorded hand resolution. -[NOTE] -You need to set the config variable rerere.enabled to enable this -command. - COMMANDS -------- diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt index 87afa6f8da..050e4eadbb 100644 --- a/Documentation/git-reset.txt +++ b/Documentation/git-reset.txt @@ -8,8 +8,8 @@ git-reset - Reset current HEAD to the specified state SYNOPSIS -------- [verse] -'git-reset' [--mixed | --soft | --hard] [<commit>] -'git-reset' [--mixed] <commit> [--] <paths>... +'git-reset' [--mixed | --soft | --hard] [-q] [<commit>] +'git-reset' [--mixed] [-q] <commit> [--] <paths>... DESCRIPTION ----------- @@ -45,6 +45,9 @@ OPTIONS switched to. Any changes to tracked files in the working tree since <commit> are lost. +-q:: + Be quiet, only report errors. + <commit>:: Commit to make the current HEAD. diff --git a/Documentation/git-rev-list.txt b/Documentation/git-rev-list.txt index 485280423e..989fbf3562 100644 --- a/Documentation/git-rev-list.txt +++ b/Documentation/git-rev-list.txt @@ -20,6 +20,7 @@ SYNOPSIS [ \--not ] [ \--all ] [ \--stdin ] + [ \--quiet ] [ \--topo-order ] [ \--parents ] [ \--timestamp ] @@ -270,6 +271,14 @@ limiting may be applied. In addition to the '<commit>' listed on the command line, read them from the standard input. +--quiet:: + + Don't print anything to standard output. This form of + git-rev-list is primarly meant to allow the caller to + test the exit status to see if a range of objects is fully + connected (or not). It is faster than redirecting stdout + to /dev/null as the output does not have to be formatted. + --cherry-pick:: Omit any commit that introduces the same change as diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt index 4758c33dee..329fce0aab 100644 --- a/Documentation/git-rev-parse.txt +++ b/Documentation/git-rev-parse.txt @@ -23,6 +23,13 @@ distinguish between them. OPTIONS ------- +--parseopt:: + Use `git-rev-parse` in option parsing mode (see PARSEOPT section below). + +--keep-dash-dash:: + Only meaningful in `--parseopt` mode. Tells the option parser to echo + out the first `--` met instead of skipping it. + --revs-only:: Do not output flags and parameters not meant for `git-rev-list` command. @@ -288,10 +295,75 @@ Here are a handful examples: C^@ I J F F^! D G H D F +PARSEOPT +-------- + +In `--parseopt` mode, `git-rev-parse` helps massaging options to bring to shell +scripts the same facilities C builtins have. It works as an option normalizer +(e.g. splits single switches aggregate values), a bit like `getopt(1)` does. + +It takes on the standard input the specification of the options to parse and +understand, and echoes on the standard output a line suitable for `sh(1)` `eval` +to replace the arguments with normalized ones. In case of error, it outputs +usage on the standard error stream, and exits with code 129. + +Input Format +~~~~~~~~~~~~ + +`git-rev-parse --parseopt` input format is fully text based. It has two parts, +separated by a line that contains only `--`. The lines before the separator +(should be more than one) are used for the usage. +The lines after the separator describe the options. + +Each line of options has this format: + +------------ +<opt_spec><arg_spec>? SP+ help LF +------------ + +`<opt_spec>`:: + its format is the short option character, then the long option name + separated by a comma. Both parts are not required, though at least one + is necessary. `h,help`, `dry-run` and `f` are all three correct + `<opt_spec>`. + +`<arg_spec>`:: + an `<arg_spec>` tells the option parser if the option has an argument + (`=`), an optional one (`?` though its use is discouraged) or none + (no `<arg_spec>` in that case). + +The remainder of the line, after stripping the spaces, is used +as the help associated to the option. + +Blank lines are ignored, and lines that don't match this specification are used +as option group headers (start the line with a space to create such +lines on purpose). + +Example +~~~~~~~ + +------------ +OPTS_SPEC="\ +some-command [options] <args>... + +some-command does foo and bar! +-- +h,help show the help + +foo some nifty option --foo +bar= some cool option --bar with an argument + + An option group Header +C? option C with an optional argument" + +eval `echo "$OPTS_SPEC" | git-rev-parse --parseopt -- "$@" || echo exit $?` +------------ + + Author ------ -Written by Linus Torvalds <torvalds@osdl.org> and -Junio C Hamano <junkio@cox.net> +Written by Linus Torvalds <torvalds@osdl.org> . +Junio C Hamano <junkio@cox.net> and Pierre Habouzit <madcoder@debian.org> Documentation -------------- diff --git a/Documentation/git-revert.txt b/Documentation/git-revert.txt index 69db498447..3457c40787 100644 --- a/Documentation/git-revert.txt +++ b/Documentation/git-revert.txt @@ -7,7 +7,7 @@ git-revert - Revert an existing commit SYNOPSIS -------- -'git-revert' [--edit | --no-edit] [-n] <commit> +'git-revert' [--edit | --no-edit] [-n] [-m parent-number] <commit> DESCRIPTION ----------- @@ -27,6 +27,13 @@ OPTIONS message prior committing the revert. This is the default if you run the command from a terminal. +-m parent-number|--mainline parent-number:: + Usually you cannot revert a merge because you do not know which + side of the merge should be considered the mainline. This + option specifies the parent number (starting from 1) of + the mainline and allows revert to reverse the change + relative to the specified parent. + --no-edit:: With this option, `git-revert` will not start the commit message editor. diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt index e38b7021b4..659215ac72 100644 --- a/Documentation/git-send-email.txt +++ b/Documentation/git-send-email.txt @@ -113,8 +113,7 @@ The --cc option must be repeated for each user you want on the cc list. is not set, this will be prompted for. --suppress-from, --no-suppress-from:: - If this is set, do not add the From: address to the cc: list, if it - shows up in a From: line. + If this is set, do not add the From: address to the cc: list. Default is the value of 'sendemail.suppressfrom' configuration value; if that is unspecified, default to --no-suppress-from. diff --git a/Documentation/git-send-pack.txt b/Documentation/git-send-pack.txt index 2fa01d4a3c..a2d9cb61be 100644 --- a/Documentation/git-send-pack.txt +++ b/Documentation/git-send-pack.txt @@ -85,7 +85,9 @@ Each pattern pair consists of the source side (before the colon) and the destination side (after the colon). The ref to be pushed is determined by finding a match that matches the source side, and where it is pushed is determined by using the -destination side. +destination side. The rules used to match a ref are the same +rules used by gitlink:git-rev-parse[1] to resolve a symbolic ref +name. - It is an error if <src> does not match exactly one of the local refs. diff --git a/Documentation/git-ssh-fetch.txt b/Documentation/git-ssh-fetch.txt deleted file mode 100644 index 8d3e2ffb2c..0000000000 --- a/Documentation/git-ssh-fetch.txt +++ /dev/null @@ -1,52 +0,0 @@ -git-ssh-fetch(1) -================ - -NAME ----- -git-ssh-fetch - Fetch from a remote repository over ssh connection - - - -SYNOPSIS --------- -'git-ssh-fetch' [-c] [-t] [-a] [-d] [-v] [-w filename] [--recover] commit-id url - -DESCRIPTION ------------ -THIS COMMAND IS DEPRECATED. - -Pulls from a remote repository over ssh connection, invoking -git-ssh-upload on the other end. It functions identically to -git-ssh-upload, aside from which end you run it on. - - -OPTIONS -------- -commit-id:: - Either the hash or the filename under [URL]/refs/ to - pull. - --c:: - Get the commit objects. --t:: - Get trees associated with the commit objects. --a:: - Get all the objects. --v:: - Report what is downloaded. --w:: - Writes the commit-id into the filename under $GIT_DIR/refs/ on - the local end after the transfer is complete. - - -Author ------- -Written by Daniel Barkalow <barkalow@iabervon.org> - -Documentation --------------- -Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>. - -GIT ---- -Part of the gitlink:git[7] suite diff --git a/Documentation/git-ssh-upload.txt b/Documentation/git-ssh-upload.txt deleted file mode 100644 index 5e2ca8dccf..0000000000 --- a/Documentation/git-ssh-upload.txt +++ /dev/null @@ -1,48 +0,0 @@ -git-ssh-upload(1) -================= - -NAME ----- -git-ssh-upload - Push to a remote repository over ssh connection - - -SYNOPSIS --------- -'git-ssh-upload' [-c] [-t] [-a] [-d] [-v] [-w filename] [--recover] commit-id url - -DESCRIPTION ------------ -THIS COMMAND IS DEPRECATED. - -Pushes from a remote repository over ssh connection, invoking -git-ssh-fetch on the other end. It functions identically to -git-ssh-fetch, aside from which end you run it on. - -OPTIONS -------- -commit-id:: - Id of commit to push. - --c:: - Get the commit objects. --t:: - Get tree associated with the requested commit object. --a:: - Get all the objects. --v:: - Report what is uploaded. --w:: - Writes the commit-id into the filename under [URL]/refs/ on - the remote end after the transfer is complete. - -Author ------- -Written by Daniel Barkalow <barkalow@iabervon.org> - -Documentation --------------- -Documentation by Daniel Barkalow - -GIT ---- -Part of the gitlink:git[7] suite diff --git a/Documentation/git-status.txt b/Documentation/git-status.txt index 8fd0fc6236..a1bb9bd829 100644 --- a/Documentation/git-status.txt +++ b/Documentation/git-status.txt @@ -12,21 +12,22 @@ SYNOPSIS DESCRIPTION ----------- -Examines paths in the working tree that has changes unrecorded -to the index file, and changes between the index file and the -current HEAD commit. The former paths are what you _could_ -commit by running 'git add' before running 'git -commit', and the latter paths are what you _would_ commit by -running 'git commit'. - -If there is no path that is different between the index file and -the current HEAD commit, the command exits with non-zero -status. +Displays paths that have differences between the index file and the +current HEAD commit, paths that have differences between the working +tree and the index file, and paths in the working tree that are not +tracked by git (and are not ignored by gitlink:gitignore[5]). The first +are what you _would_ commit by running `git commit`; the second and +third are what you _could_ commit by running `git add` before running +`git commit`. The command takes the same set of options as `git-commit`; it shows what would be committed if the same options are given to `git-commit`. +If there is no path that is different between the index file and +the current HEAD commit (i.e., there is nothing to commit by running +`git-commit`), the command exits with non-zero status. + If any paths have been touched in the working tree (that is, their modification times have changed) but their contents and permissions are identical to those in the index file, the command @@ -38,7 +39,12 @@ contains many paths that have been touched but not modified. OUTPUT ------ The output from this command is designed to be used as a commit -template comments, and all the output lines are prefixed with '#'. +template comment, and all the output lines are prefixed with '#'. + +The paths mentioned in the output, unlike many other git commands, are +made relative to the current directory if you are working in a +subdirectory (this is on purpose, to help cutting and pasting). See +the status.relativePaths config option below. CONFIGURATION @@ -49,6 +55,10 @@ mean the same thing and the latter is kept for backward compatibility) and `color.status.<slot>` configuration variables to colorize its output. +If the config variable `status.relativePaths` is set to false, then all +paths shown are relative to the repository root, not to the current +directory. + See Also -------- gitlink:gitignore[5] diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt index 488e4b1caf..918a9928b1 100644 --- a/Documentation/git-svn.txt +++ b/Documentation/git-svn.txt @@ -193,6 +193,12 @@ Any other arguments are passed directly to `git log' repository (that has been init-ed with git-svn). The -r<revision> option is required for this. +'info':: + Shows information about a file or directory similar to what + `svn info' provides. Does not currently support a -r/--revision + argument. Use the --url option to output only the value of the + 'URL:' field. + -- OPTIONS diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt index 10d3e3fa95..784ec6d4c2 100644 --- a/Documentation/git-tag.txt +++ b/Documentation/git-tag.txt @@ -65,7 +65,9 @@ OPTIONS Typing "git tag" without arguments, also lists all tags. -m <msg>:: - Use the given tag message (instead of prompting) + Use the given tag message (instead of prompting). + If multiple `-m` options are given, there values are + concatenated as separate paragraphs. -F <file>:: Take the tag message from the given file. Use '-' to diff --git a/Documentation/git.txt b/Documentation/git.txt index 6db7ae1ea7..1574ecd77e 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -46,6 +46,8 @@ Documentation for older releases are available here: * link:v1.5.3/git.html[documentation for release 1.5.3] * release notes for + link:RelNotes-1.5.3.7.txt[1.5.3.7], + link:RelNotes-1.5.3.6.txt[1.5.3.6], link:RelNotes-1.5.3.5.txt[1.5.3.5], link:RelNotes-1.5.3.4.txt[1.5.3.4], link:RelNotes-1.5.3.3.txt[1.5.3.3], @@ -99,9 +101,9 @@ OPTIONS --help:: Prints the synopsis and a list of the most commonly used - commands. If a git command is named this option will bring up - the man-page for that command. If the option '--all' or '-a' is - given then all available commands are printed. + commands. If the option '--all' or '-a' is given then all + available commands are printed. If a git command is named this + option will bring up the manual page for that command. --exec-path:: Path to wherever your core git programs are installed. @@ -534,7 +536,7 @@ Authors ------- * git's founding father is Linus Torvalds <torvalds@osdl.org>. * The current git nurse is Junio C Hamano <gitster@pobox.com>. -* The git potty was written by Andres Ericsson <ae@op5.se>. +* The git potty was written by Andreas Ericsson <ae@op5.se>. * General upbringing is handled by the git-list <git@vger.kernel.org>. Documentation diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt index c4bcbb9358..71c7ad76d5 100644 --- a/Documentation/gitattributes.txt +++ b/Documentation/gitattributes.txt @@ -148,22 +148,23 @@ with `$Id$` upon check-in. `filter` ^^^^^^^^ -A `filter` attribute can be set to a string value. This names +A `filter` attribute can be set to a string value that names a filter driver specified in the configuration. -A filter driver consists of `clean` command and `smudge` +A filter driver consists of a `clean` command and a `smudge` command, either of which can be left unspecified. Upon -checkout, when `smudge` command is specified, the command is fed -the blob object from its standard input, and its standard output -is used to update the worktree file. Similarly, `clean` command -is used to convert the contents of worktree file upon checkin. +checkout, when the `smudge` command is specified, the command is +fed the blob object from its standard input, and its standard +output is used to update the worktree file. Similarly, the +`clean` command is used to convert the contents of worktree file +upon checkin. -Missing filter driver definition in the config is not an error +A missing filter driver definition in the config is not an error but makes the filter a no-op passthru. The content filtering is done to massage the content into a shape that is more convenient for the platform, filesystem, and -the user to use. The keyword here is "more convenient" and not +the user to use. The key phrase here is "more convenient" and not "turning something unusable into usable". In other words, the intent is that if someone unsets the filter driver definition, or does not have the appropriate filter program, the project diff --git a/Documentation/howto/maintain-git.txt b/Documentation/howto/maintain-git.txt new file mode 100644 index 0000000000..4357e26913 --- /dev/null +++ b/Documentation/howto/maintain-git.txt @@ -0,0 +1,277 @@ +From: Junio C Hamano <gitster@pobox.com> +Date: Wed, 21 Nov 2007 16:32:55 -0800 +Subject: Addendum to "MaintNotes" +Abstract: Imagine that git development is racing along as usual, when our friendly + neighborhood maintainer is struck down by a wayward bus. Out of the + hordes of suckers (loyal developers), you have been tricked (chosen) to + step up as the new maintainer. This howto will show you "how to" do it. + +The maintainer's git time is spent on three activities. + + - Communication (60%) + + Mailing list discussions on general design, fielding user + questions, diagnosing bug reports; reviewing, commenting on, + suggesting alternatives to, and rejecting patches. + + - Integration (30%) + + Applying new patches from the contributors while spotting and + correcting minor mistakes, shuffling the integration and + testing branches, pushing the results out, cutting the + releases, and making announcements. + + - Own development (10%) + + Scratching my own itch and sending proposed patch series out. + +The policy on Integration is informally mentioned in "A Note +from the maintainer" message, which is periodically posted to +this mailing list after each feature release is made. + +The policy. + + - Feature releases are numbered as vX.Y.Z and are meant to + contain bugfixes and enhancements in any area, including + functionality, performance and usability, without regression. + + - Maintenance releases are numbered as vX.Y.Z.W and are meant + to contain only bugfixes for the corresponding vX.Y.Z feature + release and earlier maintenance releases vX.Y.Z.V (V < W). + + - 'master' branch is used to prepare for the next feature + release. In other words, at some point, the tip of 'master' + branch is tagged with vX.Y.Z. + + - 'maint' branch is used to prepare for the next maintenance + release. After the feature release vX.Y.Z is made, the tip + of 'maint' branch is set to that release, and bugfixes will + accumulate on the branch, and at some point, the tip of the + branch is tagged with vX.Y.Z.1, vX.Y.Z.2, and so on. + + - 'next' branch is used to publish changes (both enhancements + and fixes) that (1) have worthwhile goal, (2) are in a fairly + good shape suitable for everyday use, (3) but have not yet + demonstrated to be regression free. New changes are tested + in 'next' before merged to 'master'. + + - 'pu' branch is used to publish other proposed changes that do + not yet pass the criteria set for 'next'. + + - The tips of 'master', 'maint' and 'next' branches will always + fast forward, to allow people to build their own + customization on top of them. + + - Usually 'master' contains all of 'maint', 'next' contains all + of 'master' and 'pu' contains all of 'next'. + + - The tip of 'master' is meant to be more stable than any + tagged releases, and the users are encouraged to follow it. + + - The 'next' branch is where new action takes place, and the + users are encouraged to test it so that regressions and bugs + are found before new topics are merged to 'master'. + + +A typical git day for the maintainer implements the above policy +by doing the following: + + - Scan mailing list and #git channel log. Respond with review + comments, suggestions etc. Kibitz. Collect potentially + usable patches from the mailing list. Patches about a single + topic go to one mailbox (I read my mail in Gnus, and type + \C-o to save/append messages in files in mbox format). + + - Review the patches in the saved mailboxes. Edit proposed log + message for typofixes and clarifications, and add Acks + collected from the list. Edit patch to incorporate "Oops, + that should have been like this" fixes from the discussion. + + - Classify the collected patches and handle 'master' and + 'maint' updates: + + - Obviously correct fixes that pertain to the tip of 'maint' + are directly applied to 'maint'. + + - Obviously correct fixes that pertain to the tip of 'master' + are directly applied to 'master'. + + This step is done with "git am". + + $ git checkout master ;# or "git checkout maint" + $ git am -3 -s mailbox + $ make test + + - Merge downwards (maint->master): + + $ git checkout master + $ git merge maint + $ make test + + - Review the last issue of "What's cooking" message, review the + topics scheduled for merging upwards (topic->master and + topic->maint), and merge. + + $ git checkout master ;# or "git checkout maint" + $ git merge ai/topic ;# or "git merge ai/maint-topic" + $ git log -p ORIG_HEAD.. ;# final review + $ git diff ORIG_HEAD.. ;# final review + $ make test ;# final review + $ git branch -d ai/topic ;# or "git branch -d ai/maint-topic" + + - Merge downwards (maint->master) if needed: + + $ git checkout master + $ git merge maint + $ make test + + - Merge downwards (master->next) if needed: + + $ git checkout next + $ git merge master + $ make test + + - Handle the remaining patches: + + - Anything unobvious that is applicable to 'master' (in other + words, does not depend on anything that is still in 'next' + and not in 'master') is applied to a new topic branch that + is forked from the tip of 'master'. This includes both + enhancements and unobvious fixes to 'master'. A topic + branch is named as ai/topic where "ai" is typically + author's initial and "topic" is a descriptive name of the + topic (in other words, "what's the series is about"). + + - An unobvious fix meant for 'maint' is applied to a new + topic branch that is forked from the tip of 'maint'. The + topic is named as ai/maint-topic. + + - Changes that pertain to an existing topic are applied to + the branch, but: + + - obviously correct ones are applied first; + + - questionable ones are discarded or applied to near the tip; + + - Replacement patches to an existing topic are accepted only + for commits not in 'next'. + + The above except the "replacement" are all done with: + + $ git am -3 -s mailbox + + while patch replacement is often done by: + + $ git format-patch ai/topic~$n..ai/topic ;# export existing + + then replace some parts with the new patch, and reapplying: + + $ git reset --hard ai/topic~$n + $ git am -3 -s 000*.txt + + The full test suite is always run for 'maint' and 'master' + after patch application; for topic branches the tests are run + as time permits. + + - Update "What's cooking" message to review the updates to + existing topics, newly added topics and graduated topics. + + This step is helped with Meta/UWC script (where Meta/ contains + a checkout of the 'todo' branch). + + - Merge topics to 'next'. For each branch whose tip is not + merged to 'next', one of three things can happen: + + - The commits are all next-worthy; merge the topic to next: + + $ git checkout next + $ git merge ai/topic ;# or "git merge ai/maint-topic" + $ make test + + - The new parts are of mixed quality, but earlier ones are + next-worthy; merge the early parts to next: + + $ git checkout next + $ git merge ai/topic~2 ;# the tip two are dubious + $ make test + + - Nothing is next-worthy; do not do anything. + + - Rebase topics that do not have any commit in next yet. This + step is optional but sometimes is worth doing when an old + series that is not in next can take advantage of low-level + framework change that is merged to 'master' already. + + $ git rebase master ai/topic + + This step is helped with Meta/git-topic.perl script to + identify which topic is rebaseable. There also is a + pre-rebase hook to make sure that topics that are already in + 'next' are not rebased beyond the merged commit. + + - Rebuild "pu" to merge the tips of topics not in 'next'. + + $ git checkout pu + $ git reset --hard next + $ git merge ai/topic ;# repeat for all remaining topics + $ make test + + This step is helped with Meta/PU script + + - Push four integration branches to a private repository at + k.org and run "make test" on all of them. + + - Push four integration branches to /pub/scm/git/git.git at + k.org. This triggers its post-update hook which: + + (1) runs "git pull" in $HOME/git-doc/ repository to pull + 'master' just pushed out; + + (2) runs "make doc" in $HOME/git-doc/, install the generated + documentation in staging areas, which are separate + repositories that have html and man branches checked + out. + + (3) runs "git commit" in the staging areas, and run "git + push" back to /pub/scm/git/git.git/ to update the html + and man branches. + + (4) installs generated documentation to /pub/software/scm/git/docs/ + to be viewed from http://www.kernel.org/ + + - Fetch html and man branches back from k.org, and push four + integration branches and the two documentation branches to + repo.or.cz + + +Some observations to be made. + + * Each topic is tested individually, and also together with + other topics cooking in 'next'. Until it matures, none part + of it is merged to 'master'. + + * A topic already in 'next' can get fixes while still in + 'next'. Such a topic will have many merges to 'next' (in + other words, "git log --first-parent next" will show many + "Merge ai/topic to next" for the same topic. + + * An unobvious fix for 'maint' is cooked in 'next' and then + merged to 'master' to make extra sure it is Ok and then + merged to 'maint'. + + * Even when 'next' becomes empty (in other words, all topics + prove stable and are merged to 'master' and "git diff master + next" shows empty), it has tons of merge commits that will + never be in 'master'. + + * In principle, "git log --first-parent master..next" should + show nothing but merges (in practice, there are fixup commits + and reverts that are not merges). + + * Commits near the tip of a topic branch that are not in 'next' + are fair game to be discarded, replaced or rewritten. + Commits already merged to 'next' will not be. + + * Being in the 'next' branch is not a guarantee for a topic to + be included in the next feature release. Being in the + 'master' branch typically is. diff --git a/Documentation/howto/recover-corrupted-blob-object.txt b/Documentation/howto/recover-corrupted-blob-object.txt new file mode 100644 index 0000000000..323b513ed0 --- /dev/null +++ b/Documentation/howto/recover-corrupted-blob-object.txt @@ -0,0 +1,134 @@ +Date: Fri, 9 Nov 2007 08:28:38 -0800 (PST) +From: Linus Torvalds <torvalds@linux-foundation.org> +Subject: corrupt object on git-gc +Abstract: Some tricks to reconstruct blob objects in order to fix + a corrupted repository. + +On Fri, 9 Nov 2007, Yossi Leybovich wrote: +> +> Did not help still the repository look for this object? +> Any one know how can I track this object and understand which file is it + +So exactly *because* the SHA1 hash is cryptographically secure, the hash +itself doesn't actually tell you anything, in order to fix a corrupt +object you basically have to find the "original source" for it. + +The easiest way to do that is almost always to have backups, and find the +same object somewhere else. Backups really are a good idea, and git makes +it pretty easy (if nothing else, just clone the repository somewhere else, +and make sure that you do *not* use a hard-linked clone, and preferably +not the same disk/machine). + +But since you don't seem to have backups right now, the good news is that +especially with a single blob being corrupt, these things *are* somewhat +debuggable. + +First off, move the corrupt object away, and *save* it. The most common +cause of corruption so far has been memory corruption, but even so, there +are people who would be interested in seeing the corruption - but it's +basically impossible to judge the corruption until we can also see the +original object, so right now the corrupt object is useless, but it's very +interesting for the future, in the hope that you can re-create a +non-corrupt version. + +So: + +> ib]$ mv .git/objects/4b/9458b3786228369c63936db65827de3cc06200 ../ + +This is the right thing to do, although it's usually best to save it under +it's full SHA1 name (you just dropped the "4b" from the result ;). + +Let's see what that tells us: + +> ib]$ git-fsck --full +> broken link from tree 2d9263c6d23595e7cb2a21e5ebbb53655278dff8 +> to blob 4b9458b3786228369c63936db65827de3cc06200 +> missing blob 4b9458b3786228369c63936db65827de3cc06200 + +Ok, I removed the "dangling commit" messages, because they are just +messages about the fact that you probably have rebased etc, so they're not +at all interesting. But what remains is still very useful. In particular, +we now know which tree points to it! + +Now you can do + + git ls-tree 2d9263c6d23595e7cb2a21e5ebbb53655278dff8 + +which will show something like + + 100644 blob 8d14531846b95bfa3564b58ccfb7913a034323b8 .gitignore + 100644 blob ebf9bf84da0aab5ed944264a5db2a65fe3a3e883 .mailmap + 100644 blob ca442d313d86dc67e0a2e5d584b465bd382cbf5c COPYING + 100644 blob ee909f2cc49e54f0799a4739d24c4cb9151ae453 CREDITS + 040000 tree 0f5f709c17ad89e72bdbbef6ea221c69807009f6 Documentation + 100644 blob 1570d248ad9237e4fa6e4d079336b9da62d9ba32 Kbuild + 100644 blob 1c7c229a092665b11cd46a25dbd40feeb31661d9 MAINTAINERS + ... + +and you should now have a line that looks like + + 10064 blob 4b9458b3786228369c63936db65827de3cc06200 my-magic-file + +in the output. This already tells you a *lot* it tells you what file the +corrupt blob came from! + +Now, it doesn't tell you quite enough, though: it doesn't tell what +*version* of the file didn't get correctly written! You might be really +lucky, and it may be the version that you already have checked out in your +working tree, in which case fixing this problem is really simple, just do + + git hash-object -w my-magic-file + +again, and if it outputs the missing SHA1 (4b945..) you're now all done! + +But that's the really lucky case, so let's assume that it was some older +version that was broken. How do you tell which version it was? + +The easiest way to do it is to do + + git log --raw --all --full-history -- subdirectory/my-magic-file + +and that will show you the whole log for that file (please realize that +the tree you had may not be the top-level tree, so you need to figure out +which subdirectory it was in on your own), and because you're asking for +raw output, you'll now get something like + + commit abc + Author: + Date: + .. + :100644 100644 4b9458b... newsha... M somedirectory/my-magic-file + + + commit xyz + Author: + Date: + + .. + :100644 100644 oldsha... 4b9458b... M somedirectory/my-magic-file + +and this actually tells you what the *previous* and *subsequent* versions +of that file were! So now you can look at those ("oldsha" and "newsha" +respectively), and hopefully you have done commits often, and can +re-create the missing my-magic-file version by looking at those older and +newer versions! + +If you can do that, you can now recreate the missing object with + + git hash-object -w <recreated-file> + +and your repository is good again! + +(Btw, you could have ignored the fsck, and started with doing a + + git log --raw --all + +and just looked for the sha of the missing object (4b9458b..) in that +whole thing. It's up to you - git does *have* a lot of information, it is +just missing one particular blob version. + +Trying to recreate trees and especially commits is *much* harder. So you +were lucky that it's a blob. It's quite possible that you can recreate the +thing. + + Linus diff --git a/Documentation/urls.txt b/Documentation/urls.txt index e67f9140ab..4f667382ec 100644 --- a/Documentation/urls.txt +++ b/Documentation/urls.txt @@ -36,5 +36,11 @@ To sync with a local directory, you can use: - file:///path/to/repo.git/ =============================================================== +ifndef::git-clone[] They are mostly equivalent, except when cloning. See gitlink:git-clone[1] for details. +endif::git-clone[] + +ifdef::git-clone[] +They are equivalent, except the former implies --local option. +endif::git-clone[] diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt index d99adc6f72..93a47b439b 100644 --- a/Documentation/user-manual.txt +++ b/Documentation/user-manual.txt @@ -56,11 +56,12 @@ $ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git The initial clone may be time-consuming for a large project, but you will only need to clone once. -The clone command creates a new directory named after the project -("git" or "linux-2.6" in the examples above). After you cd into this +The clone command creates a new directory named after the project ("git" +or "linux-2.6" in the examples above). After you cd into this directory, you will see that it contains a copy of the project files, -together with a special top-level directory named ".git", which -contains all the information about the history of the project. +called the <<def_working_tree,working tree>>, together with a special +top-level directory named ".git", which contains all the information +about the history of the project. [[how-to-check-out]] How to check out a different version of a project @@ -71,8 +72,13 @@ of files. It stores the history as a compressed collection of interrelated snapshots of the project's contents. In git each such version is called a <<def_commit,commit>>. -A single git repository may contain multiple branches. It keeps track -of them by keeping a list of <<def_head,heads>> which reference the +Those snapshots aren't necessarily all arranged in a single line from +oldest to newest; instead, work may simultaneously proceed along +parallel lines of development, called <def_branch,branches>>, which may +merge and diverge. + +A single git repository can track development on multiple branches. It +does this by keeping a list of <<def_head,heads>> which reference the latest commit on each branch; the gitlink:git-branch[1] command shows you the list of branch heads: @@ -475,7 +481,7 @@ Bisecting: 3537 revisions left to test after this If you run "git branch" at this point, you'll see that git has temporarily moved you to a new branch named "bisect". This branch points to a commit (with commit id 65934...) that is reachable from -v2.6.19 but not from v2.6.18. Compile and test it, and see whether +"master" but not from v2.6.18. Compile and test it, and see whether it crashes. Assume it does crash. Then: ------------------------------------------------- @@ -658,16 +664,23 @@ gitlink:git-diff[1]: $ git diff master..test ------------------------------------------------- -Sometimes what you want instead is a set of patches: +That will produce the diff between the tips of the two branches. If +you'd prefer to find the diff from their common ancestor to test, you +can use three dots instead of two: + +------------------------------------------------- +$ git diff master...test +------------------------------------------------- + +Sometimes what you want instead is a set of patches; for this you can +use gitlink:git-format-patch[1]: ------------------------------------------------- $ git format-patch master..test ------------------------------------------------- will generate a file with a patch for each commit reachable from test -but not from master. Note that if master also has commits which are -not reachable from test, then the combined result of these patches -will not be the same as the diff produced by the git-diff example. +but not from master. [[viewing-old-file-versions]] Viewing old file versions @@ -1367,7 +1380,7 @@ If you make a commit that you later wish you hadn't, there are two fundamentally different ways to fix the problem: 1. You can create a new commit that undoes whatever was done - by the previous commit. This is the correct thing if your + by the old commit. This is the correct thing if your mistake has already been made public. 2. You can go back and modify the old commit. You should @@ -1403,8 +1416,8 @@ with the changes to be reverted, then you will be asked to fix conflicts manually, just as in the case of <<resolving-a-merge, resolving a merge>>. -[[fixing-a-mistake-by-editing-history]] -Fixing a mistake by editing history +[[fixing-a-mistake-by-rewriting-history]] +Fixing a mistake by rewriting history ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If the problematic commit is the most recent commit, and you have not @@ -1427,7 +1440,7 @@ Again, you should never do this to a commit that may already have been merged into another branch; use gitlink:git-revert[1] instead in that case. -It is also possible to edit commits further back in the history, but +It is also possible to replace commits further back in the history, but this is an advanced topic to be left for <<cleaning-up-history,another chapter>>. @@ -1547,6 +1560,11 @@ This may be time-consuming. Unlike most other git operations (including git-gc when run without any options), it is not safe to prune while other git operations are in progress in the same repository. +If gitlink:git-fsck[1] complains about sha1 mismatches or missing +objects, you may have a much more serious problem; your best option is +probably restoring from backups. See +<<recovering-from-repository-corruption>> for a detailed discussion. + [[recovering-lost-changes]] Recovering lost changes ~~~~~~~~~~~~~~~~~~~~~~~ @@ -1567,9 +1585,9 @@ old history using, for example, $ git log master@{1} ------------------------------------------------- -This lists the commits reachable from the previous version of the head. -This syntax can be used to with any git command that accepts a commit, -not just with git log. Some other examples: +This lists the commits reachable from the previous version of the +"master" branch head. This syntax can be used with any git command +that accepts a commit, not just with git log. Some other examples: ------------------------------------------------- $ git show master@{2} # See where the branch pointed 2, @@ -1916,15 +1934,9 @@ or just $ git push ssh://yourserver.com/~you/proj.git master ------------------------------------------------- -As with git-fetch, git-push will complain if this does not result in -a <<fast-forwards,fast forward>>. Normally this is a sign of -something wrong. However, if you are sure you know what you're -doing, you may force git-push to perform the update anyway by -preceding the branch name by a plus sign: - -------------------------------------------------- -$ git push ssh://yourserver.com/~you/proj.git +master -------------------------------------------------- +As with git-fetch, git-push will complain if this does not result in a +<<fast-forwards,fast forward>>; see the following section for details on +handling this case. Note that the target of a "push" is normally a <<def_bare_repository,bare>> repository. You can also push to a @@ -1952,6 +1964,52 @@ See the explanations of the remote.<name>.url, branch.<name>.remote, and remote.<name>.push options in gitlink:git-config[1] for details. +[[forcing-push]] +What to do when a push fails +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If a push would not result in a <<fast-forwards,fast forward>> of the +remote branch, then it will fail with an error like: + +------------------------------------------------- +error: remote 'refs/heads/master' is not an ancestor of + local 'refs/heads/master'. + Maybe you are not up-to-date and need to pull first? +error: failed to push to 'ssh://yourserver.com/~you/proj.git' +------------------------------------------------- + +This can happen, for example, if you: + + - use `git reset --hard` to remove already-published commits, or + - use `git commit --amend` to replace already-published commits + (as in <<fixing-a-mistake-by-rewriting-history>>), or + - use `git rebase` to rebase any already-published commits (as + in <<using-git-rebase>>). + +You may force git-push to perform the update anyway by preceding the +branch name with a plus sign: + +------------------------------------------------- +$ git push ssh://yourserver.com/~you/proj.git +master +------------------------------------------------- + +Normally whenever a branch head in a public repository is modified, it +is modified to point to a descendent of the commit that it pointed to +before. By forcing a push in this situation, you break that convention. +(See <<problems-with-rewriting-history>>.) + +Nevertheless, this is a common practice for people that need a simple +way to publish a work-in-progress patch series, and it is an acceptable +compromise as long as you warn other developers that this is how you +intend to manage the branch. + +It's also possible for a push to fail in this way when other people have +the right to push to the same repository. In that case, the correct +solution is to retry the push after first updating your work by either a +pull or a fetch followed by a rebase; see the +<<setting-up-a-shared-repository,next section>> and +link:cvs-migration.html[git for CVS users] for more. + [[setting-up-a-shared-repository]] Setting up a shared repository ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -2419,11 +2477,11 @@ return mywork to the state it had before you started the rebase: $ git rebase --abort ------------------------------------------------- -[[modifying-one-commit]] -Modifying a single commit +[[rewriting-one-commit]] +Rewriting a single commit ------------------------- -We saw in <<fixing-a-mistake-by-editing-history>> that you can replace the +We saw in <<fixing-a-mistake-by-rewriting-history>> that you can replace the most recent commit using ------------------------------------------------- @@ -2433,8 +2491,10 @@ $ git commit --amend which will replace the old commit by a new commit incorporating your changes, giving you a chance to edit the old commit message first. -You can also use a combination of this and gitlink:git-rebase[1] to edit -commits further back in your history. First, tag the problematic commit with +You can also use a combination of this and gitlink:git-rebase[1] to +replace a commit further back in your history and recreate the +intervening changes on top of it. First, tag the problematic commit +with ------------------------------------------------- $ git tag bad mywork~5 @@ -2554,6 +2614,72 @@ branches into their own work. For true distributed development that supports proper merging, published branches should never be rewritten. +[[bisect-merges]] +Why bisecting merge commits can be harder than bisecting linear history +----------------------------------------------------------------------- + +The gitlink:git-bisect[1] command correctly handles history that +includes merge commits. However, when the commit that it finds is a +merge commit, the user may need to work harder than usual to figure out +why that commit introduced a problem. + +Imagine this history: + +................................................ + ---Z---o---X---...---o---A---C---D + \ / + o---o---Y---...---o---B +................................................ + +Suppose that on the upper line of development, the meaning of one +of the functions that exists at Z is changed at commit X. The +commits from Z leading to A change both the function's +implementation and all calling sites that exist at Z, as well +as new calling sites they add, to be consistent. There is no +bug at A. + +Suppose that in the meantime on the lower line of development somebody +adds a new calling site for that function at commit Y. The +commits from Z leading to B all assume the old semantics of that +function and the callers and the callee are consistent with each +other. There is no bug at B, either. + +Suppose further that the two development lines merge cleanly at C, +so no conflict resolution is required. + +Nevertheless, the code at C is broken, because the callers added +on the lower line of development have not been converted to the new +semantics introduced on the upper line of development. So if all +you know is that D is bad, that Z is good, and that +gitlink:git-bisect[1] identifies C as the culprit, how will you +figure out that the problem is due to this change in semantics? + +When the result of a git-bisect is a non-merge commit, you should +normally be able to discover the problem by examining just that commit. +Developers can make this easy by breaking their changes into small +self-contained commits. That won't help in the case above, however, +because the problem isn't obvious from examination of any single +commit; instead, a global view of the development is required. To +make matters worse, the change in semantics in the problematic +function may be just one small part of the changes in the upper +line of development. + +On the other hand, if instead of merging at C you had rebased the +history between Z to B on top of A, you would have gotten this +linear history: + +................................................................ + ---Z---o---X--...---o---A---o---o---Y*--...---o---B*--D* +................................................................ + +Bisecting between Z and D* would hit a single culprit commit Y*, +and understanding why Y* was broken would probably be easier. + +Partly for this reason, many experienced git users, even when +working on an otherwise merge-heavy project, keep the history +linear by rebasing against the latest upstream version before +publishing. + [[advanced-branch-management]] Advanced branch management ========================== @@ -3099,6 +3225,127 @@ confusing and scary messages, but it won't actually do anything bad. In contrast, running "git prune" while somebody is actively changing the repository is a *BAD* idea). +[[recovering-from-repository-corruption]] +Recovering from repository corruption +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +By design, git treats data trusted to it with caution. However, even in +the absence of bugs in git itself, it is still possible that hardware or +operating system errors could corrupt data. + +The first defense against such problems is backups. You can back up a +git directory using clone, or just using cp, tar, or any other backup +mechanism. + +As a last resort, you can search for the corrupted objects and attempt +to replace them by hand. Back up your repository before attempting this +in case you corrupt things even more in the process. + +We'll assume that the problem is a single missing or corrupted blob, +which is sometimes a solveable problem. (Recovering missing trees and +especially commits is *much* harder). + +Before starting, verify that there is corruption, and figure out where +it is with gitlink:git-fsck[1]; this may be time-consuming. + +Assume the output looks like this: + +------------------------------------------------ +$ git-fsck --full +broken link from tree 2d9263c6d23595e7cb2a21e5ebbb53655278dff8 + to blob 4b9458b3786228369c63936db65827de3cc06200 +missing blob 4b9458b3786228369c63936db65827de3cc06200 +------------------------------------------------ + +(Typically there will be some "dangling object" messages too, but they +aren't interesting.) + +Now you know that blob 4b9458b3 is missing, and that the tree 2d9263c6 +points to it. If you could find just one copy of that missing blob +object, possibly in some other repository, you could move it into +.git/objects/4b/9458b3... and be done. Suppose you can't. You can +still examine the tree that pointed to it with gitlink:git-ls-tree[1], +which might output something like: + +------------------------------------------------ +$ git ls-tree 2d9263c6d23595e7cb2a21e5ebbb53655278dff8 +100644 blob 8d14531846b95bfa3564b58ccfb7913a034323b8 .gitignore +100644 blob ebf9bf84da0aab5ed944264a5db2a65fe3a3e883 .mailmap +100644 blob ca442d313d86dc67e0a2e5d584b465bd382cbf5c COPYING +... +100644 blob 4b9458b3786228369c63936db65827de3cc06200 myfile +... +------------------------------------------------ + +So now you know that the missing blob was the data for a file named +"myfile". And chances are you can also identify the directory--let's +say it's in "somedirectory". If you're lucky the missing copy might be +the same as the copy you have checked out in your working tree at +"somedirectory/myfile"; you can test whether that's right with +gitlink:git-hash-object[1]: + +------------------------------------------------ +$ git hash-object -w somedirectory/myfile +------------------------------------------------ + +which will create and store a blob object with the contents of +somedirectory/myfile, and output the sha1 of that object. if you're +extremely lucky it might be 4b9458b3786228369c63936db65827de3cc06200, in +which case you've guessed right, and the corruption is fixed! + +Otherwise, you need more information. How do you tell which version of +the file has been lost? + +The easiest way to do this is with: + +------------------------------------------------ +$ git log --raw --all --full-history -- somedirectory/myfile +------------------------------------------------ + +Because you're asking for raw output, you'll now get something like + +------------------------------------------------ +commit abc +Author: +Date: +... +:100644 100644 4b9458b... newsha... M somedirectory/myfile + + +commit xyz +Author: +Date: + +... +:100644 100644 oldsha... 4b9458b... M somedirectory/myfile +------------------------------------------------ + +This tells you that the immediately preceding version of the file was +"newsha", and that the immediately following version was "oldsha". +You also know the commit messages that went with the change from oldsha +to 4b9458b and with the change from 4b9458b to newsha. + +If you've been committing small enough changes, you may now have a good +shot at reconstructing the contents of the in-between state 4b9458b. + +If you can do that, you can now recreate the missing object with + +------------------------------------------------ +$ git hash-object -w <recreated-file> +------------------------------------------------ + +and your repository is good again! + +(Btw, you could have ignored the fsck, and started with doing a + +------------------------------------------------ +$ git log --raw --all +------------------------------------------------ + +and just looked for the sha of the missing object (4b9458b..) in that +whole thing. It's up to you - git does *have* a lot of information, it is +just missing one particular blob version. + [[the-index]] The index ----------- @@ -3460,7 +3707,7 @@ should use the `--remove` and `--add` flags respectively. NOTE! A `--remove` flag does 'not' mean that subsequent filenames will necessarily be removed: if the files still exist in your directory structure, the index will be updated with their new status, not -removed. The only thing `--remove` means is that update-cache will be +removed. The only thing `--remove` means is that update-index will be considering a removed file to be a valid thing, and if the file really does not exist any more, it will update the index accordingly. @@ -4309,4 +4556,7 @@ Write a chapter on using plumbing and writing scripts. Alternates, clone -reference, etc. -git unpack-objects -r for recovery +More on recovery from repository corruption. See: + http://marc.theaimsgroup.com/?l=git&m=117263864820799&w=2 + http://marc.theaimsgroup.com/?l=git&m=117147855503798&w=2 + http://marc.theaimsgroup.com/?l=git&m=117147855503798&w=2 @@ -98,6 +98,8 @@ all:: # Define OLD_ICONV if your library has an old iconv(), where the second # (input buffer pointer) parameter is declared with type (const char **). # +# Define NO_DEFLATE_BOUND if your zlib does not have deflateBound. +# # Define NO_R_TO_GCC_LINKER if your gcc does not like "-R/path/lib" # that tells runtime paths to dynamic libraries; # "-Wl,-rpath=/path/lib" is used instead. @@ -109,10 +111,12 @@ all:: # times (my ext3 doesn't). # # Define USE_STDEV below if you want git to care about the underlying device -# change being considered an inode change from the update-cache perspective. +# change being considered an inode change from the update-index perspective. # # Define ASCIIDOC8 if you want to format documentation with AsciiDoc 8 # +# Define DOCBOOK_XSL_172 if you want to format man pages with DocBook XSL v1.72. +# # Define NO_PERL_MAKEMAKER if you cannot use Makefiles generated by perl's # MakeMaker (e.g. using ActiveState under Cygwin). # @@ -150,6 +154,7 @@ STRIP ?= strip prefix = $(HOME) bindir = $(prefix)/bin +mandir = $(prefix)/share/man gitexecdir = $(bindir) sharedir = $(prefix)/share template_dir = $(sharedir)/git-core/templates @@ -209,15 +214,14 @@ BASIC_LDFLAGS = SCRIPT_SH = \ git-bisect.sh git-checkout.sh \ - git-clean.sh git-clone.sh git-commit.sh \ - git-ls-remote.sh \ + git-clone.sh \ git-merge-one-file.sh git-mergetool.sh git-parse-remote.sh \ git-pull.sh git-rebase.sh git-rebase--interactive.sh \ git-repack.sh git-request-pull.sh \ git-sh-setup.sh \ git-am.sh \ git-merge.sh git-merge-stupid.sh git-merge-octopus.sh \ - git-merge-resolve.sh git-merge-ours.sh \ + git-merge-resolve.sh \ git-lost-found.sh git-quiltimport.sh git-submodule.sh \ git-filter-branch.sh \ git-stash.sh @@ -230,7 +234,7 @@ SCRIPT_PERL = \ SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \ $(patsubst %.perl,%,$(SCRIPT_PERL)) \ - git-status git-instaweb + git-instaweb # ... and all the rest that could be moved out of bindir to gitexecdir PROGRAMS = \ @@ -239,7 +243,7 @@ PROGRAMS = \ git-fast-import$X \ git-daemon$X \ git-merge-index$X git-mktag$X git-mktree$X git-patch-id$X \ - git-peek-remote$X git-receive-pack$X \ + git-receive-pack$X \ git-send-pack$X git-shell$X \ git-show-index$X \ git-unpack-file$X \ @@ -256,7 +260,7 @@ EXTRA_PROGRAMS = BUILT_INS = \ git-format-patch$X git-show$X git-whatchanged$X git-cherry$X \ git-get-tar-commit-id$X git-init$X git-repo-config$X \ - git-fsck-objects$X git-cherry-pick$X \ + git-fsck-objects$X git-cherry-pick$X git-peek-remote$X git-status$X \ $(patsubst builtin-%.o,git-%$X,$(BUILTIN_OBJS)) # what 'all' will build and 'install' will install, in gitexecdir @@ -266,9 +270,6 @@ ALL_PROGRAMS += git-merge-subtree$X # what 'all' will build but not install in gitexecdir OTHER_PROGRAMS = git$X gitweb/gitweb.cgi -ifndef NO_TCLTK -OTHER_PROGRAMS += gitk-wish -endif # Set paths to tools early so that they can be used for version tests. ifndef SHELL_PATH @@ -299,7 +300,7 @@ DIFF_OBJS = \ LIB_OBJS = \ blob.o commit.o connect.o csum-file.o cache-tree.o base85.o \ date.o diff-delta.o entry.o exec_cmd.o ident.o \ - interpolate.o hash.o \ + pretty.o interpolate.o hash.o \ lockfile.o \ patch-ids.o \ object.o pack-check.o pack-write.o patch-delta.o path.o pkt-line.o \ @@ -326,6 +327,8 @@ BUILTIN_OBJS = \ builtin-check-attr.o \ builtin-checkout-index.o \ builtin-check-ref-format.o \ + builtin-clean.o \ + builtin-commit.o \ builtin-commit-tree.o \ builtin-count-objects.o \ builtin-describe.o \ @@ -333,6 +336,7 @@ BUILTIN_OBJS = \ builtin-diff-files.o \ builtin-diff-index.o \ builtin-diff-tree.o \ + builtin-fast-export.o \ builtin-fetch.o \ builtin-fetch-pack.o \ builtin-fetch--tool.o \ @@ -345,10 +349,12 @@ BUILTIN_OBJS = \ builtin-log.o \ builtin-ls-files.o \ builtin-ls-tree.o \ + builtin-ls-remote.o \ builtin-mailinfo.o \ builtin-mailsplit.o \ builtin-merge-base.o \ builtin-merge-file.o \ + builtin-merge-ours.o \ builtin-mv.o \ builtin-name-rev.o \ builtin-pack-objects.o \ @@ -357,6 +363,7 @@ BUILTIN_OBJS = \ builtin-push.o \ builtin-read-tree.o \ builtin-reflog.o \ + builtin-send-pack.o \ builtin-config.o \ builtin-rerere.o \ builtin-reset.o \ @@ -364,7 +371,6 @@ BUILTIN_OBJS = \ builtin-rev-parse.o \ builtin-revert.o \ builtin-rm.o \ - builtin-runstatus.o \ builtin-shortlog.o \ builtin-show-branch.o \ builtin-stripspace.o \ @@ -401,7 +407,9 @@ endif ifeq ($(uname_S),Darwin) NEEDS_SSL_WITH_CRYPTO = YesPlease NEEDS_LIBICONV = YesPlease - OLD_ICONV = UnfortunatelyYes + ifneq ($(shell expr "$(uname_R)" : '9\.'),2) + OLD_ICONV = UnfortunatelyYes + endif NO_STRLCPY = YesPlease NO_MEMMEM = YesPlease endif @@ -412,18 +420,17 @@ ifeq ($(uname_S),SunOS) NO_STRCASESTR = YesPlease NO_MEMMEM = YesPlease NO_HSTRERROR = YesPlease + NO_MKDTEMP = YesPlease ifeq ($(uname_R),5.8) NEEDS_LIBICONV = YesPlease NO_UNSETENV = YesPlease NO_SETENV = YesPlease - NO_MKDTEMP = YesPlease NO_C99_FORMAT = YesPlease NO_STRTOUMAX = YesPlease endif ifeq ($(uname_R),5.9) NO_UNSETENV = YesPlease NO_SETENV = YesPlease - NO_MKDTEMP = YesPlease NO_C99_FORMAT = YesPlease NO_STRTOUMAX = YesPlease endif @@ -440,6 +447,7 @@ ifeq ($(uname_O),Cygwin) NEEDS_LIBICONV = YesPlease NO_FAST_WORKING_DIRECTORY = UnfortunatelyYes NO_TRUSTABLE_FILEMODE = UnfortunatelyYes + OLD_ICONV = UnfortunatelyYes # There are conflicting reports about this. # On some boxes NO_MMAP is needed, and not so elsewhere. # Try commenting this out if you suspect MMAP is more efficient @@ -661,6 +669,10 @@ ifdef OLD_ICONV BASIC_CFLAGS += -DOLD_ICONV endif +ifdef NO_DEFLATE_BOUND + BASIC_CFLAGS += -DNO_DEFLATE_BOUND +endif + ifdef PPC_SHA1 SHA1_HEADER = "ppc/sha1.h" LIB_OBJS += ppc/sha1.o ppc/sha1ppc.o @@ -735,6 +747,7 @@ ETC_GITCONFIG_SQ = $(subst ','\'',$(ETC_GITCONFIG)) DESTDIR_SQ = $(subst ','\'',$(DESTDIR)) bindir_SQ = $(subst ','\'',$(bindir)) +mandir_SQ = $(subst ','\'',$(mandir)) gitexecdir_SQ = $(subst ','\'',$(gitexecdir)) template_dir_SQ = $(subst ','\'',$(template_dir)) prefix_SQ = $(subst ','\'',$(prefix)) @@ -746,7 +759,7 @@ TCLTK_PATH_SQ = $(subst ','\'',$(TCLTK_PATH)) LIBS = $(GITLIBS) $(EXTLIBS) BASIC_CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER_SQ)' \ - -DETC_GITCONFIG='"$(ETC_GITCONFIG_SQ)"' $(COMPAT_CFLAGS) + $(COMPAT_CFLAGS) LIB_OBJS += $(COMPAT_OBJS) ALL_CFLAGS += $(BASIC_CFLAGS) @@ -765,6 +778,7 @@ endif all:: ifndef NO_TCLTK $(QUIET_SUBDIR0)git-gui $(QUIET_SUBDIR1) all + $(QUIET_SUBDIR0)gitk-git $(QUIET_SUBDIR1) all endif $(QUIET_SUBDIR0)perl $(QUIET_SUBDIR1) PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' all $(QUIET_SUBDIR0)templates $(QUIET_SUBDIR1) @@ -772,12 +786,6 @@ endif strip: $(PROGRAMS) git$X $(STRIP) $(STRIP_OPTS) $(PROGRAMS) git$X -gitk-wish: gitk GIT-GUI-VARS - $(QUIET_GEN)$(RM) $@ $@+ && \ - sed -e '1,3s|^exec .* "$$0"|exec $(subst |,'\|',$(TCLTK_PATH_SQ)) "$$0"|' <gitk >$@+ && \ - chmod +x $@+ && \ - mv -f $@+ $@ - git.o: git.c common-cmds.h GIT-CFLAGS $(QUIET_CC)$(CC) -DGIT_VERSION='"$(GIT_VERSION)"' \ $(ALL_CFLAGS) -c $(filter %.c,$^) @@ -786,7 +794,8 @@ git$X: git.o $(BUILTIN_OBJS) $(GITLIBS) $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ git.o \ $(BUILTIN_OBJS) $(ALL_LDFLAGS) $(LIBS) -help.o: common-cmds.h +help.o: help.c common-cmds.h GIT-CFLAGS + $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) '-DGIT_MAN_PATH="$(mandir_SQ)"' $< git-merge-subtree$X: git-merge-recursive$X $(QUIET_BUILT_IN)$(RM) $@ && ln git-merge-recursive$X $@ @@ -794,7 +803,7 @@ git-merge-subtree$X: git-merge-recursive$X $(BUILT_INS): git$X $(QUIET_BUILT_IN)$(RM) $@ && ln git$X $@ -common-cmds.h: ./generate-cmdlist.sh +common-cmds.h: ./generate-cmdlist.sh command-list.txt common-cmds.h: $(wildcard Documentation/git-*.txt) $(QUIET_GEN)./generate-cmdlist.sh > $@+ && mv $@+ $@ @@ -830,9 +839,6 @@ $(patsubst %.perl,%,$(SCRIPT_PERL)): % : %.perl chmod +x $@+ && \ mv $@+ $@ -git-status: git-commit - $(QUIET_GEN)cp $< $@+ && mv $@+ $@ - gitweb/gitweb.cgi: gitweb/gitweb.perl $(QUIET_GEN)$(RM) $@ $@+ && \ sed -e '1s|#!.*perl|#!$(PERL_PATH_SQ)|' \ @@ -895,6 +901,9 @@ exec_cmd.o: exec_cmd.c GIT-CFLAGS builtin-init-db.o: builtin-init-db.c GIT-CFLAGS $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DDEFAULT_GIT_TEMPLATE_DIR='"$(template_dir_SQ)"' $< +config.o: config.c GIT-CFLAGS + $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DETC_GITCONFIG='"$(ETC_GITCONFIG_SQ)"' $< + http.o: http.c GIT-CFLAGS $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DGIT_USER_AGENT='"git/$(GIT_VERSION)"' $< @@ -916,6 +925,7 @@ git-http-push$X: revision.o http.o http-push.o $(GITLIBS) $(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H) $(patsubst git-%$X,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h) +builtin-revert.o builtin-runstatus.o wt-status.o: wt-status.h $(LIB_FILE): $(LIB_OBJS) $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(LIB_OBJS) @@ -991,6 +1001,8 @@ test-date$X: date.o ctype.o test-delta$X: diff-delta.o patch-delta.o +test-parse-options$X: parse-options.o + .PRECIOUS: $(patsubst test-%$X,test-%.o,$(TEST_PROGRAMS)) test-%$X: test-%.o $(GITLIBS) @@ -1008,14 +1020,14 @@ remove-dashes: ### Installation rules install: all - $(INSTALL) -d -m755 '$(DESTDIR_SQ)$(bindir_SQ)' - $(INSTALL) -d -m755 '$(DESTDIR_SQ)$(gitexecdir_SQ)' + $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)' + $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(gitexecdir_SQ)' $(INSTALL) $(ALL_PROGRAMS) '$(DESTDIR_SQ)$(gitexecdir_SQ)' $(INSTALL) git$X '$(DESTDIR_SQ)$(bindir_SQ)' $(MAKE) -C templates DESTDIR='$(DESTDIR_SQ)' install $(MAKE) -C perl prefix='$(prefix_SQ)' install ifndef NO_TCLTK - $(INSTALL) gitk-wish '$(DESTDIR_SQ)$(bindir_SQ)'/gitk + $(MAKE) -C gitk-git install $(MAKE) -C git-gui install endif if test 'z$(bindir_SQ)' != 'z$(gitexecdir_SQ)'; \ @@ -1108,7 +1120,7 @@ clean: $(MAKE) -C templates/ clean $(MAKE) -C t/ clean ifndef NO_TCLTK - $(RM) gitk-wish + $(MAKE) -C gitk-git clean $(MAKE) -C git-gui clean endif $(RM) GIT-VERSION-FILE GIT-CFLAGS GIT-GUI-VARS @@ -1119,23 +1131,48 @@ endif ### Check documentation # check-docs:: - @for v in $(ALL_PROGRAMS) $(BUILT_INS) git$X gitk; \ + @(for v in $(ALL_PROGRAMS) $(BUILT_INS) git gitk; \ do \ case "$$v" in \ git-merge-octopus | git-merge-ours | git-merge-recursive | \ - git-merge-resolve | git-merge-stupid | \ + git-merge-resolve | git-merge-stupid | git-merge-subtree | \ git-add--interactive | git-fsck-objects | git-init-db | \ + git-rebase--interactive | \ git-repo-config | git-fetch--tool ) continue ;; \ esac ; \ test -f "Documentation/$$v.txt" || \ echo "no doc: $$v"; \ - sed -e '1,/^__DATA__/d' Documentation/cmd-list.perl | \ + sed -e '/^#/d' command-list.txt | \ grep -q "^$$v[ ]" || \ case "$$v" in \ git) ;; \ *) echo "no link: $$v";; \ esac ; \ - done | sort + done; \ + ( \ + sed -e '/^#/d' \ + -e 's/[ ].*//' \ + -e 's/^/listed /' command-list.txt; \ + ls -1 Documentation/git*txt | \ + sed -e 's|Documentation/|documented |' \ + -e 's/\.txt//'; \ + ) | while read how cmd; \ + do \ + case "$$how,$$cmd" in \ + *,git-citool | \ + *,git-gui | \ + *,git-help | \ + documented,gitattributes | \ + documented,gitignore | \ + documented,gitmodules | \ + documented,git-tools | \ + sentinel,not,matching,is,ok ) continue ;; \ + esac; \ + case " $(ALL_PROGRAMS) $(BUILT_INS) git gitk " in \ + *" $$cmd "*) ;; \ + *) echo "removed but $$how: $$cmd" ;; \ + esac; \ + done ) | sort ### Make sure built-ins do not have dups and listed in git.c # diff --git a/builtin-add.c b/builtin-add.c index 45b14e8a61..5c29cc2f3f 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -19,9 +19,8 @@ static const char * const builtin_add_usage[] = { "git-add [options] [--] <filepattern>...", NULL }; - +static int patch_interactive = 0, add_interactive = 0; static int take_worktree_changes; -static const char *excludes_file; static void prune_directory(struct dir_struct *dir, const char **pathspec, int prefix) { @@ -61,12 +60,7 @@ static void fill_directory(struct dir_struct *dir, const char **pathspec, memset(dir, 0, sizeof(*dir)); if (!ignored_too) { dir->collect_ignored = 1; - dir->exclude_per_dir = ".gitignore"; - path = git_path("info/exclude"); - if (!access(path, R_OK)) - add_excludes_from_file(dir, path); - if (excludes_file != NULL && !access(excludes_file, R_OK)) - add_excludes_from_file(dir, excludes_file); + setup_standard_excludes(dir); } /* @@ -111,16 +105,16 @@ static void update_callback(struct diff_queue_struct *q, } } -void add_files_to_cache(int verbose, const char *prefix, const char **files) +void add_files_to_cache(int verbose, const char *prefix, const char **pathspec) { struct rev_info rev; init_revisions(&rev, prefix); setup_revisions(0, NULL, &rev, NULL); - rev.prune_data = get_pathspec(prefix, files); + rev.prune_data = pathspec; rev.diffopt.output_format = DIFF_FORMAT_CALLBACK; rev.diffopt.format_callback = update_callback; rev.diffopt.format_callback_data = &verbose; - run_diff_files(&rev, 0); + run_diff_files(&rev, DIFF_RACY_IS_MODIFIED); } static void refresh(int verbose, const char **pathspec) @@ -141,23 +135,40 @@ static void refresh(int verbose, const char **pathspec) free(seen); } -static int git_add_config(const char *var, const char *value) +static const char **validate_pathspec(int argc, const char **argv, const char *prefix) { - if (!strcmp(var, "core.excludesfile")) { - if (!value) - die("core.excludesfile without value"); - excludes_file = xstrdup(value); - return 0; - } + const char **pathspec = get_pathspec(prefix, argv); - return git_default_config(var, value); + return pathspec; } -int interactive_add(void) +int interactive_add(int argc, const char **argv, const char *prefix) { - const char *argv[2] = { "add--interactive", NULL }; + int status, ac; + const char **args; + const char **pathspec = NULL; + + if (argc) { + pathspec = validate_pathspec(argc, argv, prefix); + if (!pathspec) + return -1; + } - return run_command_v_opt(argv, RUN_GIT_CMD); + args = xcalloc(sizeof(const char *), (argc + 4)); + ac = 0; + args[ac++] = "add--interactive"; + if (patch_interactive) + args[ac++] = "--patch"; + args[ac++] = "--"; + if (argc) { + memcpy(&(args[ac]), pathspec, sizeof(const char *) * argc); + ac += argc; + } + args[ac] = NULL; + + status = run_command_v_opt(args, RUN_GIT_CMD); + free(args); + return status; } static struct lock_file lock_file; @@ -166,13 +177,13 @@ static const char ignore_error[] = "The following paths are ignored by one of your .gitignore files:\n"; static int verbose = 0, show_only = 0, ignored_too = 0, refresh_only = 0; -static int add_interactive = 0; static struct option builtin_add_options[] = { OPT__DRY_RUN(&show_only), OPT__VERBOSE(&verbose), OPT_GROUP(""), OPT_BOOLEAN('i', "interactive", &add_interactive, "interactive picking"), + OPT_BOOLEAN('p', "patch", &patch_interactive, "interactive patching"), OPT_BOOLEAN('f', NULL, &ignored_too, "allow adding otherwise ignored files"), OPT_BOOLEAN('u', NULL, &take_worktree_changes, "update tracked files"), OPT_BOOLEAN( 0 , "refresh", &refresh_only, "don't add, only refresh the index"), @@ -181,26 +192,27 @@ static struct option builtin_add_options[] = { int cmd_add(int argc, const char **argv, const char *prefix) { - int i, newfd, orig_argc = argc; + int i, newfd; const char **pathspec; struct dir_struct dir; argc = parse_options(argc, argv, builtin_add_options, builtin_add_usage, 0); - if (add_interactive) { - if (add_interactive != 1 || orig_argc != 2) - die("add --interactive does not take any parameters"); - exit(interactive_add()); - } + if (patch_interactive) + add_interactive = 1; + if (add_interactive) + exit(interactive_add(argc, argv, prefix)); - git_config(git_add_config); + git_config(git_default_config); newfd = hold_locked_index(&lock_file, 1); if (take_worktree_changes) { + const char **pathspec; if (read_cache() < 0) die("index file corrupt"); - add_files_to_cache(verbose, prefix, argv); + pathspec = get_pathspec(prefix, argv); + add_files_to_cache(verbose, prefix, pathspec); goto finish; } diff --git a/builtin-apply.c b/builtin-apply.c index ee3ef60268..f2e9a332ca 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -692,7 +692,6 @@ static char *git_header_name(char *line, int llen) } } } - return NULL; } /* Verify that we recognize the lines following a git header */ @@ -2064,7 +2063,7 @@ static int verify_index_match(struct cache_entry *ce, struct stat *st) return -1; return 0; } - return ce_match_stat(ce, st, 1); + return ce_match_stat(ce, st, CE_MATCH_IGNORE_VALID); } static int check_patch(struct patch *patch, struct patch *prev_patch) diff --git a/builtin-blame.c b/builtin-blame.c index 8432b823e6..c158d319dc 100644 --- a/builtin-blame.c +++ b/builtin-blame.c @@ -335,7 +335,7 @@ static struct origin *find_origin(struct scoreboard *sb, * same and diff-tree is fairly efficient about this. */ diff_setup(&diff_opts); - diff_opts.recursive = 1; + DIFF_OPT_SET(&diff_opts, RECURSIVE); diff_opts.detect_rename = 0; diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT; paths[0] = origin->path; @@ -409,7 +409,7 @@ static struct origin *find_rename(struct scoreboard *sb, const char *paths[2]; diff_setup(&diff_opts); - diff_opts.recursive = 1; + DIFF_OPT_SET(&diff_opts, RECURSIVE); diff_opts.detect_rename = DIFF_DETECT_RENAME; diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT; diff_opts.single_follow = origin->path; @@ -1075,7 +1075,7 @@ static int find_copy_in_parent(struct scoreboard *sb, return 1; /* nothing remains for this target */ diff_setup(&diff_opts); - diff_opts.recursive = 1; + DIFF_OPT_SET(&diff_opts, RECURSIVE); diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT; paths[0] = NULL; @@ -1093,7 +1093,7 @@ static int find_copy_in_parent(struct scoreboard *sb, if ((opt & PICKAXE_BLAME_COPY_HARDEST) || ((opt & PICKAXE_BLAME_COPY_HARDER) && (!porigin || strcmp(target->path, porigin->path)))) - diff_opts.find_copies_harder = 1; + DIFF_OPT_SET(&diff_opts, FIND_COPIES_HARDER); if (is_null_sha1(target->commit->object.sha1)) do_diff_cache(parent->tree->object.sha1, &diff_opts); @@ -1102,7 +1102,7 @@ static int find_copy_in_parent(struct scoreboard *sb, target->commit->tree->object.sha1, "", &diff_opts); - if (!diff_opts.find_copies_harder) + if (!DIFF_OPT_TST(&diff_opts, FIND_COPIES_HARDER)) diffcore_std(&diff_opts); retval = 0; @@ -2215,9 +2215,6 @@ int cmd_blame(int argc, const char **argv, const char *prefix) argv[unk++] = arg; } - if (!incremental) - setup_pager(); - if (!blame_move_score) blame_move_score = BLAME_DEFAULT_MOVE_SCORE; if (!blame_copy_score) @@ -2298,6 +2295,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix) else if (i != argc - 1) usage(blame_usage); /* garbage at end */ + setup_work_tree(); if (!has_path_in_work_tree(path)) die("cannot stat path %s: %s", path, strerror(errno)); @@ -2345,6 +2343,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix) * do not default to HEAD, but use the working tree * or "--contents". */ + setup_work_tree(); sb.final = fake_working_tree_commit(path, contents_from); add_pending_object(&revs, &(sb.final->object), ":"); } @@ -2411,6 +2410,9 @@ int cmd_blame(int argc, const char **argv, const char *prefix) read_mailmap(&mailmap, ".mailmap", NULL); + if (!incremental) + setup_pager(); + assign_blame(&sb, &revs, opt); if (incremental) diff --git a/builtin-branch.c b/builtin-branch.c index d6d5cff6b8..089cae5929 100644 --- a/builtin-branch.c +++ b/builtin-branch.c @@ -65,7 +65,7 @@ static int parse_branch_color_slot(const char *var, int ofs) static int git_branch_config(const char *var, const char *value) { if (!strcmp(var, "color.branch")) { - branch_use_color = git_config_colorbool(var, value); + branch_use_color = git_config_colorbool(var, value, -1); return 0; } if (!prefixcmp(var, "color.branch.")) { @@ -148,7 +148,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds) if (!force && !in_merge_bases(rev, &head_rev, 1)) { - error("The branch '%s' is not a strict subset of " + error("The branch '%s' is not an ancestor of " "your current HEAD.\n" "If you are sure you want to delete it, " "run 'git branch -D %s'.", argv[i], argv[i]); @@ -184,9 +184,30 @@ struct ref_item { struct ref_list { int index, alloc, maxwidth; struct ref_item *list; + struct commit_list *with_commit; int kinds; }; +static int has_commit(const unsigned char *sha1, struct commit_list *with_commit) +{ + struct commit *commit; + + if (!with_commit) + return 1; + commit = lookup_commit_reference_gently(sha1, 1); + if (!commit) + return 0; + while (with_commit) { + struct commit *other; + + other = with_commit->item; + with_commit = with_commit->next; + if (in_merge_bases(other, &commit, 1)) + return 1; + } + return 0; +} + static int append_ref(const char *refname, const unsigned char *sha1, int flags, void *cb_data) { struct ref_list *ref_list = (struct ref_list*)(cb_data); @@ -206,6 +227,10 @@ static int append_ref(const char *refname, const unsigned char *sha1, int flags, refname += 10; } + /* Filter with with_commit if specified */ + if (!has_commit(sha1, ref_list->with_commit)) + return 0; + /* Don't add types the caller doesn't want */ if ((kind & ref_list->kinds) == 0) return 0; @@ -282,7 +307,7 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose, commit = lookup_commit(item->sha1); if (commit && !parse_commit(commit)) { pretty_print_commit(CMIT_FMT_ONELINE, commit, - &subject, 0, NULL, NULL, 0); + &subject, 0, NULL, NULL, 0, 0); sub = subject.buf; } printf("%c %s%-*s%s %s %s\n", c, branch_get_color(color), @@ -296,19 +321,20 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose, } } -static void print_ref_list(int kinds, int detached, int verbose, int abbrev) +static void print_ref_list(int kinds, int detached, int verbose, int abbrev, struct commit_list *with_commit) { int i; struct ref_list ref_list; memset(&ref_list, 0, sizeof(ref_list)); ref_list.kinds = kinds; + ref_list.with_commit = with_commit; for_each_ref(append_ref, &ref_list); qsort(ref_list.list, ref_list.index, sizeof(struct ref_item), ref_cmp); detached = (detached && (kinds & REF_LOCAL_BRANCH)); - if (detached) { + if (detached && has_commit(head_sha1, with_commit)) { struct ref_item item; item.name = xstrdup("(no branch)"); item.kind = REF_LOCAL_BRANCH; @@ -505,50 +531,63 @@ static void rename_branch(const char *oldname, const char *newname, int force) die("Branch is renamed, but update of config-file failed"); } +static int opt_parse_with_commit(const struct option *opt, const char *arg, int unset) +{ + unsigned char sha1[20]; + struct commit *commit; + + if (!arg) + return -1; + if (get_sha1(arg, sha1)) + die("malformed object name %s", arg); + commit = lookup_commit_reference(sha1); + if (!commit) + die("no such commit %s", arg); + commit_list_insert(commit, opt->value); + return 0; +} + int cmd_branch(int argc, const char **argv, const char *prefix) { - int delete = 0, force_delete = 0, force_create = 0; - int rename = 0, force_rename = 0; + int delete = 0, rename = 0, force_create = 0; int verbose = 0, abbrev = DEFAULT_ABBREV, detached = 0; int reflog = 0, track; - int kinds = REF_LOCAL_BRANCH, kind_remote = 0, kind_any = 0; + int kinds = REF_LOCAL_BRANCH; + struct commit_list *with_commit = NULL; struct option options[] = { OPT_GROUP("Generic options"), OPT__VERBOSE(&verbose), OPT_BOOLEAN( 0 , "track", &track, "set up tracking mode (see git-pull(1))"), OPT_BOOLEAN( 0 , "color", &branch_use_color, "use colored output"), - OPT_BOOLEAN('r', NULL, &kind_remote, "act on remote-tracking branches"), + OPT_SET_INT('r', NULL, &kinds, "act on remote-tracking branches", + REF_REMOTE_BRANCH), + OPT_CALLBACK(0, "contains", &with_commit, "commit", + "print only branches that contain the commit", + opt_parse_with_commit), + { + OPTION_CALLBACK, 0, "with", &with_commit, "commit", + "print only branches that contain the commit", + PARSE_OPT_HIDDEN, opt_parse_with_commit, + }, OPT__ABBREV(&abbrev), OPT_GROUP("Specific git-branch actions:"), - OPT_BOOLEAN('a', NULL, &kind_any, "list both remote-tracking and local branches"), - OPT_BOOLEAN('d', NULL, &delete, "delete fully merged branch"), - OPT_BOOLEAN('D', NULL, &force_delete, "delete branch (even if not merged)"), - OPT_BOOLEAN('l', NULL, &reflog, "create the branch's reflog"), - OPT_BOOLEAN('f', NULL, &force_create, "force creation (when already exists)"), - OPT_BOOLEAN('m', NULL, &rename, "move/rename a branch and its reflog"), - OPT_BOOLEAN('M', NULL, &force_rename, "move/rename a branch, even if target exists"), + OPT_SET_INT('a', NULL, &kinds, "list both remote-tracking and local branches", + REF_REMOTE_BRANCH | REF_LOCAL_BRANCH), + OPT_BIT('d', NULL, &delete, "delete fully merged branch", 1), + OPT_BIT('D', NULL, &delete, "delete branch (even if not merged)", 2), + OPT_BIT('m', NULL, &rename, "move/rename a branch and its reflog", 1), + OPT_BIT('M', NULL, &rename, "move/rename a branch, even if target exists", 2), + OPT_BOOLEAN('l', NULL, &reflog, "create the branch's reflog"), + OPT_BOOLEAN('f', NULL, &force_create, "force creation (when already exists)"), OPT_END(), }; git_config(git_branch_config); track = branch_track; argc = parse_options(argc, argv, options, builtin_branch_usage, 0); - - delete |= force_delete; - rename |= force_rename; - if (kind_remote) - kinds = REF_REMOTE_BRANCH; - if (kind_any) - kinds = REF_REMOTE_BRANCH | REF_LOCAL_BRANCH; - if (abbrev && abbrev < MINIMUM_ABBREV) - abbrev = MINIMUM_ABBREV; - else if (abbrev > 40) - abbrev = 40; - - if ((delete && rename) || (delete && force_create) || - (rename && force_create)) + if (!!delete + !!rename + !!force_create > 1) usage_with_options(builtin_branch_usage, options); head = resolve_ref("HEAD", head_sha1, 0, NULL); @@ -564,13 +603,13 @@ int cmd_branch(int argc, const char **argv, const char *prefix) } if (delete) - return delete_branches(argc, argv, force_delete, kinds); + return delete_branches(argc, argv, delete > 1, kinds); else if (argc == 0) - print_ref_list(kinds, detached, verbose, abbrev); + print_ref_list(kinds, detached, verbose, abbrev, with_commit); else if (rename && (argc == 1)) - rename_branch(head, argv[0], force_rename); + rename_branch(head, argv[0], rename > 1); else if (rename && (argc == 2)) - rename_branch(argv[0], argv[1], force_rename); + rename_branch(argv[0], argv[1], rename > 1); else if (argc <= 2) create_branch(argv[0], (argc == 2) ? argv[1] : head, force_create, reflog, track); diff --git a/builtin-clean.c b/builtin-clean.c new file mode 100644 index 0000000000..ae30d4e76c --- /dev/null +++ b/builtin-clean.c @@ -0,0 +1,163 @@ +/* + * "git clean" builtin command + * + * Copyright (C) 2007 Shawn Bohrer + * + * Based on git-clean.sh by Pavel Roskin + */ + +#include "builtin.h" +#include "cache.h" +#include "dir.h" +#include "parse-options.h" + +static int force = -1; /* unset */ + +static const char *const builtin_clean_usage[] = { + "git-clean [-d] [-f] [-n] [-q] [-x | -X] [--] <paths>...", + NULL +}; + +static int git_clean_config(const char *var, const char *value) +{ + if (!strcmp(var, "clean.requireforce")) + force = !git_config_bool(var, value); + return git_default_config(var, value); +} + +int cmd_clean(int argc, const char **argv, const char *prefix) +{ + int i; + int show_only = 0, remove_directories = 0, quiet = 0, ignored = 0; + int ignored_only = 0, baselen = 0, config_set = 0; + struct strbuf directory; + struct dir_struct dir; + const char *path, *base; + static const char **pathspec; + char *seen = NULL; + struct option options[] = { + OPT__QUIET(&quiet), + OPT__DRY_RUN(&show_only), + OPT_BOOLEAN('f', NULL, &force, "force"), + OPT_BOOLEAN('d', NULL, &remove_directories, + "remove whole directories"), + OPT_BOOLEAN('x', NULL, &ignored, "remove ignored files, too"), + OPT_BOOLEAN('X', NULL, &ignored_only, + "remove only ignored files"), + OPT_END() + }; + + git_config(git_clean_config); + if (force < 0) + force = 0; + else + config_set = 1; + + argc = parse_options(argc, argv, options, builtin_clean_usage, 0); + + memset(&dir, 0, sizeof(dir)); + if (ignored_only) + dir.show_ignored = 1; + + if (ignored && ignored_only) + die("-x and -X cannot be used together"); + + if (!show_only && !force) + die("clean.requireForce%s set and -n or -f not given; " + "refusing to clean", config_set ? "" : " not"); + + dir.show_other_directories = 1; + + if (!ignored) + setup_standard_excludes(&dir); + + pathspec = get_pathspec(prefix, argv); + read_cache(); + + /* + * Calculate common prefix for the pathspec, and + * use that to optimize the directory walk + */ + baselen = common_prefix(pathspec); + path = "."; + base = ""; + if (baselen) + path = base = xmemdupz(*pathspec, baselen); + read_directory(&dir, path, base, baselen, pathspec); + strbuf_init(&directory, 0); + + if (pathspec) + seen = xmalloc(argc); + + for (i = 0; i < dir.nr; i++) { + struct dir_entry *ent = dir.entries[i]; + int len, pos, matches; + struct cache_entry *ce; + struct stat st; + + /* + * Remove the '/' at the end that directory + * walking adds for directory entries. + */ + len = ent->len; + if (len && ent->name[len-1] == '/') + len--; + pos = cache_name_pos(ent->name, len); + if (0 <= pos) + continue; /* exact match */ + pos = -pos - 1; + if (pos < active_nr) { + ce = active_cache[pos]; + if (ce_namelen(ce) == len && + !memcmp(ce->name, ent->name, len)) + continue; /* Yup, this one exists unmerged */ + } + + /* + * we might have removed this as part of earlier + * recursive directory removal, so lstat() here could + * fail with ENOENT. + */ + if (lstat(ent->name, &st)) + continue; + + if (pathspec) { + memset(seen, 0, argc); + matches = match_pathspec(pathspec, ent->name, ent->len, + baselen, seen); + } else { + matches = 0; + } + + if (S_ISDIR(st.st_mode)) { + strbuf_addstr(&directory, ent->name); + if (show_only && (remove_directories || matches)) { + printf("Would remove %s\n", directory.buf); + } else if (quiet && (remove_directories || matches)) { + remove_dir_recursively(&directory, 0); + } else if (remove_directories || matches) { + printf("Removing %s\n", directory.buf); + remove_dir_recursively(&directory, 0); + } else if (show_only) { + printf("Would not remove %s\n", directory.buf); + } else { + printf("Not removing %s\n", directory.buf); + } + strbuf_reset(&directory); + } else { + if (pathspec && !matches) + continue; + if (show_only) { + printf("Would remove %s\n", ent->name); + continue; + } else if (!quiet) { + printf("Removing %s\n", ent->name); + } + unlink(ent->name); + } + } + free(seen); + + strbuf_release(&directory); + return 0; +} diff --git a/builtin-commit-tree.c b/builtin-commit-tree.c index 88b0ab36eb..6610d18358 100644 --- a/builtin-commit-tree.c +++ b/builtin-commit-tree.c @@ -98,8 +98,8 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix) strbuf_addf(&buffer, "parent %s\n", sha1_to_hex(parent_sha1[i])); /* Person/date information */ - strbuf_addf(&buffer, "author %s\n", git_author_info(1)); - strbuf_addf(&buffer, "committer %s\n", git_committer_info(1)); + strbuf_addf(&buffer, "author %s\n", git_author_info(IDENT_ERROR_ON_NO_NAME)); + strbuf_addf(&buffer, "committer %s\n", git_committer_info(IDENT_ERROR_ON_NO_NAME)); if (!encoding_is_utf8) strbuf_addf(&buffer, "encoding %s\n", git_commit_encoding); strbuf_addch(&buffer, '\n'); diff --git a/builtin-commit.c b/builtin-commit.c new file mode 100644 index 0000000000..b6b81d531d --- /dev/null +++ b/builtin-commit.c @@ -0,0 +1,854 @@ +/* + * Builtin "git commit" + * + * Copyright (c) 2007 Kristian Høgsberg <krh@redhat.com> + * Based on git-commit.sh by Junio C Hamano and Linus Torvalds + */ + +#include "cache.h" +#include "cache-tree.h" +#include "dir.h" +#include "builtin.h" +#include "diff.h" +#include "diffcore.h" +#include "commit.h" +#include "revision.h" +#include "wt-status.h" +#include "run-command.h" +#include "refs.h" +#include "log-tree.h" +#include "strbuf.h" +#include "utf8.h" +#include "parse-options.h" +#include "path-list.h" + +static const char * const builtin_commit_usage[] = { + "git-commit [options] [--] <filepattern>...", + NULL +}; + +static const char * const builtin_status_usage[] = { + "git-status [options] [--] <filepattern>...", + NULL +}; + +static unsigned char head_sha1[20], merge_head_sha1[20]; +static char *use_message_buffer; +static const char commit_editmsg[] = "COMMIT_EDITMSG"; +static struct lock_file index_lock; /* real index */ +static struct lock_file false_lock; /* used only for partial commits */ +static enum { + COMMIT_AS_IS = 1, + COMMIT_NORMAL, + COMMIT_PARTIAL, +} commit_style; + +static char *logfile, *force_author, *template_file; +static char *edit_message, *use_message; +static int all, edit_flag, also, interactive, only, amend, signoff; +static int quiet, verbose, untracked_files, no_verify, allow_empty; + +static int no_edit, initial_commit, in_merge; +const char *only_include_assumed; +struct strbuf message; + +static int opt_parse_m(const struct option *opt, const char *arg, int unset) +{ + struct strbuf *buf = opt->value; + if (unset) + strbuf_setlen(buf, 0); + else { + strbuf_addstr(buf, arg); + strbuf_addch(buf, '\n'); + strbuf_addch(buf, '\n'); + } + return 0; +} + +static struct option builtin_commit_options[] = { + OPT__QUIET(&quiet), + OPT__VERBOSE(&verbose), + OPT_GROUP("Commit message options"), + + OPT_STRING('F', "file", &logfile, "FILE", "read log from file"), + OPT_STRING(0, "author", &force_author, "AUTHOR", "override author for commit"), + OPT_CALLBACK('m', "message", &message, "MESSAGE", "specify commit message", opt_parse_m), + OPT_STRING('c', "reedit-message", &edit_message, "COMMIT", "reuse and edit message from specified commit "), + OPT_STRING('C', "reuse-message", &use_message, "COMMIT", "reuse message from specified commit"), + OPT_BOOLEAN('s', "signoff", &signoff, "add Signed-off-by: header"), + OPT_STRING('t', "template", &template_file, "FILE", "use specified template file"), + OPT_BOOLEAN('e', "edit", &edit_flag, "force edit of commit"), + + OPT_GROUP("Commit contents options"), + OPT_BOOLEAN('a', "all", &all, "commit all changed files"), + OPT_BOOLEAN('i', "include", &also, "add specified files to index for commit"), + OPT_BOOLEAN(0, "interactive", &interactive, "interactively add files"), + OPT_BOOLEAN('o', "only", &only, ""), + OPT_BOOLEAN('n', "no-verify", &no_verify, "bypass pre-commit hook"), + OPT_BOOLEAN(0, "amend", &amend, "amend previous commit"), + OPT_BOOLEAN(0, "untracked-files", &untracked_files, "show all untracked files"), + OPT_BOOLEAN(0, "allow-empty", &allow_empty, "ok to record an empty change"), + + OPT_END() +}; + +static void rollback_index_files(void) +{ + switch (commit_style) { + case COMMIT_AS_IS: + break; /* nothing to do */ + case COMMIT_NORMAL: + rollback_lock_file(&index_lock); + break; + case COMMIT_PARTIAL: + rollback_lock_file(&index_lock); + rollback_lock_file(&false_lock); + break; + } +} + +static void commit_index_files(void) +{ + switch (commit_style) { + case COMMIT_AS_IS: + break; /* nothing to do */ + case COMMIT_NORMAL: + commit_lock_file(&index_lock); + break; + case COMMIT_PARTIAL: + commit_lock_file(&index_lock); + rollback_lock_file(&false_lock); + break; + } +} + +/* + * Take a union of paths in the index and the named tree (typically, "HEAD"), + * and return the paths that match the given pattern in list. + */ +static int list_paths(struct path_list *list, const char *with_tree, + const char *prefix, const char **pattern) +{ + int i; + char *m; + + for (i = 0; pattern[i]; i++) + ; + m = xcalloc(1, i); + + if (with_tree) + overlay_tree_on_cache(with_tree, prefix); + + for (i = 0; i < active_nr; i++) { + struct cache_entry *ce = active_cache[i]; + if (ce->ce_flags & htons(CE_UPDATE)) + continue; + if (!pathspec_match(pattern, m, ce->name, 0)) + continue; + path_list_insert(ce->name, list); + } + + return report_path_error(m, pattern, prefix ? strlen(prefix) : 0); +} + +static void add_remove_files(struct path_list *list) +{ + int i; + for (i = 0; i < list->nr; i++) { + struct path_list_item *p = &(list->items[i]); + if (file_exists(p->path)) + add_file_to_cache(p->path, 0); + else + remove_file_from_cache(p->path); + } +} + +static char *prepare_index(int argc, const char **argv, const char *prefix) +{ + int fd; + struct tree *tree; + struct path_list partial; + const char **pathspec = NULL; + + if (interactive) { + interactive_add(argc, argv, prefix); + commit_style = COMMIT_AS_IS; + return get_index_file(); + } + + if (read_cache() < 0) + die("index file corrupt"); + + if (*argv) + pathspec = get_pathspec(prefix, argv); + + /* + * Non partial, non as-is commit. + * + * (1) get the real index; + * (2) update the_index as necessary; + * (3) write the_index out to the real index (still locked); + * (4) return the name of the locked index file. + * + * The caller should run hooks on the locked real index, and + * (A) if all goes well, commit the real index; + * (B) on failure, rollback the real index. + */ + if (all || (also && pathspec && *pathspec)) { + int fd = hold_locked_index(&index_lock, 1); + add_files_to_cache(0, also ? prefix : NULL, pathspec); + refresh_cache(REFRESH_QUIET); + if (write_cache(fd, active_cache, active_nr) || close(fd)) + die("unable to write new_index file"); + commit_style = COMMIT_NORMAL; + return index_lock.filename; + } + + /* + * As-is commit. + * + * (1) return the name of the real index file. + * + * The caller should run hooks on the real index, and run + * hooks on the real index, and create commit from the_index. + * We still need to refresh the index here. + */ + if (!pathspec || !*pathspec) { + fd = hold_locked_index(&index_lock, 1); + refresh_cache(REFRESH_QUIET); + if (write_cache(fd, active_cache, active_nr) || + close(fd) || commit_locked_index(&index_lock)) + die("unable to write new_index file"); + commit_style = COMMIT_AS_IS; + return get_index_file(); + } + + /* + * A partial commit. + * + * (0) find the set of affected paths; + * (1) get lock on the real index file; + * (2) update the_index with the given paths; + * (3) write the_index out to the real index (still locked); + * (4) get lock on the false index file; + * (5) reset the_index from HEAD; + * (6) update the_index the same way as (2); + * (7) write the_index out to the false index file; + * (8) return the name of the false index file (still locked); + * + * The caller should run hooks on the locked false index, and + * create commit from it. Then + * (A) if all goes well, commit the real index; + * (B) on failure, rollback the real index; + * In either case, rollback the false index. + */ + commit_style = COMMIT_PARTIAL; + + if (file_exists(git_path("MERGE_HEAD"))) + die("cannot do a partial commit during a merge."); + + memset(&partial, 0, sizeof(partial)); + partial.strdup_paths = 1; + if (list_paths(&partial, initial_commit ? NULL : "HEAD", prefix, pathspec)) + exit(1); + + discard_cache(); + if (read_cache() < 0) + die("cannot read the index"); + + fd = hold_locked_index(&index_lock, 1); + add_remove_files(&partial); + refresh_cache(REFRESH_QUIET); + if (write_cache(fd, active_cache, active_nr) || close(fd)) + die("unable to write new_index file"); + + fd = hold_lock_file_for_update(&false_lock, + git_path("next-index-%d", getpid()), 1); + discard_cache(); + if (!initial_commit) { + tree = parse_tree_indirect(head_sha1); + if (!tree) + die("failed to unpack HEAD tree object"); + if (read_tree(tree, 0, NULL)) + die("failed to read HEAD tree object"); + } + add_remove_files(&partial); + refresh_cache(REFRESH_QUIET); + + if (write_cache(fd, active_cache, active_nr) || close(fd)) + die("unable to write temporary index file"); + return false_lock.filename; +} + +static int run_status(FILE *fp, const char *index_file, const char *prefix) +{ + struct wt_status s; + + wt_status_prepare(&s); + if (wt_status_relative_paths) + s.prefix = prefix; + + if (amend) { + s.amend = 1; + s.reference = "HEAD^1"; + } + s.verbose = verbose; + s.untracked = untracked_files; + s.index_file = index_file; + s.fp = fp; + + wt_status_print(&s); + + return s.commitable; +} + +static const char sign_off_header[] = "Signed-off-by: "; + +static int prepare_log_message(const char *index_file, const char *prefix) +{ + struct stat statbuf; + int commitable, saved_color_setting; + struct strbuf sb; + char *buffer; + FILE *fp; + + strbuf_init(&sb, 0); + if (message.len) { + strbuf_addbuf(&sb, &message); + } else if (logfile && !strcmp(logfile, "-")) { + if (isatty(0)) + fprintf(stderr, "(reading log message from standard input)\n"); + if (strbuf_read(&sb, 0, 0) < 0) + die("could not read log from standard input"); + } else if (logfile) { + if (strbuf_read_file(&sb, logfile, 0) < 0) + die("could not read log file '%s': %s", + logfile, strerror(errno)); + } else if (use_message) { + buffer = strstr(use_message_buffer, "\n\n"); + if (!buffer || buffer[2] == '\0') + die("commit has empty message"); + strbuf_add(&sb, buffer + 2, strlen(buffer + 2)); + } else if (!stat(git_path("MERGE_MSG"), &statbuf)) { + if (strbuf_read_file(&sb, git_path("MERGE_MSG"), 0) < 0) + die("could not read MERGE_MSG: %s", strerror(errno)); + } else if (!stat(git_path("SQUASH_MSG"), &statbuf)) { + if (strbuf_read_file(&sb, git_path("SQUASH_MSG"), 0) < 0) + die("could not read SQUASH_MSG: %s", strerror(errno)); + } else if (template_file && !stat(template_file, &statbuf)) { + if (strbuf_read_file(&sb, template_file, 0) < 0) + die("could not read %s: %s", + template_file, strerror(errno)); + } + + fp = fopen(git_path(commit_editmsg), "w"); + if (fp == NULL) + die("could not open %s", git_path(commit_editmsg)); + + stripspace(&sb, 0); + + if (signoff) { + struct strbuf sob; + int i; + + strbuf_init(&sob, 0); + strbuf_addstr(&sob, sign_off_header); + strbuf_addstr(&sob, fmt_name(getenv("GIT_COMMITTER_NAME"), + getenv("GIT_COMMITTER_EMAIL"))); + strbuf_addch(&sob, '\n'); + for (i = sb.len - 1; i > 0 && sb.buf[i - 1] != '\n'; i--) + ; /* do nothing */ + if (prefixcmp(sb.buf + i, sob.buf)) { + if (prefixcmp(sb.buf + i, sign_off_header)) + strbuf_addch(&sb, '\n'); + strbuf_addbuf(&sb, &sob); + } + strbuf_release(&sob); + } + + if (fwrite(sb.buf, 1, sb.len, fp) < sb.len) + die("could not write commit template: %s", strerror(errno)); + + strbuf_release(&sb); + + if (no_edit) { + struct rev_info rev; + unsigned char sha1[40]; + + fclose(fp); + + if (!active_nr && read_cache() < 0) + die("Cannot read index"); + + if (get_sha1("HEAD", sha1) != 0) + return !!active_nr; + + init_revisions(&rev, ""); + rev.abbrev = 0; + setup_revisions(0, NULL, &rev, "HEAD"); + DIFF_OPT_SET(&rev.diffopt, QUIET); + DIFF_OPT_SET(&rev.diffopt, EXIT_WITH_STATUS); + run_diff_index(&rev, 1 /* cached */); + + return !!DIFF_OPT_TST(&rev.diffopt, HAS_CHANGES); + } + + if (in_merge && !no_edit) + fprintf(fp, + "#\n" + "# It looks like you may be committing a MERGE.\n" + "# If this is not correct, please remove the file\n" + "# %s\n" + "# and try again.\n" + "#\n", + git_path("MERGE_HEAD")); + + fprintf(fp, + "\n" + "# Please enter the commit message for your changes.\n" + "# (Comment lines starting with '#' will not be included)\n"); + if (only_include_assumed) + fprintf(fp, "# %s\n", only_include_assumed); + + saved_color_setting = wt_status_use_color; + wt_status_use_color = 0; + commitable = run_status(fp, index_file, prefix); + wt_status_use_color = saved_color_setting; + + fclose(fp); + + return commitable; +} + +/* + * Find out if the message starting at position 'start' in the strbuf + * contains only whitespace and Signed-off-by lines. + */ +static int message_is_empty(struct strbuf *sb, int start) +{ + struct strbuf tmpl; + const char *nl; + int eol, i; + + /* See if the template is just a prefix of the message. */ + strbuf_init(&tmpl, 0); + if (template_file && strbuf_read_file(&tmpl, template_file, 0) > 0) { + stripspace(&tmpl, 1); + if (start + tmpl.len <= sb->len && + memcmp(tmpl.buf, sb->buf + start, tmpl.len) == 0) + start += tmpl.len; + } + strbuf_release(&tmpl); + + /* Check if the rest is just whitespace and Signed-of-by's. */ + for (i = start; i < sb->len; i++) { + nl = memchr(sb->buf + i, '\n', sb->len - i); + if (nl) + eol = nl - sb->buf; + else + eol = sb->len; + + if (strlen(sign_off_header) <= eol - i && + !prefixcmp(sb->buf + i, sign_off_header)) { + i = eol; + continue; + } + while (i < eol) + if (!isspace(sb->buf[i++])) + return 0; + } + + return 1; +} + +static void determine_author_info(struct strbuf *sb) +{ + char *name, *email, *date; + + name = getenv("GIT_AUTHOR_NAME"); + email = getenv("GIT_AUTHOR_EMAIL"); + date = getenv("GIT_AUTHOR_DATE"); + + if (use_message) { + const char *a, *lb, *rb, *eol; + + a = strstr(use_message_buffer, "\nauthor "); + if (!a) + die("invalid commit: %s", use_message); + + lb = strstr(a + 8, " <"); + rb = strstr(a + 8, "> "); + eol = strchr(a + 8, '\n'); + if (!lb || !rb || !eol) + die("invalid commit: %s", use_message); + + name = xstrndup(a + 8, lb - (a + 8)); + email = xstrndup(lb + 2, rb - (lb + 2)); + date = xstrndup(rb + 2, eol - (rb + 2)); + } + + if (force_author) { + const char *lb = strstr(force_author, " <"); + const char *rb = strchr(force_author, '>'); + + if (!lb || !rb) + die("malformed --author parameter"); + name = xstrndup(force_author, lb - force_author); + email = xstrndup(lb + 2, rb - (lb + 2)); + } + + strbuf_addf(sb, "author %s\n", fmt_ident(name, email, date, IDENT_ERROR_ON_NO_NAME)); +} + +static int parse_and_validate_options(int argc, const char *argv[], + const char * const usage[]) +{ + int f = 0; + + argc = parse_options(argc, argv, builtin_commit_options, usage, 0); + + if (logfile || message.len || use_message) + no_edit = 1; + if (edit_flag) + no_edit = 0; + + if (get_sha1("HEAD", head_sha1)) + initial_commit = 1; + + if (!get_sha1("MERGE_HEAD", merge_head_sha1)) + in_merge = 1; + + /* Sanity check options */ + if (amend && initial_commit) + die("You have nothing to amend."); + if (amend && in_merge) + die("You are in the middle of a merge -- cannot amend."); + + if (use_message) + f++; + if (edit_message) + f++; + if (logfile) + f++; + if (f > 1) + die("Only one of -c/-C/-F can be used."); + if (message.len && f > 0) + die("Option -m cannot be combined with -c/-C/-F."); + if (edit_message) + use_message = edit_message; + if (amend) + use_message = "HEAD"; + if (use_message) { + unsigned char sha1[20]; + static char utf8[] = "UTF-8"; + const char *out_enc; + char *enc, *end; + struct commit *commit; + + if (get_sha1(use_message, sha1)) + die("could not lookup commit %s", use_message); + commit = lookup_commit(sha1); + if (!commit || parse_commit(commit)) + die("could not parse commit %s", use_message); + + enc = strstr(commit->buffer, "\nencoding"); + if (enc) { + end = strchr(enc + 10, '\n'); + enc = xstrndup(enc + 10, end - (enc + 10)); + } else { + enc = utf8; + } + out_enc = git_commit_encoding ? git_commit_encoding : utf8; + + if (strcmp(out_enc, enc)) + use_message_buffer = + reencode_string(commit->buffer, out_enc, enc); + + /* + * If we failed to reencode the buffer, just copy it + * byte for byte so the user can try to fix it up. + * This also handles the case where input and output + * encodings are identical. + */ + if (use_message_buffer == NULL) + use_message_buffer = xstrdup(commit->buffer); + if (enc != utf8) + free(enc); + } + + if (!!also + !!only + !!all + !!interactive > 1) + die("Only one of --include/--only/--all/--interactive can be used."); + if (argc == 0 && (also || (only && !amend))) + die("No paths with --include/--only does not make sense."); + if (argc == 0 && only && amend) + only_include_assumed = "Clever... amending the last one with dirty index."; + if (argc > 0 && !also && !only) { + only_include_assumed = "Explicit paths specified without -i nor -o; assuming --only paths..."; + also = 0; + } + + if (all && argc > 0) + die("Paths with -a does not make sense."); + else if (interactive && argc > 0) + die("Paths with --interactive does not make sense."); + + return argc; +} + +int cmd_status(int argc, const char **argv, const char *prefix) +{ + const char *index_file; + int commitable; + + git_config(git_status_config); + + argc = parse_and_validate_options(argc, argv, builtin_status_usage); + + index_file = prepare_index(argc, argv, prefix); + + commitable = run_status(stdout, index_file, prefix); + + rollback_index_files(); + + return commitable ? 0 : 1; +} + +static int run_hook(const char *index_file, const char *name, const char *arg) +{ + struct child_process hook; + const char *argv[3], *env[2]; + char index[PATH_MAX]; + + argv[0] = git_path("hooks/%s", name); + argv[1] = arg; + argv[2] = NULL; + snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file); + env[0] = index; + env[1] = NULL; + + if (access(argv[0], X_OK) < 0) + return 0; + + memset(&hook, 0, sizeof(hook)); + hook.argv = argv; + hook.no_stdin = 1; + hook.stdout_to_stderr = 1; + hook.env = env; + + return run_command(&hook); +} + +static void print_summary(const char *prefix, const unsigned char *sha1) +{ + struct rev_info rev; + struct commit *commit; + + commit = lookup_commit(sha1); + if (!commit) + die("couldn't look up newly created commit"); + if (!commit || parse_commit(commit)) + die("could not parse newly created commit"); + + init_revisions(&rev, prefix); + setup_revisions(0, NULL, &rev, NULL); + + rev.abbrev = 0; + rev.diff = 1; + rev.diffopt.output_format = + DIFF_FORMAT_SHORTSTAT | DIFF_FORMAT_SUMMARY; + + rev.verbose_header = 1; + rev.show_root_diff = 1; + rev.commit_format = get_commit_format("format:%h: %s"); + rev.always_show_header = 1; + + printf("Created %scommit ", initial_commit ? "initial " : ""); + + log_tree_commit(&rev, commit); + printf("\n"); +} + +int git_commit_config(const char *k, const char *v) +{ + if (!strcmp(k, "commit.template")) { + template_file = xstrdup(v); + return 0; + } + + return git_status_config(k, v); +} + +static int is_a_merge(const unsigned char *sha1) +{ + struct commit *commit = lookup_commit(sha1); + if (!commit || parse_commit(commit)) + die("could not parse HEAD commit"); + return !!(commit->parents && commit->parents->next); +} + +static const char commit_utf8_warn[] = +"Warning: commit message does not conform to UTF-8.\n" +"You may want to amend it after fixing the message, or set the config\n" +"variable i18n.commitencoding to the encoding your project uses.\n"; + +int cmd_commit(int argc, const char **argv, const char *prefix) +{ + int header_len; + struct strbuf sb; + const char *index_file, *reflog_msg; + char *nl, *p; + unsigned char commit_sha1[20]; + struct ref_lock *ref_lock; + + git_config(git_commit_config); + + argc = parse_and_validate_options(argc, argv, builtin_commit_usage); + + index_file = prepare_index(argc, argv, prefix); + + if (!no_verify && run_hook(index_file, "pre-commit", NULL)) { + rollback_index_files(); + return 1; + } + + if (!prepare_log_message(index_file, prefix) && !in_merge && + !allow_empty && !(amend && is_a_merge(head_sha1))) { + run_status(stdout, index_file, prefix); + rollback_index_files(); + unlink(commit_editmsg); + return 1; + } + + /* + * Re-read the index as pre-commit hook could have updated it, + * and write it out as a tree. + */ + discard_cache(); + read_cache_from(index_file); + if (!active_cache_tree) + active_cache_tree = cache_tree(); + if (cache_tree_update(active_cache_tree, + active_cache, active_nr, 0, 0) < 0) { + rollback_index_files(); + die("Error building trees"); + } + + /* + * The commit object + */ + strbuf_init(&sb, 0); + strbuf_addf(&sb, "tree %s\n", + sha1_to_hex(active_cache_tree->sha1)); + + /* Determine parents */ + if (initial_commit) { + reflog_msg = "commit (initial)"; + } else if (amend) { + struct commit_list *c; + struct commit *commit; + + reflog_msg = "commit (amend)"; + commit = lookup_commit(head_sha1); + if (!commit || parse_commit(commit)) + die("could not parse HEAD commit"); + + for (c = commit->parents; c; c = c->next) + strbuf_addf(&sb, "parent %s\n", + sha1_to_hex(c->item->object.sha1)); + } else if (in_merge) { + struct strbuf m; + FILE *fp; + + reflog_msg = "commit (merge)"; + strbuf_addf(&sb, "parent %s\n", sha1_to_hex(head_sha1)); + strbuf_init(&m, 0); + fp = fopen(git_path("MERGE_HEAD"), "r"); + if (fp == NULL) + die("could not open %s for reading: %s", + git_path("MERGE_HEAD"), strerror(errno)); + while (strbuf_getline(&m, fp, '\n') != EOF) + strbuf_addf(&sb, "parent %s\n", m.buf); + fclose(fp); + strbuf_release(&m); + } else { + reflog_msg = "commit"; + strbuf_addf(&sb, "parent %s\n", sha1_to_hex(head_sha1)); + } + + determine_author_info(&sb); + strbuf_addf(&sb, "committer %s\n", git_committer_info(IDENT_ERROR_ON_NO_NAME)); + if (!is_encoding_utf8(git_commit_encoding)) + strbuf_addf(&sb, "encoding %s\n", git_commit_encoding); + strbuf_addch(&sb, '\n'); + + /* Get the commit message and validate it */ + header_len = sb.len; + if (!no_edit) { + char index[PATH_MAX]; + const char *env[2] = { index, NULL }; + snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file); + launch_editor(git_path(commit_editmsg), NULL, env); + } + if (!no_verify && + run_hook(index_file, "commit-msg", git_path(commit_editmsg))) { + rollback_index_files(); + exit(1); + } + if (strbuf_read_file(&sb, git_path(commit_editmsg), 0) < 0) { + rollback_index_files(); + die("could not read commit message"); + } + + /* Truncate the message just before the diff, if any. */ + p = strstr(sb.buf, "\ndiff --git a/"); + if (p != NULL) + strbuf_setlen(&sb, p - sb.buf + 1); + + stripspace(&sb, 1); + if (sb.len < header_len || message_is_empty(&sb, header_len)) { + rollback_index_files(); + die("no commit message? aborting commit."); + } + strbuf_addch(&sb, '\0'); + if (is_encoding_utf8(git_commit_encoding) && !is_utf8(sb.buf)) + fprintf(stderr, commit_utf8_warn); + + if (write_sha1_file(sb.buf, sb.len - 1, commit_type, commit_sha1)) { + rollback_index_files(); + die("failed to write commit object"); + } + + ref_lock = lock_any_ref_for_update("HEAD", + initial_commit ? NULL : head_sha1, + 0); + + nl = strchr(sb.buf + header_len, '\n'); + if (nl) + strbuf_setlen(&sb, nl + 1 - sb.buf); + else + strbuf_addch(&sb, '\n'); + strbuf_remove(&sb, 0, header_len); + strbuf_insert(&sb, 0, reflog_msg, strlen(reflog_msg)); + strbuf_insert(&sb, strlen(reflog_msg), ": ", 2); + + if (!ref_lock) { + rollback_index_files(); + die("cannot lock HEAD ref"); + } + if (write_ref_sha1(ref_lock, commit_sha1, sb.buf) < 0) { + rollback_index_files(); + die("cannot update HEAD ref"); + } + + unlink(git_path("MERGE_HEAD")); + unlink(git_path("MERGE_MSG")); + + commit_index_files(); + + rerere(); + run_hook(get_index_file(), "post-commit", NULL); + if (!quiet) + print_summary(prefix, commit_sha1); + + return 0; +} diff --git a/builtin-config.c b/builtin-config.c index e5e243f27c..e4a12e3166 100644 --- a/builtin-config.c +++ b/builtin-config.c @@ -1,8 +1,9 @@ #include "builtin.h" #include "cache.h" +#include "color.h" static const char git_config_set_usage[] = -"git-config [ --global | --system | [ -f | --file ] config-file ] [ --bool | --int ] [ -z | --null ] [--get | --get-all | --get-regexp | --replace-all | --add | --unset | --unset-all] name [value [value_regex]] | --rename-section old_name new_name | --remove-section name | --list"; +"git-config [ --global | --system | [ -f | --file ] config-file ] [ --bool | --int ] [ -z | --null ] [--get | --get-all | --get-regexp | --replace-all | --add | --unset | --unset-all] name [value [value_regex]] | --rename-section old_name new_name | --remove-section name | --list | --get-color var [default] | --get-colorbool name [stdout-is-tty]"; static char *key; static regex_t *key_regexp; @@ -37,8 +38,7 @@ static int show_config(const char* key_, const char* value_) if (use_key_regexp && regexec(key_regexp, key_, 0, NULL, 0)) return 0; if (regexp != NULL && - (do_not_match ^ - regexec(regexp, (value_?value_:""), 0, NULL, 0))) + (do_not_match ^ !!regexec(regexp, (value_?value_:""), 0, NULL, 0))) return 0; if (show_keys) { @@ -81,7 +81,7 @@ static int get_value(const char* key_, const char* regex_) local = repo_config = xstrdup(git_path("config")); if (home) global = xstrdup(mkpath("%s/.gitconfig", home)); - system_wide = ETC_GITCONFIG; + system_wide = git_etc_gitconfig(); } key = xstrdup(key_); @@ -161,6 +161,104 @@ char *normalize_value(const char *key, const char *value) return normalized; } +static int get_color_found; +static const char *get_color_slot; +static char parsed_color[COLOR_MAXLEN]; + +static int git_get_color_config(const char *var, const char *value) +{ + if (!strcmp(var, get_color_slot)) { + color_parse(value, var, parsed_color); + get_color_found = 1; + } + return 0; +} + +static int get_color(int argc, const char **argv) +{ + /* + * grab the color setting for the given slot from the configuration, + * or parse the default value if missing, and return ANSI color + * escape sequence. + * + * e.g. + * git config --get-color color.diff.whitespace "blue reverse" + */ + const char *def_color = NULL; + + switch (argc) { + default: + usage(git_config_set_usage); + case 2: + def_color = argv[1]; + /* fallthru */ + case 1: + get_color_slot = argv[0]; + break; + } + + get_color_found = 0; + parsed_color[0] = '\0'; + git_config(git_get_color_config); + + if (!get_color_found && def_color) + color_parse(def_color, "command line", parsed_color); + + fputs(parsed_color, stdout); + return 0; +} + +static int stdout_is_tty; +static int get_colorbool_found; +static int get_diff_color_found; +static int git_get_colorbool_config(const char *var, const char *value) +{ + if (!strcmp(var, get_color_slot)) { + get_colorbool_found = + git_config_colorbool(var, value, stdout_is_tty); + } + if (!strcmp(var, "diff.color")) { + get_diff_color_found = + git_config_colorbool(var, value, stdout_is_tty); + } + return 0; +} + +static int get_colorbool(int argc, const char **argv) +{ + /* + * git config --get-colorbool <slot> [<stdout-is-tty>] + * + * returns "true" or "false" depending on how <slot> + * is configured. + */ + + if (argc == 2) + stdout_is_tty = git_config_bool("command line", argv[1]); + else if (argc == 1) + stdout_is_tty = isatty(1); + else + usage(git_config_set_usage); + get_colorbool_found = -1; + get_diff_color_found = -1; + get_color_slot = argv[0]; + git_config(git_get_colorbool_config); + + if (get_colorbool_found < 0) { + if (!strcmp(get_color_slot, "color.diff")) + get_colorbool_found = get_diff_color_found; + if (get_colorbool_found < 0) + get_colorbool_found = 0; + } + + if (argc == 1) { + return get_colorbool_found ? 0 : 1; + } else { + printf("%s\n", get_colorbool_found ? "true" : "false"); + return 0; + } +} + int cmd_config(int argc, const char **argv, const char *prefix) { int nongit = 0; @@ -191,7 +289,7 @@ int cmd_config(int argc, const char **argv, const char *prefix) } } else if (!strcmp(argv[1], "--system")) - setenv(CONFIG_ENVIRONMENT, ETC_GITCONFIG, 1); + setenv(CONFIG_ENVIRONMENT, git_etc_gitconfig(), 1); else if (!strcmp(argv[1], "--file") || !strcmp(argv[1], "-f")) { if (argc < 3) usage(git_config_set_usage); @@ -234,8 +332,11 @@ int cmd_config(int argc, const char **argv, const char *prefix) return 1; } return 0; - } - else + } else if (!strcmp(argv[1], "--get-color")) { + return get_color(argc-2, argv+2); + } else if (!strcmp(argv[1], "--get-colorbool")) { + return get_colorbool(argc-2, argv+2); + } else break; argc--; argv++; diff --git a/builtin-diff-files.c b/builtin-diff-files.c index 6cb30c8e12..046b7e34b5 100644 --- a/builtin-diff-files.c +++ b/builtin-diff-files.c @@ -31,5 +31,7 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix) if (!rev.diffopt.output_format) rev.diffopt.output_format = DIFF_FORMAT_RAW; result = run_diff_files_cmd(&rev, argc, argv); - return rev.diffopt.exit_with_status ? rev.diffopt.has_changes: result; + if (DIFF_OPT_TST(&rev.diffopt, EXIT_WITH_STATUS)) + return DIFF_OPT_TST(&rev.diffopt, HAS_CHANGES) != 0; + return result; } diff --git a/builtin-diff-index.c b/builtin-diff-index.c index 81e7167438..556c506bfa 100644 --- a/builtin-diff-index.c +++ b/builtin-diff-index.c @@ -44,5 +44,7 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix) return -1; } result = run_diff_index(&rev, cached); - return rev.diffopt.exit_with_status ? rev.diffopt.has_changes: result; + if (DIFF_OPT_TST(&rev.diffopt, EXIT_WITH_STATUS)) + return DIFF_OPT_TST(&rev.diffopt, HAS_CHANGES) != 0; + return result; } diff --git a/builtin-diff-tree.c b/builtin-diff-tree.c index 0b591c8716..2e13716eec 100644 --- a/builtin-diff-tree.c +++ b/builtin-diff-tree.c @@ -118,8 +118,8 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix) } if (!read_stdin) - return opt->diffopt.exit_with_status ? - opt->diffopt.has_changes: 0; + return DIFF_OPT_TST(&opt->diffopt, EXIT_WITH_STATUS) + && DIFF_OPT_TST(&opt->diffopt, HAS_CHANGES); if (opt->diffopt.detect_rename) opt->diffopt.setup |= (DIFF_SETUP_USE_SIZE_CACHE | @@ -134,5 +134,6 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix) else diff_tree_stdin(line); } - return opt->diffopt.exit_with_status ? opt->diffopt.has_changes: 0; + return DIFF_OPT_TST(&opt->diffopt, EXIT_WITH_STATUS) + && DIFF_OPT_TST(&opt->diffopt, HAS_CHANGES); } diff --git a/builtin-diff.c b/builtin-diff.c index f77352b40d..1b615991e1 100644 --- a/builtin-diff.c +++ b/builtin-diff.c @@ -35,7 +35,7 @@ static void stuff_change(struct diff_options *opt, !hashcmp(old_sha1, new_sha1) && (old_mode == new_mode)) return; - if (opt->reverse_diff) { + if (DIFF_OPT_TST(opt, REVERSE_DIFF)) { unsigned tmp; const unsigned char *tmp_u; const char *tmp_c; @@ -200,15 +200,11 @@ static void refresh_index_quietly(void) discard_cache(); read_cache(); refresh_cache(REFRESH_QUIET|REFRESH_UNMERGED); - if (active_cache_changed) { - if (write_cache(fd, active_cache, active_nr) || - close(fd) || - commit_locked_index(lock_file)) - ; /* - * silently ignore it -- we haven't mucked - * with the real index. - */ - } + + if (active_cache_changed && + !write_cache(fd, active_cache, active_nr) && !close(fd)) + commit_locked_index(lock_file); + rollback_lock_file(lock_file); } @@ -257,13 +253,13 @@ int cmd_diff(int argc, const char **argv, const char *prefix) if (diff_setup_done(&rev.diffopt) < 0) die("diff_setup_done failed"); } - rev.diffopt.allow_external = 1; - rev.diffopt.recursive = 1; + DIFF_OPT_SET(&rev.diffopt, ALLOW_EXTERNAL); + DIFF_OPT_SET(&rev.diffopt, RECURSIVE); /* If the user asked for our exit code then don't start a * pager or we would end up reporting its exit code instead. */ - if (!rev.diffopt.exit_with_status) + if (!DIFF_OPT_TST(&rev.diffopt, EXIT_WITH_STATUS)) setup_pager(); /* Do we have --cached and not have a pending object, then @@ -367,8 +363,8 @@ int cmd_diff(int argc, const char **argv, const char *prefix) else result = builtin_diff_combined(&rev, argc, argv, ent, ents); - if (rev.diffopt.exit_with_status) - result = rev.diffopt.has_changes; + if (DIFF_OPT_TST(&rev.diffopt, EXIT_WITH_STATUS)) + result = DIFF_OPT_TST(&rev.diffopt, HAS_CHANGES) != 0; if (1 < rev.diffopt.skip_stat_unmatch) refresh_index_quietly(); diff --git a/builtin-fast-export.c b/builtin-fast-export.c new file mode 100755 index 0000000000..2136aadfd7 --- /dev/null +++ b/builtin-fast-export.c @@ -0,0 +1,406 @@ +/* + * "git fast-export" builtin command + * + * Copyright (C) 2007 Johannes E. Schindelin + */ +#include "builtin.h" +#include "cache.h" +#include "commit.h" +#include "object.h" +#include "tag.h" +#include "diff.h" +#include "diffcore.h" +#include "log-tree.h" +#include "revision.h" +#include "decorate.h" +#include "path-list.h" +#include "utf8.h" +#include "parse-options.h" + +static const char *fast_export_usage[] = { + "git-fast-export [rev-list-opts]", + NULL +}; + +static int progress; +static enum { VERBATIM, WARN, STRIP, ABORT } signed_tag_mode = ABORT; + +static int parse_opt_signed_tag_mode(const struct option *opt, + const char *arg, int unset) +{ + if (unset || !strcmp(arg, "abort")) + signed_tag_mode = ABORT; + else if (!strcmp(arg, "verbatim") || !strcmp(arg, "ignore")) + signed_tag_mode = VERBATIM; + else if (!strcmp(arg, "warn")) + signed_tag_mode = WARN; + else if (!strcmp(arg, "strip")) + signed_tag_mode = STRIP; + else + return error("Unknown signed-tag mode: %s", arg); + return 0; +} + +static struct decoration idnums; +static uint32_t last_idnum; + +static int has_unshown_parent(struct commit *commit) +{ + struct commit_list *parent; + + for (parent = commit->parents; parent; parent = parent->next) + if (!(parent->item->object.flags & SHOWN) && + !(parent->item->object.flags & UNINTERESTING)) + return 1; + return 0; +} + +/* Since intptr_t is C99, we do not use it here */ +static void mark_object(struct object *object) +{ + last_idnum++; + add_decoration(&idnums, object, ((uint32_t *)NULL) + last_idnum); +} + +static int get_object_mark(struct object *object) +{ + void *decoration = lookup_decoration(&idnums, object); + if (!decoration) + return 0; + return (uint32_t *)decoration - (uint32_t *)NULL; +} + +static void show_progress(void) +{ + static int counter = 0; + if (!progress) + return; + if ((++counter % progress) == 0) + printf("progress %d objects\n", counter); +} + +static void handle_object(const unsigned char *sha1) +{ + unsigned long size; + enum object_type type; + char *buf; + struct object *object; + + if (is_null_sha1(sha1)) + return; + + object = parse_object(sha1); + if (!object) + die ("Could not read blob %s", sha1_to_hex(sha1)); + + if (object->flags & SHOWN) + return; + + buf = read_sha1_file(sha1, &type, &size); + if (!buf) + die ("Could not read blob %s", sha1_to_hex(sha1)); + + mark_object(object); + + printf("blob\nmark :%d\ndata %lu\n", last_idnum, size); + if (fwrite(buf, size, 1, stdout) != 1) + die ("Could not write blob %s", sha1_to_hex(sha1)); + printf("\n"); + + show_progress(); + + object->flags |= SHOWN; + free(buf); +} + +static void show_filemodify(struct diff_queue_struct *q, + struct diff_options *options, void *data) +{ + int i; + for (i = 0; i < q->nr; i++) { + struct diff_filespec *spec = q->queue[i]->two; + if (is_null_sha1(spec->sha1)) + printf("D %s\n", spec->path); + else { + struct object *object = lookup_object(spec->sha1); + printf("M 0%06o :%d %s\n", spec->mode, + get_object_mark(object), spec->path); + } + } +} + +static const char *find_encoding(const char *begin, const char *end) +{ + const char *needle = "\nencoding "; + char *bol, *eol; + + bol = memmem(begin, end ? end - begin : strlen(begin), + needle, strlen(needle)); + if (!bol) + return git_commit_encoding; + bol += strlen(needle); + eol = strchrnul(bol, '\n'); + *eol = '\0'; + return bol; +} + +static void handle_commit(struct commit *commit, struct rev_info *rev) +{ + int saved_output_format = rev->diffopt.output_format; + const char *author, *author_end, *committer, *committer_end; + const char *encoding, *message; + char *reencoded = NULL; + struct commit_list *p; + int i; + + rev->diffopt.output_format = DIFF_FORMAT_CALLBACK; + + parse_commit(commit); + author = strstr(commit->buffer, "\nauthor "); + if (!author) + die ("Could not find author in commit %s", + sha1_to_hex(commit->object.sha1)); + author++; + author_end = strchrnul(author, '\n'); + committer = strstr(author_end, "\ncommitter "); + if (!committer) + die ("Could not find committer in commit %s", + sha1_to_hex(commit->object.sha1)); + committer++; + committer_end = strchrnul(committer, '\n'); + message = strstr(committer_end, "\n\n"); + encoding = find_encoding(committer_end, message); + if (message) + message += 2; + + if (commit->parents) { + parse_commit(commit->parents->item); + diff_tree_sha1(commit->parents->item->tree->object.sha1, + commit->tree->object.sha1, "", &rev->diffopt); + } + else + diff_root_tree_sha1(commit->tree->object.sha1, + "", &rev->diffopt); + + for (i = 0; i < diff_queued_diff.nr; i++) + handle_object(diff_queued_diff.queue[i]->two->sha1); + + mark_object(&commit->object); + if (!is_encoding_utf8(encoding)) + reencoded = reencode_string(message, "UTF-8", encoding); + printf("commit %s\nmark :%d\n%.*s\n%.*s\ndata %u\n%s", + (const char *)commit->util, last_idnum, + (int)(author_end - author), author, + (int)(committer_end - committer), committer, + (unsigned)(reencoded + ? strlen(reencoded) : message + ? strlen(message) : 0), + reencoded ? reencoded : message ? message : ""); + if (reencoded) + free(reencoded); + + for (i = 0, p = commit->parents; p; p = p->next) { + int mark = get_object_mark(&p->item->object); + if (!mark) + continue; + if (i == 0) + printf("from :%d\n", mark); + else if (i == 1) + printf("merge :%d", mark); + else + printf(" :%d", mark); + i++; + } + if (i > 1) + printf("\n"); + + log_tree_diff_flush(rev); + rev->diffopt.output_format = saved_output_format; + + printf("\n"); + + show_progress(); +} + +static void handle_tail(struct object_array *commits, struct rev_info *revs) +{ + struct commit *commit; + while (commits->nr) { + commit = (struct commit *)commits->objects[commits->nr - 1].item; + if (has_unshown_parent(commit)) + return; + handle_commit(commit, revs); + commits->nr--; + } +} + +static void handle_tag(const char *name, struct tag *tag) +{ + unsigned long size; + enum object_type type; + char *buf; + const char *tagger, *tagger_end, *message; + size_t message_size = 0; + + buf = read_sha1_file(tag->object.sha1, &type, &size); + if (!buf) + die ("Could not read tag %s", sha1_to_hex(tag->object.sha1)); + message = memmem(buf, size, "\n\n", 2); + if (message) { + message += 2; + message_size = strlen(message); + } + tagger = memmem(buf, message ? message - buf : size, "\ntagger ", 8); + if (!tagger) + die ("No tagger for tag %s", sha1_to_hex(tag->object.sha1)); + tagger++; + tagger_end = strchrnul(tagger, '\n'); + + /* handle signed tags */ + if (message) { + const char *signature = strstr(message, + "\n-----BEGIN PGP SIGNATURE-----\n"); + if (signature) + switch(signed_tag_mode) { + case ABORT: + die ("Encountered signed tag %s; use " + "--signed-tag=<mode> to handle it.", + sha1_to_hex(tag->object.sha1)); + case WARN: + warning ("Exporting signed tag %s", + sha1_to_hex(tag->object.sha1)); + /* fallthru */ + case VERBATIM: + break; + case STRIP: + message_size = signature + 1 - message; + break; + } + } + + if (!prefixcmp(name, "refs/tags/")) + name += 10; + printf("tag %s\nfrom :%d\n%.*s\ndata %d\n%.*s\n", + name, get_object_mark(tag->tagged), + (int)(tagger_end - tagger), tagger, + (int)message_size, (int)message_size, message ? message : ""); +} + +static void get_tags_and_duplicates(struct object_array *pending, + struct path_list *extra_refs) +{ + struct tag *tag; + int i; + + for (i = 0; i < pending->nr; i++) { + struct object_array_entry *e = pending->objects + i; + unsigned char sha1[20]; + struct commit *commit = commit; + char *full_name; + + if (dwim_ref(e->name, strlen(e->name), sha1, &full_name) != 1) + continue; + + switch (e->item->type) { + case OBJ_COMMIT: + commit = (struct commit *)e->item; + break; + case OBJ_TAG: + tag = (struct tag *)e->item; + while (tag && tag->object.type == OBJ_TAG) { + path_list_insert(full_name, extra_refs)->util = tag; + tag = (struct tag *)tag->tagged; + } + if (!tag) + die ("Tag %s points nowhere?", e->name); + switch(tag->object.type) { + case OBJ_COMMIT: + commit = (struct commit *)tag; + break; + case OBJ_BLOB: + handle_object(tag->object.sha1); + continue; + } + break; + default: + die ("Unexpected object of type %s", + typename(e->item->type)); + } + if (commit->util) + /* more than one name for the same object */ + path_list_insert(full_name, extra_refs)->util = commit; + else + commit->util = full_name; + } +} + +static void handle_tags_and_duplicates(struct path_list *extra_refs) +{ + struct commit *commit; + int i; + + for (i = extra_refs->nr - 1; i >= 0; i--) { + const char *name = extra_refs->items[i].path; + struct object *object = extra_refs->items[i].util; + switch (object->type) { + case OBJ_TAG: + handle_tag(name, (struct tag *)object); + break; + case OBJ_COMMIT: + /* create refs pointing to already seen commits */ + commit = (struct commit *)object; + printf("reset %s\nfrom :%d\n\n", name, + get_object_mark(&commit->object)); + show_progress(); + break; + } + } +} + +int cmd_fast_export(int argc, const char **argv, const char *prefix) +{ + struct rev_info revs; + struct object_array commits = { 0, 0, NULL }; + struct path_list extra_refs = { NULL, 0, 0, 0 }; + struct commit *commit; + struct option options[] = { + OPT_INTEGER(0, "progress", &progress, + "show progress after <n> objects"), + OPT_CALLBACK(0, "signed-tags", &signed_tag_mode, "mode", + "select handling of signed tags", + parse_opt_signed_tag_mode), + OPT_END() + }; + + /* we handle encodings */ + git_config(git_default_config); + + init_revisions(&revs, prefix); + argc = setup_revisions(argc, argv, &revs, NULL); + argc = parse_options(argc, argv, options, fast_export_usage, 0); + if (argc > 1) + usage_with_options (fast_export_usage, options); + + get_tags_and_duplicates(&revs.pending, &extra_refs); + + prepare_revision_walk(&revs); + revs.diffopt.format_callback = show_filemodify; + DIFF_OPT_SET(&revs.diffopt, RECURSIVE); + while ((commit = get_revision(&revs))) { + if (has_unshown_parent(commit)) { + struct commit_list *parent = commit->parents; + add_object_array(&commit->object, NULL, &commits); + for (; parent; parent = parent->next) + if (!parent->item->util) + parent->item->util = commit->util; + } + else { + handle_commit(commit, &revs); + handle_tail(&commits, &revs); + } + } + + handle_tags_and_duplicates(&extra_refs); + + return 0; +} diff --git a/builtin-fetch--tool.c b/builtin-fetch--tool.c index 6a78517958..7460ab7fce 100644 --- a/builtin-fetch--tool.c +++ b/builtin-fetch--tool.c @@ -435,9 +435,7 @@ static int pick_rref(int sha1_only, const char *rref, const char *ls_remote_resu cp++; if (!*cp) break; - np = strchr(cp, '\n'); - if (!np) - np = cp + strlen(cp); + np = strchrnul(cp, '\n'); if (pass) { lrr_list[i].line = cp; lrr_list[i].name = cp + 41; @@ -461,9 +459,7 @@ static int pick_rref(int sha1_only, const char *rref, const char *ls_remote_resu rref++; if (!*rref) break; - next = strchr(rref, '\n'); - if (!next) - next = rref + strlen(rref); + next = strchrnul(rref, '\n'); rreflen = next - rref; for (i = 0; i < lrr_count; i++) { @@ -515,10 +511,14 @@ int cmd_fetch__tool(int argc, const char **argv, const char *prefix) if (!strcmp("append-fetch-head", argv[1])) { int result; FILE *fp; + char *filename; if (argc != 8) return error("append-fetch-head takes 6 args"); - fp = fopen(git_path("FETCH_HEAD"), "a"); + filename = git_path("FETCH_HEAD"); + fp = fopen(filename, "a"); + if (!fp) + return error("cannot open %s: %s\n", filename, strerror(errno)); result = append_fetch_head(fp, argv[2], argv[3], argv[4], argv[5], argv[6], !!argv[7][0], @@ -529,10 +529,14 @@ int cmd_fetch__tool(int argc, const char **argv, const char *prefix) if (!strcmp("native-store", argv[1])) { int result; FILE *fp; + char *filename; if (argc != 5) return error("fetch-native-store takes 3 args"); - fp = fopen(git_path("FETCH_HEAD"), "a"); + filename = git_path("FETCH_HEAD"); + fp = fopen(filename, "a"); + if (!fp) + return error("cannot open %s: %s\n", filename, strerror(errno)); result = fetch_native_store(fp, argv[2], argv[3], argv[4], verbose, force); fclose(fp); diff --git a/builtin-fetch-pack.c b/builtin-fetch-pack.c index 862652be92..807fa93b53 100644 --- a/builtin-fetch-pack.c +++ b/builtin-fetch-pack.c @@ -32,7 +32,7 @@ static const char fetch_pack_usage[] = #define MAX_IN_VAIN 256 static struct commit_list *rev_list; -static int non_common_revs, multi_ack, use_thin_pack, use_sideband; +static int non_common_revs, multi_ack, use_sideband; static void rev_list_push(struct commit *commit, int mark) { @@ -178,7 +178,7 @@ static int find_common(int fd[2], unsigned char *result_sha1, (multi_ack ? " multi_ack" : ""), (use_sideband == 2 ? " side-band-64k" : ""), (use_sideband == 1 ? " side-band" : ""), - (use_thin_pack ? " thin-pack" : ""), + (args.use_thin_pack ? " thin-pack" : ""), (args.no_progress ? " no-progress" : ""), " ofs-delta"); else @@ -462,34 +462,12 @@ static int sideband_demux(int fd, void *data) { int *xd = data; - close(xd[1]); return recv_sideband("fetch-pack", xd[0], fd, 2); } -static void setup_sideband(int fd[2], int xd[2], struct async *demux) -{ - if (!use_sideband) { - fd[0] = xd[0]; - fd[1] = xd[1]; - return; - } - /* xd[] is talking with upload-pack; subprocess reads from - * xd[0], spits out band#2 to stderr, and feeds us band#1 - * through demux->out. - */ - demux->proc = sideband_demux; - demux->data = xd; - if (start_async(demux)) - die("fetch-pack: unable to fork off sideband demultiplexer"); - close(xd[0]); - fd[0] = demux->out; - fd[1] = xd[1]; -} - static int get_pack(int xd[2], char **pack_lockfile) { struct async demux; - int fd[2]; const char *argv[20]; char keep_arg[256]; char hdr_arg[256]; @@ -497,7 +475,20 @@ static int get_pack(int xd[2], char **pack_lockfile) int do_keep = args.keep_pack; struct child_process cmd; - setup_sideband(fd, xd, &demux); + memset(&demux, 0, sizeof(demux)); + if (use_sideband) { + /* xd[] is talking with upload-pack; subprocess reads from + * xd[0], spits out band#2 to stderr, and feeds us band#1 + * through demux->out. + */ + demux.proc = sideband_demux; + demux.data = xd; + if (start_async(&demux)) + die("fetch-pack: unable to fork off sideband" + " demultiplexer"); + } + else + demux.out = xd[0]; memset(&cmd, 0, sizeof(cmd)); cmd.argv = argv; @@ -506,7 +497,7 @@ static int get_pack(int xd[2], char **pack_lockfile) if (!args.keep_pack && unpack_limit) { struct pack_header header; - if (read_pack_header(fd[0], &header)) + if (read_pack_header(demux.out, &header)) die("protocol error: bad pack header"); snprintf(hdr_arg, sizeof(hdr_arg), "--pack_header=%u,%u", ntohl(header.hdr_version), ntohl(header.hdr_entries)); @@ -542,11 +533,10 @@ static int get_pack(int xd[2], char **pack_lockfile) *av++ = hdr_arg; *av++ = NULL; - cmd.in = fd[0]; + cmd.in = demux.out; cmd.git_cmd = 1; if (start_command(&cmd)) die("fetch-pack: unable to fork off %s", argv[0]); - close(fd[1]); if (do_keep && pack_lockfile) *pack_lockfile = index_pack_lockfile(cmd.out); diff --git a/builtin-fetch.c b/builtin-fetch.c index 003ed76d16..320e235682 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -8,13 +8,46 @@ #include "path-list.h" #include "remote.h" #include "transport.h" - -static const char fetch_usage[] = "git-fetch [-a | --append] [--upload-pack <upload-pack>] [-f | --force] [--no-tags] [-t | --tags] [-k | --keep] [-u | --update-head-ok] [--depth <depth>] [-v | --verbose] [<repository> <refspec>...]"; - -static int append, force, tags, no_tags, update_head_ok, verbose, quiet; -static char *default_rla = NULL; +#include "run-command.h" +#include "parse-options.h" + +static const char * const builtin_fetch_usage[] = { + "git-fetch [options] [<repository> <refspec>...]", + NULL +}; + +enum { + TAGS_UNSET = 0, + TAGS_DEFAULT = 1, + TAGS_SET = 2 +}; + +static int append, force, keep, update_head_ok, verbose, quiet; +static int tags = TAGS_DEFAULT; +static const char *depth; +static const char *upload_pack; +static struct strbuf default_rla = STRBUF_INIT; static struct transport *transport; +static struct option builtin_fetch_options[] = { + OPT__QUIET(&quiet), + OPT__VERBOSE(&verbose), + OPT_BOOLEAN('a', "append", &append, + "append to .git/FETCH_HEAD instead of overwriting"), + OPT_STRING(0, "upload-pack", &upload_pack, "PATH", + "path to upload pack on remote end"), + OPT_BOOLEAN('f', "force", &force, + "force overwrite of local branch"), + OPT_SET_INT('t', "tags", &tags, + "fetch all tags and associated objects", TAGS_SET), + OPT_BOOLEAN('k', "keep", &keep, "keep downloaded pack"), + OPT_BOOLEAN('u', "update-head-ok", &update_head_ok, + "allow updating of HEAD ref"), + OPT_STRING(0, "depth", &depth, "DEPTH", + "deepen history of shallow clone"), + OPT_END() +}; + static void unlock_pack(void) { if (transport) @@ -29,7 +62,7 @@ static void unlock_pack_on_signal(int signo) } static void add_merge_config(struct ref **head, - struct ref *remote_refs, + const struct ref *remote_refs, struct branch *branch, struct ref ***tail) { @@ -77,9 +110,9 @@ static struct ref *get_ref_map(struct transport *transport, struct ref *ref_map = NULL; struct ref **tail = &ref_map; - struct ref *remote_refs = transport_get_remote_refs(transport); + const struct ref *remote_refs = transport_get_remote_refs(transport); - if (ref_count || tags) { + if (ref_count || tags == TAGS_SET) { for (i = 0; i < ref_count; i++) { get_fetch_map(remote_refs, &refs[i], &tail, 0); if (refs[i].dst && refs[i].dst[0]) @@ -88,7 +121,7 @@ static struct ref *get_ref_map(struct transport *transport, /* Merge everything on the command line, but not --tags */ for (rm = ref_map; rm; rm = rm->next) rm->merge = 1; - if (tags) { + if (tags == TAGS_SET) { struct refspec refspec; refspec.src = "refs/tags/"; refspec.dst = "refs/tags/"; @@ -131,12 +164,6 @@ static struct ref *get_ref_map(struct transport *transport, return ref_map; } -static void show_new(enum object_type type, unsigned char *sha1_new) -{ - fprintf(stderr, " %s: %s\n", typename(type), - find_unique_abbrev(sha1_new, DEFAULT_ABBREV)); -} - static int s_update_ref(const char *action, struct ref *ref, int check_old) @@ -146,7 +173,7 @@ static int s_update_ref(const char *action, static struct ref_lock *lock; if (!rla) - rla = default_rla; + rla = default_rla.buf; snprintf(msg, sizeof(msg), "%s: %s", rla, action); lock = lock_any_ref_for_update(ref->name, check_old ? ref->old_sha1 : NULL, 0); @@ -157,34 +184,40 @@ static int s_update_ref(const char *action, return 0; } +#define SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3) +#define REFCOL_WIDTH 10 + static int update_local_ref(struct ref *ref, - const char *note, - int verbose) + const char *remote, + int verbose, + char *display) { - char oldh[41], newh[41]; struct commit *current = NULL, *updated; enum object_type type; struct branch *current_branch = branch_get(NULL); + const char *pretty_ref = ref->name + ( + !prefixcmp(ref->name, "refs/heads/") ? 11 : + !prefixcmp(ref->name, "refs/tags/") ? 10 : + !prefixcmp(ref->name, "refs/remotes/") ? 13 : + 0); + *display = 0; type = sha1_object_info(ref->new_sha1, NULL); if (type < 0) die("object %s not found", sha1_to_hex(ref->new_sha1)); if (!*ref->name) { /* Not storing */ - if (verbose) { - fprintf(stderr, "* fetched %s\n", note); - show_new(type, ref->new_sha1); - } + if (verbose) + sprintf(display, "* branch %s -> FETCH_HEAD", remote); return 0; } if (!hashcmp(ref->old_sha1, ref->new_sha1)) { - if (verbose) { - fprintf(stderr, "* %s: same as %s\n", - ref->name, note); - show_new(type, ref->new_sha1); - } + if (verbose) + sprintf(display, "= %-*s %-*s -> %s", SUMMARY_WIDTH, + "[up to date]", REFCOL_WIDTH, remote, + pretty_ref); return 0; } @@ -196,68 +229,76 @@ static int update_local_ref(struct ref *ref, * If this is the head, and it's not okay to update * the head, and the old value of the head isn't empty... */ - fprintf(stderr, - " * %s: Cannot fetch into the current branch.\n", - ref->name); + sprintf(display, "! %-*s %-*s -> %s (can't fetch in current branch)", + SUMMARY_WIDTH, "[rejected]", REFCOL_WIDTH, remote, + pretty_ref); return 1; } if (!is_null_sha1(ref->old_sha1) && !prefixcmp(ref->name, "refs/tags/")) { - fprintf(stderr, "* %s: updating with %s\n", - ref->name, note); - show_new(type, ref->new_sha1); + sprintf(display, "- %-*s %-*s -> %s", + SUMMARY_WIDTH, "[tag update]", REFCOL_WIDTH, remote, + pretty_ref); return s_update_ref("updating tag", ref, 0); } current = lookup_commit_reference_gently(ref->old_sha1, 1); updated = lookup_commit_reference_gently(ref->new_sha1, 1); if (!current || !updated) { - char *msg; - if (!strncmp(ref->name, "refs/tags/", 10)) + const char *msg; + const char *what; + if (!strncmp(ref->name, "refs/tags/", 10)) { msg = "storing tag"; - else + what = "[new tag]"; + } + else { msg = "storing head"; - fprintf(stderr, "* %s: storing %s\n", - ref->name, note); - show_new(type, ref->new_sha1); + what = "[new branch]"; + } + + sprintf(display, "* %-*s %-*s -> %s", SUMMARY_WIDTH, what, + REFCOL_WIDTH, remote, pretty_ref); return s_update_ref(msg, ref, 0); } - strcpy(oldh, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV)); - strcpy(newh, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV)); - if (in_merge_bases(current, &updated, 1)) { - fprintf(stderr, "* %s: fast forward to %s\n", - ref->name, note); - fprintf(stderr, " old..new: %s..%s\n", oldh, newh); + char quickref[83]; + strcpy(quickref, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV)); + strcat(quickref, ".."); + strcat(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV)); + sprintf(display, " %-*s %-*s -> %s", SUMMARY_WIDTH, quickref, + REFCOL_WIDTH, remote, pretty_ref); return s_update_ref("fast forward", ref, 1); - } - if (!force && !ref->force) { - fprintf(stderr, - "* %s: not updating to non-fast forward %s\n", - ref->name, note); - fprintf(stderr, - " old...new: %s...%s\n", oldh, newh); + } else if (force || ref->force) { + char quickref[84]; + strcpy(quickref, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV)); + strcat(quickref, "..."); + strcat(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV)); + sprintf(display, "+ %-*s %-*s -> %s (forced update)", + SUMMARY_WIDTH, quickref, REFCOL_WIDTH, remote, pretty_ref); + return s_update_ref("forced-update", ref, 1); + } else { + sprintf(display, "! %-*s %-*s -> %s (non fast forward)", + SUMMARY_WIDTH, "[rejected]", REFCOL_WIDTH, remote, + pretty_ref); return 1; } - fprintf(stderr, - "* %s: forcing update to non-fast forward %s\n", - ref->name, note); - fprintf(stderr, " old...new: %s...%s\n", oldh, newh); - return s_update_ref("forced-update", ref, 1); } -static void store_updated_refs(const char *url, struct ref *ref_map) +static int store_updated_refs(const char *url, struct ref *ref_map) { FILE *fp; struct commit *commit; - int url_len, i, note_len; + int url_len, i, note_len, shown_url = 0; char note[1024]; const char *what, *kind; struct ref *rm; + char *filename = git_path("FETCH_HEAD"); - fp = fopen(git_path("FETCH_HEAD"), "a"); + fp = fopen(filename, "a"); + if (!fp) + return error("cannot open %s: %s\n", filename, strerror(errno)); for (rm = ref_map; rm; rm = rm->next) { struct ref *ref = NULL; @@ -315,17 +356,90 @@ static void store_updated_refs(const char *url, struct ref *ref_map) rm->merge ? "" : "not-for-merge", note); - if (ref) - update_local_ref(ref, note, verbose); + if (ref) { + update_local_ref(ref, what, verbose, note); + if (*note) { + if (!shown_url) { + fprintf(stderr, "From %.*s\n", + url_len, url); + shown_url = 1; + } + fprintf(stderr, " %s\n", note); + } + } } fclose(fp); + return 0; +} + +/* + * We would want to bypass the object transfer altogether if + * everything we are going to fetch already exists and connected + * locally. + * + * The refs we are going to fetch are in to_fetch (nr_heads in + * total). If running + * + * $ git-rev-list --objects to_fetch[0] to_fetch[1] ... --not --all + * + * does not error out, that means everything reachable from the + * refs we are going to fetch exists and is connected to some of + * our existing refs. + */ +static int quickfetch(struct ref *ref_map) +{ + struct child_process revlist; + struct ref *ref; + char **argv; + int i, err; + + /* + * If we are deepening a shallow clone we already have these + * objects reachable. Running rev-list here will return with + * a good (0) exit status and we'll bypass the fetch that we + * really need to perform. Claiming failure now will ensure + * we perform the network exchange to deepen our history. + */ + if (depth) + return -1; + + for (i = 0, ref = ref_map; ref; ref = ref->next) + i++; + if (!i) + return 0; + + argv = xmalloc(sizeof(*argv) * (i + 6)); + i = 0; + argv[i++] = xstrdup("rev-list"); + argv[i++] = xstrdup("--quiet"); + argv[i++] = xstrdup("--objects"); + for (ref = ref_map; ref; ref = ref->next) + argv[i++] = xstrdup(sha1_to_hex(ref->old_sha1)); + argv[i++] = xstrdup("--not"); + argv[i++] = xstrdup("--all"); + argv[i++] = NULL; + + memset(&revlist, 0, sizeof(revlist)); + revlist.argv = (const char**)argv; + revlist.git_cmd = 1; + revlist.no_stdin = 1; + revlist.no_stdout = 1; + revlist.no_stderr = 1; + err = run_command(&revlist); + + for (i = 0; argv[i]; i++) + free(argv[i]); + free(argv); + return err; } static int fetch_refs(struct transport *transport, struct ref *ref_map) { - int ret = transport_fetch_refs(transport, ref_map); + int ret = quickfetch(ref_map); + if (ret) + ret = transport_fetch_refs(transport, ref_map); if (!ret) - store_updated_refs(transport->url, ref_map); + ret |= store_updated_refs(transport->url, ref_map); transport_unlock_pack(transport); return ret; } @@ -345,12 +459,12 @@ static struct ref *find_non_local_tags(struct transport *transport, struct path_list new_refs = { NULL, 0, 0, 1 }; char *ref_name; int ref_name_len; - unsigned char *ref_sha1; - struct ref *tag_ref; + const unsigned char *ref_sha1; + const struct ref *tag_ref; struct ref *rm = NULL; struct ref *ref_map = NULL; struct ref **tail = &ref_map; - struct ref *ref; + const struct ref *ref; for_each_ref(add_existing, &existing_refs); for (ref = transport_get_remote_refs(transport); ref; ref = ref->next) { @@ -375,10 +489,7 @@ static struct ref *find_non_local_tags(struct transport *transport, if (!path_list_has_path(&existing_refs, ref_name) && !path_list_has_path(&new_refs, ref_name) && - lookup_object(ref->old_sha1)) { - fprintf(stderr, "Auto-following %s\n", - ref_name); - + has_sha1_file(ref->old_sha1)) { path_list_insert(ref_name, &new_refs); rm = alloc_ref(strlen(ref_name) + 1); @@ -402,17 +513,22 @@ static int do_fetch(struct transport *transport, struct ref *ref_map, *fetch_map; struct ref *rm; int autotags = (transport->remote->fetch_tags == 1); - if (transport->remote->fetch_tags == 2 && !no_tags) - tags = 1; + if (transport->remote->fetch_tags == 2 && tags != TAGS_UNSET) + tags = TAGS_SET; if (transport->remote->fetch_tags == -1) - no_tags = 1; + tags = TAGS_UNSET; if (!transport->get_refs_list || !transport->fetch) die("Don't know how to fetch from %s", transport->url); /* if not appending, truncate FETCH_HEAD */ - if (!append) - fclose(fopen(git_path("FETCH_HEAD"), "w")); + if (!append) { + char *filename = git_path("FETCH_HEAD"); + FILE *fp = fopen(filename, "w"); + if (!fp) + return error("cannot open %s: %s\n", filename, strerror(errno)); + fclose(fp); + } ref_map = get_ref_map(transport, refs, ref_count, tags, &autotags); @@ -430,7 +546,7 @@ static int do_fetch(struct transport *transport, /* if neither --no-tags nor --tags was specified, do automated tag * following ... */ - if (!(tags || no_tags) && autotags) { + if (tags == TAGS_DEFAULT && autotags) { ref_map = find_non_local_tags(transport, fetch_map); if (ref_map) { transport_set_option(transport, TRANS_OPT_DEPTH, "0"); @@ -458,91 +574,22 @@ static void set_option(const char *name, const char *value) int cmd_fetch(int argc, const char **argv, const char *prefix) { struct remote *remote; - int i, j, rla_offset; + int i; static const char **refs = NULL; int ref_nr = 0; - int cmd_len = 0; - const char *depth = NULL, *upload_pack = NULL; - int keep = 0; - - for (i = 1; i < argc; i++) { - const char *arg = argv[i]; - cmd_len += strlen(arg); - - if (arg[0] != '-') - break; - if (!strcmp(arg, "--append") || !strcmp(arg, "-a")) { - append = 1; - continue; - } - if (!prefixcmp(arg, "--upload-pack=")) { - upload_pack = arg + 14; - continue; - } - if (!strcmp(arg, "--upload-pack")) { - i++; - if (i == argc) - usage(fetch_usage); - upload_pack = argv[i]; - continue; - } - if (!strcmp(arg, "--force") || !strcmp(arg, "-f")) { - force = 1; - continue; - } - if (!strcmp(arg, "--no-tags")) { - no_tags = 1; - continue; - } - if (!strcmp(arg, "--tags") || !strcmp(arg, "-t")) { - tags = 1; - continue; - } - if (!strcmp(arg, "--keep") || !strcmp(arg, "-k")) { - keep = 1; - continue; - } - if (!strcmp(arg, "--update-head-ok") || !strcmp(arg, "-u")) { - update_head_ok = 1; - continue; - } - if (!prefixcmp(arg, "--depth=")) { - depth = arg + 8; - continue; - } - if (!strcmp(arg, "--depth")) { - i++; - if (i == argc) - usage(fetch_usage); - depth = argv[i]; - continue; - } - if (!strcmp(arg, "--quiet")) { - quiet = 1; - continue; - } - if (!strcmp(arg, "--verbose") || !strcmp(arg, "-v")) { - verbose++; - continue; - } - usage(fetch_usage); - } - for (j = i; j < argc; j++) - cmd_len += strlen(argv[j]); + /* Record the command line for the reflog */ + strbuf_addstr(&default_rla, "fetch"); + for (i = 1; i < argc; i++) + strbuf_addf(&default_rla, " %s", argv[i]); - default_rla = xmalloc(cmd_len + 5 + argc + 1); - sprintf(default_rla, "fetch"); - rla_offset = strlen(default_rla); - for (j = 1; j < argc; j++) { - sprintf(default_rla + rla_offset, " %s", argv[j]); - rla_offset += strlen(argv[j]) + 1; - } + argc = parse_options(argc, argv, + builtin_fetch_options, builtin_fetch_usage, 0); - if (i == argc) + if (argc == 0) remote = remote_get(NULL); else - remote = remote_get(argv[i++]); + remote = remote_get(argv[0]); transport = transport_get(remote, remote->url[0]); if (verbose >= 2) @@ -559,10 +606,10 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) if (!transport->url) die("Where do you want to fetch from today?"); - if (i < argc) { + if (argc > 1) { int j = 0; - refs = xcalloc(argc - i + 1, sizeof(const char *)); - while (i < argc) { + refs = xcalloc(argc + 1, sizeof(const char *)); + for (i = 1; i < argc; i++) { if (!strcmp(argv[i], "tag")) { char *ref; i++; @@ -574,7 +621,6 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) refs[j++] = ref; } else refs[j++] = argv[i]; - i++; } refs[j] = NULL; ref_nr = j; diff --git a/builtin-fmt-merge-msg.c b/builtin-fmt-merge-msg.c index 8a3c962f89..6163bd4975 100644 --- a/builtin-fmt-merge-msg.c +++ b/builtin-fmt-merge-msg.c @@ -176,7 +176,7 @@ static void shortlog(const char *name, unsigned char *sha1, struct commit *commit; struct object *branch; struct list subjects = { NULL, NULL, 0, 0 }; - int flags = UNINTERESTING | TREECHANGE | SEEN | SHOWN | ADDED; + int flags = UNINTERESTING | TREESAME | SEEN | SHOWN | ADDED; branch = deref_tag(parse_object(sha1), sha1_to_hex(sha1), 40); if (!branch || branch->type != OBJ_COMMIT) diff --git a/builtin-for-each-ref.c b/builtin-for-each-ref.c index da8c7948e6..f36a43c264 100644 --- a/builtin-for-each-ref.c +++ b/builtin-for-each-ref.c @@ -13,8 +13,8 @@ #define QUOTE_NONE 0 #define QUOTE_SHELL 1 #define QUOTE_PERL 2 -#define QUOTE_PYTHON 3 -#define QUOTE_TCL 4 +#define QUOTE_PYTHON 4 +#define QUOTE_TCL 8 typedef enum { FIELD_STR, FIELD_ULONG, FIELD_TIME } cmp_type; @@ -304,7 +304,7 @@ static const char *find_wholine(const char *who, int wholen, const char *buf, un if (!eol) return ""; eol++; - if (eol[1] == '\n') + if (*eol == '\n') return ""; /* end of header */ buf = eol; } @@ -833,21 +833,24 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix) int i, num_refs; const char *format = "%(objectname) %(objecttype)\t%(refname)"; struct ref_sort *sort = NULL, **sort_tail = &sort; - int maxcount = 0, quote_style; - int quote_shell = 0, quote_perl = 0, quote_python = 0, quote_tcl = 0; + int maxcount = 0, quote_style = 0; struct refinfo **refs; struct grab_ref_cbdata cbdata; struct option opts[] = { - OPT_BOOLEAN('s', "shell", "e_shell, "quote placeholders suitably for shells"), - OPT_BOOLEAN('p', "perl", "e_perl, "quote placeholders suitably for perl"), - OPT_BOOLEAN( 0 , "python", "e_python, "quote placeholders suitably for python"), - OPT_BOOLEAN( 0 , "tcl", "e_tcl, "quote placeholders suitably for tcl"), + OPT_BIT('s', "shell", "e_style, + "quote placeholders suitably for shells", QUOTE_SHELL), + OPT_BIT('p', "perl", "e_style, + "quote placeholders suitably for perl", QUOTE_PERL), + OPT_BIT(0 , "python", "e_style, + "quote placeholders suitably for python", QUOTE_PYTHON), + OPT_BIT(0 , "tcl", "e_style, + "quote placeholders suitably for tcl", QUOTE_TCL), OPT_GROUP(""), OPT_INTEGER( 0 , "count", &maxcount, "show only <n> matched refs"), OPT_STRING( 0 , "format", &format, "format", "format to use for the output"), - OPT_CALLBACK(0 , "sort", &sort_tail, "key", + OPT_CALLBACK(0 , "sort", sort_tail, "key", "field name to sort on", &opt_parse_sort), OPT_END(), }; @@ -857,15 +860,13 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix) error("invalid --count argument: `%d'", maxcount); usage_with_options(for_each_ref_usage, opts); } - if (quote_shell + quote_perl + quote_python + quote_tcl > 1) { - error("more than one quoting style ?"); + if (HAS_MULTI_BITS(quote_style)) { + error("more than one quoting style?"); usage_with_options(for_each_ref_usage, opts); } if (verify_format(format)) usage_with_options(for_each_ref_usage, opts); - quote_style = QUOTE_SHELL * quote_shell + QUOTE_PERL * quote_perl + - QUOTE_PYTHON * quote_python + QUOTE_TCL * quote_tcl; if (!sort) sort = default_sort(); sort_atom_limit = used_atom_cnt; diff --git a/builtin-gc.c b/builtin-gc.c index c5bce893a6..799c263936 100644 --- a/builtin-gc.c +++ b/builtin-gc.c @@ -175,7 +175,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix) char buf[80]; struct option builtin_gc_options[] = { - OPT_BOOLEAN(0, "prune", &prune, "prune unreferenced loose objects"), + OPT_BOOLEAN(0, "prune", &prune, "prune unreferenced objects"), OPT_BOOLEAN(0, "aggressive", &aggressive, "be more thorough (increased runtime)"), OPT_BOOLEAN(0, "auto", &auto_gc, "enable auto-gc mode"), OPT_END() diff --git a/builtin-grep.c b/builtin-grep.c index c7b45c4d58..f1ff8dc556 100644 --- a/builtin-grep.c +++ b/builtin-grep.c @@ -294,7 +294,7 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached) if (opt->pre_context) { push_arg("-B"); len += snprintf(argptr, sizeof(randarg)-len, - "%u", opt->pre_context); + "%u", opt->pre_context) + 1; if (sizeof(randarg) <= len) die("maximum length of args exceeded"); push_arg(argptr); @@ -303,7 +303,7 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached) if (opt->post_context) { push_arg("-A"); len += snprintf(argptr, sizeof(randarg)-len, - "%u", opt->post_context); + "%u", opt->post_context) + 1; if (sizeof(randarg) <= len) die("maximum length of args exceeded"); push_arg(argptr); @@ -313,7 +313,7 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached) else { push_arg("-C"); len += snprintf(argptr, sizeof(randarg)-len, - "%u", opt->post_context); + "%u", opt->post_context) + 1; if (sizeof(randarg) <= len) die("maximum length of args exceeded"); push_arg(argptr); @@ -343,12 +343,12 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached) memcpy(name + 2, ce->name, len + 1); } argv[argc++] = name; - if (argc < MAXARGS && !ce_stage(ce)) - continue; - status = flush_grep(opt, argc, nr, argv, &kept); - if (0 < status) - hit = 1; - argc = nr + kept; + if (MAXARGS <= argc) { + status = flush_grep(opt, argc, nr, argv, &kept); + if (0 < status) + hit = 1; + argc = nr + kept; + } if (ce_stage(ce)) { do { i++; diff --git a/builtin-init-db.c b/builtin-init-db.c index 763fa55e76..e1393b8d1e 100644 --- a/builtin-init-db.c +++ b/builtin-init-db.c @@ -5,6 +5,7 @@ */ #include "cache.h" #include "builtin.h" +#include "exec_cmd.h" #ifndef DEFAULT_GIT_TEMPLATE_DIR #define DEFAULT_GIT_TEMPLATE_DIR "/usr/share/git-core/templates" @@ -131,10 +132,19 @@ static void copy_templates(const char *git_dir, int len, const char *template_di int template_len; DIR *dir; - if (!template_dir) { + if (!template_dir) template_dir = getenv(TEMPLATE_DIR_ENVIRONMENT); - if (!template_dir) - template_dir = DEFAULT_GIT_TEMPLATE_DIR; + if (!template_dir) { + /* + * if the hard-coded template is relative, it is + * interpreted relative to the exec_dir + */ + template_dir = DEFAULT_GIT_TEMPLATE_DIR; + if (!is_absolute_path(template_dir)) { + const char *exec_path = git_exec_path(); + template_dir = prefix_path(exec_path, strlen(exec_path), + template_dir); + } } strcpy(template_path, template_dir); template_len = strlen(template_path); diff --git a/builtin-log.c b/builtin-log.c index e8b982db7c..b6a11220e2 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -55,13 +55,13 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix, rev->abbrev = DEFAULT_ABBREV; rev->commit_format = CMIT_FMT_DEFAULT; rev->verbose_header = 1; - rev->diffopt.recursive = 1; + DIFF_OPT_SET(&rev->diffopt, RECURSIVE); rev->show_root_diff = default_show_root; rev->subject_prefix = fmt_patch_subject_prefix; argc = setup_revisions(argc, argv, rev, "HEAD"); if (rev->diffopt.pickaxe || rev->diffopt.filter) rev->always_show_header = 0; - if (rev->diffopt.follow_renames) { + if (DIFF_OPT_TST(&rev->diffopt, FOLLOW_RENAMES)) { rev->always_show_header = 0; if (rev->diffopt.nr_paths != 1) usage("git logs can only follow renames on one pathname at a time"); @@ -77,11 +77,134 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix, } } +/* + * This gives a rough estimate for how many commits we + * will print out in the list. + */ +static int estimate_commit_count(struct rev_info *rev, struct commit_list *list) +{ + int n = 0; + + while (list) { + struct commit *commit = list->item; + unsigned int flags = commit->object.flags; + list = list->next; + if (!(flags & (TREESAME | UNINTERESTING))) + n++; + } + return n; +} + +static void show_early_header(struct rev_info *rev, const char *stage, int nr) +{ + if (rev->shown_one) { + rev->shown_one = 0; + if (rev->commit_format != CMIT_FMT_ONELINE) + putchar(rev->diffopt.line_termination); + } + printf("Final output: %d %s\n", nr, stage); +} + +struct itimerval early_output_timer; + +static void log_show_early(struct rev_info *revs, struct commit_list *list) +{ + int i = revs->early_output; + int show_header = 1; + + sort_in_topological_order(&list, revs->lifo); + while (list && i) { + struct commit *commit = list->item; + switch (simplify_commit(revs, commit)) { + case commit_show: + if (show_header) { + int n = estimate_commit_count(revs, list); + show_early_header(revs, "incomplete", n); + show_header = 0; + } + log_tree_commit(revs, commit); + i--; + break; + case commit_ignore: + break; + case commit_error: + return; + } + list = list->next; + } + + /* Did we already get enough commits for the early output? */ + if (!i) + return; + + /* + * ..if no, then repeat it twice a second until we + * do. + * + * NOTE! We don't use "it_interval", because if the + * reader isn't listening, we want our output to be + * throttled by the writing, and not have the timer + * trigger every second even if we're blocked on a + * reader! + */ + early_output_timer.it_value.tv_sec = 0; + early_output_timer.it_value.tv_usec = 500000; + setitimer(ITIMER_REAL, &early_output_timer, NULL); +} + +static void early_output(int signal) +{ + show_early_output = log_show_early; +} + +static void setup_early_output(struct rev_info *rev) +{ + struct sigaction sa; + + /* + * Set up the signal handler, minimally intrusively: + * we only set a single volatile integer word (not + * using sigatomic_t - trying to avoid unnecessary + * system dependencies and headers), and using + * SA_RESTART. + */ + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = early_output; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + sigaction(SIGALRM, &sa, NULL); + + /* + * If we can get the whole output in less than a + * tenth of a second, don't even bother doing the + * early-output thing.. + * + * This is a one-time-only trigger. + */ + early_output_timer.it_value.tv_sec = 0; + early_output_timer.it_value.tv_usec = 100000; + setitimer(ITIMER_REAL, &early_output_timer, NULL); +} + +static void finish_early_output(struct rev_info *rev) +{ + int n = estimate_commit_count(rev, rev->commits); + signal(SIGALRM, SIG_IGN); + show_early_header(rev, "done", n); +} + static int cmd_log_walk(struct rev_info *rev) { struct commit *commit; + if (rev->early_output) + setup_early_output(rev); + prepare_revision_walk(rev); + + if (rev->early_output) + finish_early_output(rev); + while ((commit = get_revision(rev)) != NULL) { log_tree_commit(rev, commit); if (!rev->reflog_info) { @@ -185,11 +308,9 @@ int cmd_show(int argc, const char **argv, const char *prefix) struct tag *t = (struct tag *)o; printf("%stag %s%s\n\n", - diff_get_color(rev.diffopt.color_diff, - DIFF_COMMIT), + diff_get_color_opt(&rev.diffopt, DIFF_COMMIT), t->tag, - diff_get_color(rev.diffopt.color_diff, - DIFF_RESET)); + diff_get_color_opt(&rev.diffopt, DIFF_RESET)); ret = show_object(o->sha1, 1); objects[i].item = (struct object *)t->tagged; i--; @@ -197,11 +318,9 @@ int cmd_show(int argc, const char **argv, const char *prefix) } case OBJ_TREE: printf("%stree %s%s\n\n", - diff_get_color(rev.diffopt.color_diff, - DIFF_COMMIT), + diff_get_color_opt(&rev.diffopt, DIFF_COMMIT), name, - diff_get_color(rev.diffopt.color_diff, - DIFF_RESET)); + diff_get_color_opt(&rev.diffopt, DIFF_RESET)); read_tree_recursive((struct tree *)o, "", 0, 0, NULL, show_tree_object); break; @@ -273,6 +392,8 @@ static int istitlechar(char c) static char *extra_headers = NULL; static int extra_headers_size = 0; static const char *fmt_patch_suffix = ".patch"; +static int numbered = 0; +static int auto_number = 0; static int git_format_config(const char *var, const char *value) { @@ -297,6 +418,15 @@ static int git_format_config(const char *var, const char *value) if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff")) { return 0; } + if (!strcmp(var, "format.numbered")) { + if (!strcasecmp(value, "auto")) { + auto_number = 1; + return 0; + } + + numbered = git_config_bool(var, value); + return 0; + } return git_log_config(var, value); } @@ -427,7 +557,7 @@ static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids, const cha static void gen_message_id(char *dest, unsigned int length, char *base) { - const char *committer = git_committer_info(-1); + const char *committer = git_committer_info(IDENT_WARN_ON_NO_NAME); const char *email_start = strrchr(committer, '<'); const char *email_end = strrchr(committer, '>'); if(!email_start || !email_end || email_start > email_end - 1) @@ -466,7 +596,6 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) struct rev_info rev; int nr = 0, total, i, j; int use_stdout = 0; - int numbered = 0; int start_number = -1; int keep_subject = 0; int numbered_files = 0; /* _just_ numbers */ @@ -487,7 +616,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) rev.combine_merges = 0; rev.ignore_merges = 1; rev.diffopt.msg_sep = ""; - rev.diffopt.recursive = 1; + DIFF_OPT_SET(&rev.diffopt, RECURSIVE); rev.subject_prefix = fmt_patch_subject_prefix; rev.extra_headers = extra_headers; @@ -503,6 +632,11 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) else if (!strcmp(argv[i], "-n") || !strcmp(argv[i], "--numbered")) numbered = 1; + else if (!strcmp(argv[i], "-N") || + !strcmp(argv[i], "--no-numbered")) { + numbered = 0; + auto_number = 0; + } else if (!prefixcmp(argv[i], "--start-number=")) start_number = strtol(argv[i] + 15, NULL, 10); else if (!strcmp(argv[i], "--numbered-files")) @@ -531,7 +665,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) !strcmp(argv[i], "-s")) { const char *committer; const char *endpos; - committer = git_committer_info(1); + committer = git_committer_info(IDENT_ERROR_ON_NO_NAME); endpos = strchr(committer, '>'); if (!endpos) die("bogos committer info %s\n", committer); @@ -590,8 +724,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) if (!rev.diffopt.output_format) rev.diffopt.output_format = DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_SUMMARY | DIFF_FORMAT_PATCH; - if (!rev.diffopt.text) - rev.diffopt.binary = 1; + if (!DIFF_OPT_TST(&rev.diffopt, TEXT)) + DIFF_OPT_SET(&rev.diffopt, BINARY); if (!output_directory && !use_stdout) output_directory = prefix; @@ -642,6 +776,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) list[nr - 1] = commit; } total = nr; + if (!keep_subject && auto_number && total > 1) + numbered = 1; if (numbered) rev.total = total + start_number - 1; rev.add_signoff = add_signoff; @@ -747,7 +883,7 @@ int cmd_cherry(int argc, const char **argv, const char *prefix) revs.diff = 1; revs.combine_merges = 0; revs.ignore_merges = 1; - revs.diffopt.recursive = 1; + DIFF_OPT_SET(&revs.diffopt, RECURSIVE); if (add_pending_commit(head, &revs, 0)) die("Unknown commit %s", head); @@ -787,7 +923,7 @@ int cmd_cherry(int argc, const char **argv, const char *prefix) struct strbuf buf; strbuf_init(&buf, 0); pretty_print_commit(CMIT_FMT_ONELINE, commit, - &buf, 0, NULL, NULL, 0); + &buf, 0, NULL, NULL, 0, 0); printf("%c %s %s\n", sign, sha1_to_hex(commit->object.sha1), buf.buf); strbuf_release(&buf); diff --git a/builtin-ls-files.c b/builtin-ls-files.c index b70da1863b..0f0ab2da16 100644 --- a/builtin-ls-files.c +++ b/builtin-ls-files.c @@ -38,28 +38,28 @@ static const char *tag_modified = ""; /* - * Match a pathspec against a filename. The first "len" characters + * Match a pathspec against a filename. The first "skiplen" characters * are the common prefix */ -static int match(const char **spec, char *ps_matched, - const char *filename, int len) +int pathspec_match(const char **spec, char *ps_matched, + const char *filename, int skiplen) { const char *m; while ((m = *spec++) != NULL) { - int matchlen = strlen(m + len); + int matchlen = strlen(m + skiplen); if (!matchlen) goto matched; - if (!strncmp(m + len, filename + len, matchlen)) { - if (m[len + matchlen - 1] == '/') + if (!strncmp(m + skiplen, filename + skiplen, matchlen)) { + if (m[skiplen + matchlen - 1] == '/') goto matched; - switch (filename[len + matchlen]) { + switch (filename[skiplen + matchlen]) { case '/': case '\0': goto matched; } } - if (!fnmatch(m + len, filename + len, 0)) + if (!fnmatch(m + skiplen, filename + skiplen, 0)) goto matched; if (ps_matched) ps_matched++; @@ -80,7 +80,7 @@ static void show_dir_entry(const char *tag, struct dir_entry *ent) if (len >= ent->len) die("git-ls-files: internal error - directory entry not superset of prefix"); - if (pathspec && !match(pathspec, ps_matched, ent->name, len)) + if (pathspec && !pathspec_match(pathspec, ps_matched, ent->name, len)) return; fputs(tag, stdout); @@ -185,7 +185,7 @@ static void show_ce_entry(const char *tag, struct cache_entry *ce) if (len >= ce_namelen(ce)) die("git-ls-files: internal error - cache entry not superset of prefix"); - if (pathspec && !match(pathspec, ps_matched, ce->name, len)) + if (pathspec && !pathspec_match(pathspec, ps_matched, ce->name, len)) return; if (tag && *tag && show_valid_bit && @@ -331,7 +331,7 @@ static const char *verify_pathspec(const char *prefix) * that were given from the command line. We are not * going to write this index out. */ -static void overlay_tree(const char *tree_name, const char *prefix) +void overlay_tree_on_cache(const char *tree_name, const char *prefix) { struct tree *tree; unsigned char sha1[20]; @@ -384,11 +384,47 @@ static void overlay_tree(const char *tree_name, const char *prefix) } } +int report_path_error(const char *ps_matched, const char **pathspec, int prefix_offset) +{ + /* + * Make sure all pathspec matched; otherwise it is an error. + */ + int num, errors = 0; + for (num = 0; pathspec[num]; num++) { + int other, found_dup; + + if (ps_matched[num]) + continue; + /* + * The caller might have fed identical pathspec + * twice. Do not barf on such a mistake. + */ + for (found_dup = other = 0; + !found_dup && pathspec[other]; + other++) { + if (other == num || !ps_matched[other]) + continue; + if (!strcmp(pathspec[other], pathspec[num])) + /* + * Ok, we have a match already. + */ + found_dup = 1; + } + if (found_dup) + continue; + + error("pathspec '%s' did not match any file(s) known to git.", + pathspec[num] + prefix_offset); + errors++; + } + return errors; +} + static const char ls_files_usage[] = "git-ls-files [-z] [-t] [-v] (--[cached|deleted|others|stage|unmerged|killed|modified])* " "[ --ignored ] [--exclude=<pattern>] [--exclude-from=<file>] " - "[ --exclude-per-directory=<filename> ] [--full-name] [--abbrev] " - "[--] [<file>]*"; + "[ --exclude-per-directory=<filename> ] [--exclude-standard] " + "[--full-name] [--abbrev] [--] [<file>]*"; int cmd_ls_files(int argc, const char **argv, const char *prefix) { @@ -496,6 +532,11 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix) dir.exclude_per_dir = arg + 24; continue; } + if (!strcmp(arg, "--exclude-standard")) { + exc_given = 1; + setup_standard_excludes(&dir); + continue; + } if (!strcmp(arg, "--full-name")) { prefix_offset = 0; continue; @@ -525,11 +566,8 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix) break; } - if (require_work_tree && !is_inside_work_tree()) { - const char *work_tree = get_git_work_tree(); - if (!work_tree || chdir(work_tree)) - die("This operation must be run in a work tree"); - } + if (require_work_tree && !is_inside_work_tree()) + setup_work_tree(); pathspec = get_pathspec(prefix, argv + i); @@ -566,47 +604,17 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix) */ if (show_stage || show_unmerged) die("ls-files --with-tree is incompatible with -s or -u"); - overlay_tree(with_tree, prefix); + overlay_tree_on_cache(with_tree, prefix); } show_files(&dir, prefix); if (ps_matched) { - /* We need to make sure all pathspec matched otherwise - * it is an error. - */ - int num, errors = 0; - for (num = 0; pathspec[num]; num++) { - int other, found_dup; - - if (ps_matched[num]) - continue; - /* - * The caller might have fed identical pathspec - * twice. Do not barf on such a mistake. - */ - for (found_dup = other = 0; - !found_dup && pathspec[other]; - other++) { - if (other == num || !ps_matched[other]) - continue; - if (!strcmp(pathspec[other], pathspec[num])) - /* - * Ok, we have a match already. - */ - found_dup = 1; - } - if (found_dup) - continue; - - error("pathspec '%s' did not match any file(s) known to git.", - pathspec[num] + prefix_offset); - errors++; - } - - if (errors) + int bad; + bad = report_path_error(ps_matched, pathspec, prefix_offset); + if (bad) fprintf(stderr, "Did you forget to 'git add'?\n"); - return errors ? 1 : 0; + return bad ? 1 : 0; } return 0; diff --git a/builtin-ls-remote.c b/builtin-ls-remote.c new file mode 100644 index 0000000000..e5d670a93a --- /dev/null +++ b/builtin-ls-remote.c @@ -0,0 +1,106 @@ +#include "builtin.h" +#include "cache.h" +#include "transport.h" +#include "remote.h" + +static const char ls_remote_usage[] = +"git-ls-remote [--upload-pack=<git-upload-pack>] [<host>:]<directory>"; + +/* + * pattern is a list of tail-part of accepted refnames. Is there one + * among them that is a suffix of the path? Directory boundary must + * be honored when checking this match. IOW, patterns "master" and + * "sa/master" both match path "refs/hold/sa/master". On the other + * hand, path "refs/hold/foosa/master" is matched by "master" but not + * by "sa/master". + */ + +static int tail_match(const char **pattern, const char *path) +{ + int pathlen; + const char *p; + + if (!*pattern) + return 1; /* no restriction */ + + for (pathlen = strlen(path); (p = *pattern); pattern++) { + int pfxlen = pathlen - strlen(p); + if (pfxlen < 0) + continue; /* pattern is longer, will never match */ + if (strcmp(path + pfxlen, p)) + continue; /* no tail match */ + if (!pfxlen || path[pfxlen - 1] == '/') + return 1; /* fully match at directory boundary */ + } + return 0; +} + +int cmd_ls_remote(int argc, const char **argv, const char *prefix) +{ + int i; + const char *dest = NULL; + int nongit = 0; + unsigned flags = 0; + const char *uploadpack = NULL; + const char **pattern = NULL; + + struct remote *remote; + struct transport *transport; + const struct ref *ref; + + setup_git_directory_gently(&nongit); + + for (i = 1; i < argc; i++) { + const char *arg = argv[i]; + + if (*arg == '-') { + if (!prefixcmp(arg, "--upload-pack=")) { + uploadpack = arg + 14; + continue; + } + if (!prefixcmp(arg, "--exec=")) { + uploadpack = arg + 7; + continue; + } + if (!strcmp("--tags", arg)) { + flags |= REF_TAGS; + continue; + } + if (!strcmp("--heads", arg)) { + flags |= REF_HEADS; + continue; + } + if (!strcmp("--refs", arg)) { + flags |= REF_NORMAL; + continue; + } + usage(ls_remote_usage); + } + dest = arg; + break; + } + + if (!dest) + usage(ls_remote_usage); + pattern = argv + i + 1; + remote = nongit ? NULL : remote_get(dest); + if (remote && !remote->url_nr) + die("remote %s has no configured URL", dest); + transport = transport_get(remote, remote ? remote->url[0] : dest); + if (uploadpack != NULL) + transport_set_option(transport, TRANS_OPT_UPLOADPACK, uploadpack); + + ref = transport_get_remote_refs(transport); + + if (!ref) + return 1; + + for ( ; ref; ref = ref->next) { + if (!check_ref_type(ref, flags)) + continue; + if (!tail_match(pattern, ref->name)) + continue; + printf("%s %s\n", sha1_to_hex(ref->old_sha1), ref->name); + } + return 0; +} diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c index fb12248f82..2600847974 100644 --- a/builtin-mailinfo.c +++ b/builtin-mailinfo.c @@ -915,6 +915,7 @@ static void handle_info(void) static int mailinfo(FILE *in, FILE *out, int ks, const char *encoding, const char *msg, const char *patch) { + int peek; keep_subject = ks; metainfo_charset = encoding; fin = in; @@ -935,6 +936,11 @@ static int mailinfo(FILE *in, FILE *out, int ks, const char *encoding, p_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(char *)); s_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(char *)); + do { + peek = fgetc(in); + } while (isspace(peek)); + ungetc(peek, in); + /* process the email header */ while (read_one_header_line(line, sizeof(line), fin)) check_header(line, sizeof(line), p_hdr_data, 1); diff --git a/builtin-mailsplit.c b/builtin-mailsplit.c index 43fc373a15..46b27cdaea 100644 --- a/builtin-mailsplit.c +++ b/builtin-mailsplit.c @@ -101,20 +101,29 @@ static int populate_maildir_list(struct path_list *list, const char *path) { DIR *dir; struct dirent *dent; + char name[PATH_MAX]; + char *subs[] = { "cur", "new", NULL }; + char **sub; + + for (sub = subs; *sub; ++sub) { + snprintf(name, sizeof(name), "%s/%s", path, *sub); + if ((dir = opendir(name)) == NULL) { + if (errno == ENOENT) + continue; + error("cannot opendir %s (%s)", name, strerror(errno)); + return -1; + } - if ((dir = opendir(path)) == NULL) { - error("cannot opendir %s (%s)", path, strerror(errno)); - return -1; - } + while ((dent = readdir(dir)) != NULL) { + if (dent->d_name[0] == '.') + continue; + snprintf(name, sizeof(name), "%s/%s", *sub, dent->d_name); + path_list_insert(name, list); + } - while ((dent = readdir(dir)) != NULL) { - if (dent->d_name[0] == '.') - continue; - path_list_insert(dent->d_name, list); + closedir(dir); } - closedir(dir); - return 0; } @@ -122,19 +131,17 @@ static int split_maildir(const char *maildir, const char *dir, int nr_prec, int skip) { char file[PATH_MAX]; - char curdir[PATH_MAX]; char name[PATH_MAX]; int ret = -1; int i; struct path_list list = {NULL, 0, 0, 1}; - snprintf(curdir, sizeof(curdir), "%s/cur", maildir); - if (populate_maildir_list(&list, curdir) < 0) + if (populate_maildir_list(&list, maildir) < 0) goto out; for (i = 0; i < list.nr; i++) { FILE *f; - snprintf(file, sizeof(file), "%s/%s", curdir, list.items[i].path); + snprintf(file, sizeof(file), "%s/%s", maildir, list.items[i].path); f = fopen(file, "r"); if (!f) { error("cannot open mail %s (%s)", file, strerror(errno)); @@ -152,10 +159,9 @@ static int split_maildir(const char *maildir, const char *dir, fclose(f); } - path_list_clear(&list, 1); - ret = skip; out: + path_list_clear(&list, 1); return ret; } @@ -164,6 +170,7 @@ static int split_mbox(const char *file, const char *dir, int allow_bare, { char name[PATH_MAX]; int ret = -1; + int peek; FILE *f = !strcmp(file, "-") ? stdin : fopen(file, "r"); int file_done = 0; @@ -173,6 +180,11 @@ static int split_mbox(const char *file, const char *dir, int allow_bare, goto out; } + do { + peek = fgetc(f); + } while (isspace(peek)); + ungetc(peek, f); + if (fgets(buf, sizeof(buf), f) == NULL) { /* empty stdin is OK */ if (f != stdin) { diff --git a/builtin-merge-ours.c b/builtin-merge-ours.c new file mode 100644 index 0000000000..8f5bbaf402 --- /dev/null +++ b/builtin-merge-ours.c @@ -0,0 +1,28 @@ +/* + * Implementation of git-merge-ours.sh as builtin + * + * Copyright (c) 2007 Thomas Harning Jr + * Original: + * Original Copyright (c) 2005 Junio C Hamano + * + * Pretend we resolved the heads, but declare our tree trumps everybody else. + */ +#include "git-compat-util.h" +#include "builtin.h" + +static const char *diff_index_args[] = { + "diff-index", "--quiet", "--cached", "HEAD", "--", NULL +}; +#define NARGS (ARRAY_SIZE(diff_index_args) - 1) + +int cmd_merge_ours(int argc, const char **argv, const char *prefix) +{ + /* + * We need to exit with 2 if the index does not match our HEAD tree, + * because the current index is what we will be committing as the + * merge result. + */ + if (cmd_diff_index(NARGS, diff_index_args, prefix)) + exit(2); + exit(0); +} diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index 25ec65d0f0..250dc56ab5 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -57,7 +57,7 @@ struct object_entry { * nice "minimum seek" order. */ static struct object_entry *objects; -static struct object_entry **written_list; +static struct pack_idx_entry **written_list; static uint32_t nr_objects, nr_alloc, nr_result, nr_written; static int non_empty; @@ -577,7 +577,7 @@ static off_t write_one(struct sha1file *f, e->idx.offset = 0; return 0; } - written_list[nr_written++] = e; + written_list[nr_written++] = &e->idx; /* make sure off_t is sufficiently large not to wrap */ if (offset > offset + size) @@ -599,7 +599,7 @@ static void write_pack_file(void) if (do_progress) progress_state = start_progress("Writing objects", nr_result); - written_list = xmalloc(nr_objects * sizeof(struct object_entry *)); + written_list = xmalloc(nr_objects * sizeof(*written_list)); do { unsigned char sha1[20]; @@ -651,9 +651,8 @@ static void write_pack_file(void) umask(mode); mode = 0444 & ~mode; - idx_tmp_name = write_idx_file(NULL, - (struct pack_idx_entry **) written_list, - nr_written, sha1); + idx_tmp_name = write_idx_file(NULL, written_list, + nr_written, sha1); snprintf(tmpname, sizeof(tmpname), "%s-%s.pack", base_name, sha1_to_hex(sha1)); if (adjust_perm(pack_tmp_name, mode)) @@ -677,7 +676,7 @@ static void write_pack_file(void) /* mark written objects as written to previous pack */ for (j = 0; j < nr_written; j++) { - written_list[j]->idx.offset = (off_t)-1; + written_list[j]->offset = (off_t)-1; } nr_remaining -= nr_written; } while (nr_remaining && i < nr_objects); @@ -991,7 +990,7 @@ static void add_pbase_object(struct tree_desc *tree, return; if (name[cmplen] != '/') { add_object_entry(entry.sha1, - S_ISDIR(entry.mode) ? OBJ_TREE : OBJ_BLOB, + object_type(entry.mode), fullname, 1); return; } @@ -1246,28 +1245,37 @@ static void get_object_details(void) free(sorted_by_offset); } +/* + * We search for deltas in a list sorted by type, by filename hash, and then + * by size, so that we see progressively smaller and smaller files. + * That's because we prefer deltas to be from the bigger file + * to the smaller -- deletes are potentially cheaper, but perhaps + * more importantly, the bigger file is likely the more recent + * one. The deepest deltas are therefore the oldest objects which are + * less susceptible to be accessed often. + */ static int type_size_sort(const void *_a, const void *_b) { const struct object_entry *a = *(struct object_entry **)_a; const struct object_entry *b = *(struct object_entry **)_b; - if (a->type < b->type) - return -1; if (a->type > b->type) - return 1; - if (a->hash < b->hash) return -1; - if (a->hash > b->hash) + if (a->type < b->type) return 1; - if (a->preferred_base < b->preferred_base) + if (a->hash > b->hash) return -1; - if (a->preferred_base > b->preferred_base) + if (a->hash < b->hash) return 1; - if (a->size < b->size) + if (a->preferred_base > b->preferred_base) return -1; + if (a->preferred_base < b->preferred_base) + return 1; if (a->size > b->size) + return -1; + if (a->size < b->size) return 1; - return a > b ? -1 : (a < b); /* newest last */ + return a < b ? -1 : (a > b); /* newest first */ } struct unpacked { @@ -1318,14 +1326,6 @@ static pthread_mutex_t progress_mutex = PTHREAD_MUTEX_INITIALIZER; #endif -/* - * We search for deltas _backwards_ in a list sorted by type and - * by size, so that we see progressively smaller and smaller files. - * That's because we prefer deltas to be from the bigger file - * to the smaller - deletes are potentially cheaper, but perhaps - * more importantly, the bigger file is likely the more recent - * one. - */ static int try_delta(struct unpacked *trg, struct unpacked *src, unsigned max_depth, unsigned long *mem_usage) { @@ -1423,10 +1423,6 @@ static int try_delta(struct unpacked *trg, struct unpacked *src, } } - trg_entry->delta = src_entry; - trg_entry->delta_size = delta_size; - trg->depth = src->depth + 1; - /* * Handle memory allocation outside of the cache * accounting lock. Compiler will optimize the strangeness @@ -1440,7 +1436,7 @@ static int try_delta(struct unpacked *trg, struct unpacked *src, trg_entry->delta_data = NULL; } if (delta_cacheable(src_size, trg_size, delta_size)) { - delta_cache_size += trg_entry->delta_size; + delta_cache_size += delta_size; cache_unlock(); trg_entry->delta_data = xrealloc(delta_buf, delta_size); } else { @@ -1448,6 +1444,10 @@ static int try_delta(struct unpacked *trg, struct unpacked *src, free(delta_buf); } + trg_entry->delta = src_entry; + trg_entry->delta_size = delta_size; + trg->depth = src->depth + 1; + return 1; } @@ -1479,10 +1479,10 @@ static unsigned long free_unpacked(struct unpacked *n) return freed_mem; } -static void find_deltas(struct object_entry **list, unsigned list_size, +static void find_deltas(struct object_entry **list, unsigned *list_size, int window, int depth, unsigned *processed) { - uint32_t i = list_size, idx = 0, count = 0; + uint32_t i, idx = 0, count = 0; unsigned int array_size = window * sizeof(struct unpacked); struct unpacked *array; unsigned long mem_usage = 0; @@ -1490,11 +1490,23 @@ static void find_deltas(struct object_entry **list, unsigned list_size, array = xmalloc(array_size); memset(array, 0, array_size); - do { - struct object_entry *entry = list[--i]; + for (;;) { + struct object_entry *entry = *list++; struct unpacked *n = array + idx; int j, max_depth, best_base = -1; + progress_lock(); + if (!*list_size) { + progress_unlock(); + break; + } + (*list_size)--; + if (!entry->preferred_base) { + (*processed)++; + display_progress(progress_state, *processed); + } + progress_unlock(); + mem_usage -= free_unpacked(n); n->entry = entry; @@ -1512,11 +1524,6 @@ static void find_deltas(struct object_entry **list, unsigned list_size, if (entry->preferred_base) goto next; - progress_lock(); - (*processed)++; - display_progress(progress_state, *processed); - progress_unlock(); - /* * If the current object is at pack edge, take the depth the * objects that depend on the current object into account @@ -1576,7 +1583,7 @@ static void find_deltas(struct object_entry **list, unsigned list_size, count++; if (idx >= window) idx = 0; - } while (i > 0); + } for (i = 0; i < window; ++i) { free_delta_index(array[i].index); @@ -1591,6 +1598,7 @@ struct thread_params { pthread_t thread; struct object_entry **list; unsigned list_size; + unsigned remaining; int window; int depth; unsigned *processed; @@ -1612,10 +1620,10 @@ static void *threaded_find_deltas(void *arg) pthread_mutex_lock(&data_ready); pthread_mutex_unlock(&data_request); - if (!me->list_size) + if (!me->remaining) return NULL; - find_deltas(me->list, me->list_size, + find_deltas(me->list, &me->remaining, me->window, me->depth, me->processed); } } @@ -1624,57 +1632,102 @@ static void ll_find_deltas(struct object_entry **list, unsigned list_size, int window, int depth, unsigned *processed) { struct thread_params *target, p[delta_search_threads]; - int i, ret; - unsigned chunk_size; + int i, ret, active_threads = 0; if (delta_search_threads <= 1) { - find_deltas(list, list_size, window, depth, processed); + find_deltas(list, &list_size, window, depth, processed); return; } pthread_mutex_lock(&data_provider); pthread_mutex_lock(&data_ready); + /* Start work threads. */ for (i = 0; i < delta_search_threads; i++) { p[i].window = window; p[i].depth = depth; p[i].processed = processed; + p[i].remaining = 0; ret = pthread_create(&p[i].thread, NULL, threaded_find_deltas, &p[i]); if (ret) die("unable to create thread: %s", strerror(ret)); + active_threads++; } - /* this should be auto-tuned somehow */ - chunk_size = window * 1000; + /* Then partition the work amongst them. */ + for (i = 0; i < delta_search_threads; i++) { + unsigned sub_size = list_size / (delta_search_threads - i); - do { - unsigned sublist_size = chunk_size; - if (sublist_size > list_size) - sublist_size = list_size; + pthread_mutex_lock(&data_provider); + target = data_requester; + if (!sub_size) { + pthread_mutex_unlock(&data_ready); + pthread_join(target->thread, NULL); + active_threads--; + continue; + } /* try to split chunks on "path" boundaries */ - while (sublist_size < list_size && list[sublist_size]->hash && - list[sublist_size]->hash == list[sublist_size-1]->hash) - sublist_size++; + while (sub_size < list_size && list[sub_size]->hash && + list[sub_size]->hash == list[sub_size-1]->hash) + sub_size++; + target->list = list; + target->list_size = sub_size; + target->remaining = sub_size; + pthread_mutex_unlock(&data_ready); + + list += sub_size; + list_size -= sub_size; + } + + /* + * Now let's wait for work completion. Each time a thread is done + * with its work, we steal half of the remaining work from the + * thread with the largest number of unprocessed objects and give + * it to that newly idle thread. This ensure good load balancing + * until the remaining object list segments are simply too short + * to be worth splitting anymore. + */ + do { + struct thread_params *victim = NULL; + unsigned sub_size = 0; pthread_mutex_lock(&data_provider); target = data_requester; - target->list = list; - target->list_size = sublist_size; + + progress_lock(); + for (i = 0; i < delta_search_threads; i++) + if (p[i].remaining > 2*window && + (!victim || victim->remaining < p[i].remaining)) + victim = &p[i]; + if (victim) { + sub_size = victim->remaining / 2; + list = victim->list + victim->list_size - sub_size; + while (sub_size && list[0]->hash && + list[0]->hash == list[-1]->hash) { + list++; + sub_size--; + } + target->list = list; + victim->list_size -= sub_size; + victim->remaining -= sub_size; + } + progress_unlock(); + + target->list_size = sub_size; + target->remaining = sub_size; pthread_mutex_unlock(&data_ready); - list += sublist_size; - list_size -= sublist_size; - if (!sublist_size) { + if (!sub_size) { pthread_join(target->thread, NULL); - i--; + active_threads--; } - } while (i); + } while (active_threads); } #else -#define ll_find_deltas find_deltas +#define ll_find_deltas(l, s, w, d, p) find_deltas(l, &s, w, d, p) #endif static void prepare_pack(int window, int depth) @@ -1768,6 +1821,12 @@ static int git_pack_config(const char *k, const char *v) #endif return 0; } + if (!strcmp(k, "pack.indexversion")) { + pack_idx_default_version = git_config_int(k, v); + if (pack_idx_default_version > 2) + die("bad pack.indexversion=%d", pack_idx_default_version); + return 0; + } return git_default_config(k, v); } diff --git a/builtin-pack-refs.c b/builtin-pack-refs.c index a62f06bb89..1923fb1914 100644 --- a/builtin-pack-refs.c +++ b/builtin-pack-refs.c @@ -122,19 +122,13 @@ static char const * const pack_refs_usage[] = { int cmd_pack_refs(int argc, const char **argv, const char *prefix) { - int all = 0, prune = 1; - unsigned int flags = 0; + unsigned int flags = PACK_REFS_PRUNE; struct option opts[] = { - OPT_BOOLEAN(0, "all", &all, "pack everything"), - OPT_BOOLEAN(0, "prune", &prune, "prune loose refs (default)"), + OPT_BIT(0, "all", &flags, "pack everything", PACK_REFS_ALL), + OPT_BIT(0, "prune", &flags, "prune loose refs (default)", PACK_REFS_PRUNE), OPT_END(), }; - if (parse_options(argc, argv, opts, pack_refs_usage, 0)) usage_with_options(pack_refs_usage, opts); - if (prune) - flags |= PACK_REFS_PRUNE; - if (all) - flags |= PACK_REFS_ALL; return pack_refs(flags); } diff --git a/builtin-prune.c b/builtin-prune.c index 44df59e4a7..b5e768421b 100644 --- a/builtin-prune.c +++ b/builtin-prune.c @@ -7,15 +7,24 @@ static const char prune_usage[] = "git-prune [-n]"; static int show_only; +static unsigned long expire; static int prune_object(char *path, const char *filename, const unsigned char *sha1) { + const char *fullpath = mkpath("%s/%s", path, filename); + if (expire) { + struct stat st; + if (lstat(fullpath, &st)) + return error("Could not stat '%s'", fullpath); + if (st.st_mtime > expire) + return 0; + } if (show_only) { enum object_type type = sha1_object_info(sha1, NULL); printf("%s %s\n", sha1_to_hex(sha1), (type > 0) ? typename(type) : "unknown"); } else - unlink(mkpath("%s/%s", path, filename)); + unlink(fullpath); return 0; } @@ -85,6 +94,16 @@ int cmd_prune(int argc, const char **argv, const char *prefix) show_only = 1; continue; } + if (!strcmp(arg, "--expire")) { + if (++i < argc) { + expire = approxidate(argv[i]); + continue; + } + } + else if (!prefixcmp(arg, "--expire=")) { + expire = approxidate(arg + 9); + continue; + } usage(prune_usage); } diff --git a/builtin-push.c b/builtin-push.c index 4b39ef3852..c8cb63e238 100644 --- a/builtin-push.c +++ b/builtin-push.c @@ -7,8 +7,12 @@ #include "builtin.h" #include "remote.h" #include "transport.h" +#include "parse-options.h" -static const char push_usage[] = "git-push [--all] [--dry-run] [--tags] [--receive-pack=<git-receive-pack>] [--repo=all] [-f | --force] [-v] [<repository> <refspec>...]"; +static const char * const push_usage[] = { + "git-push [--all | --mirror] [--dry-run] [--tags] [--receive-pack=<git-receive-pack>] [--repo=all] [-f | --force] [-v] [<repository> <refspec>...]", + NULL, +}; static int thin, verbose; static const char *receivepack; @@ -40,6 +44,15 @@ static void set_refspecs(const char **refs, int nr) strcat(tag, refs[i]); ref = tag; } + if (!strcmp("HEAD", ref)) { + unsigned char sha1_dummy[20]; + ref = resolve_ref(ref, sha1_dummy, 1, NULL); + if (!ref) + die("HEAD cannot be resolved."); + if (prefixcmp(ref, "refs/heads/")) + die("HEAD cannot be resolved to branch."); + ref = xstrdup(ref + 11); + } add_refspec(ref); } } @@ -85,63 +98,55 @@ static int do_push(const char *repo, int flags) int cmd_push(int argc, const char **argv, const char *prefix) { - int i; int flags = 0; + int all = 0; + int mirror = 0; + int dry_run = 0; + int force = 0; + int tags = 0; const char *repo = NULL; /* default repository */ - for (i = 1; i < argc; i++) { - const char *arg = argv[i]; + struct option options[] = { + OPT__VERBOSE(&verbose), + OPT_STRING( 0 , "repo", &repo, "repository", "repository"), + OPT_BOOLEAN( 0 , "all", &all, "push all refs"), + OPT_BOOLEAN( 0 , "mirror", &mirror, "mirror all refs"), + OPT_BOOLEAN( 0 , "tags", &tags, "push tags"), + OPT_BOOLEAN( 0 , "dry-run", &dry_run, "dry run"), + OPT_BOOLEAN('f', "force", &force, "force updates"), + OPT_BOOLEAN( 0 , "thin", &thin, "use thin pack"), + OPT_STRING( 0 , "receive-pack", &receivepack, "receive-pack", "receive pack program"), + OPT_STRING( 0 , "exec", &receivepack, "receive-pack", "receive pack program"), + OPT_END() + }; - if (arg[0] != '-') { - repo = arg; - i++; - break; - } - if (!strcmp(arg, "-v")) { - verbose=1; - continue; - } - if (!prefixcmp(arg, "--repo=")) { - repo = arg+7; - continue; - } - if (!strcmp(arg, "--all")) { - flags |= TRANSPORT_PUSH_ALL; - continue; - } - if (!strcmp(arg, "--dry-run")) { - flags |= TRANSPORT_PUSH_DRY_RUN; - continue; - } - if (!strcmp(arg, "--tags")) { - add_refspec("refs/tags/*"); - continue; - } - if (!strcmp(arg, "--force") || !strcmp(arg, "-f")) { - flags |= TRANSPORT_PUSH_FORCE; - continue; - } - if (!strcmp(arg, "--thin")) { - thin = 1; - continue; - } - if (!strcmp(arg, "--no-thin")) { - thin = 0; - continue; - } - if (!prefixcmp(arg, "--receive-pack=")) { - receivepack = arg + 15; - continue; - } - if (!prefixcmp(arg, "--exec=")) { - receivepack = arg + 7; - continue; - } - usage(push_usage); + argc = parse_options(argc, argv, options, push_usage, 0); + + if (force) + flags |= TRANSPORT_PUSH_FORCE; + if (dry_run) + flags |= TRANSPORT_PUSH_DRY_RUN; + if (verbose) + flags |= TRANSPORT_PUSH_VERBOSE; + if (tags) + add_refspec("refs/tags/*"); + if (all) + flags |= TRANSPORT_PUSH_ALL; + if (mirror) + flags |= (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE); + + if (argc > 0) { + repo = argv[0]; + set_refspecs(argv + 1, argc - 1); + } + if ((flags & (TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) && refspec) + usage_with_options(push_usage, options); + + if ((flags & (TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) == + (TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) { + error("--all and --mirror are incompatible"); + usage_with_options(push_usage, options); } - set_refspecs(argv + i, argc - i); - if ((flags & TRANSPORT_PUSH_ALL) && refspec) - usage(push_usage); return do_push(repo, flags); } diff --git a/builtin-reset.c b/builtin-reset.c index e1dc31e0eb..4c61025aae 100644 --- a/builtin-reset.c +++ b/builtin-reset.c @@ -18,7 +18,7 @@ #include "tree.h" static const char builtin_reset_usage[] = -"git-reset [--mixed | --soft | --hard] [<commit-ish>] [ [--] <paths>...]"; +"git-reset [--mixed | --soft | --hard] [-q] [<commit-ish>] [ [--] <paths>...]"; static char *args_to_str(const char **argv) { @@ -46,26 +46,14 @@ static inline int is_merge(void) static int unmerged_files(void) { - char b; - ssize_t len; - struct child_process cmd; - const char *argv_ls_files[] = {"ls-files", "--unmerged", NULL}; - - memset(&cmd, 0, sizeof(cmd)); - cmd.argv = argv_ls_files; - cmd.git_cmd = 1; - cmd.out = -1; - - if (start_command(&cmd)) - die("Could not run sub-command: git ls-files"); - - len = xread(cmd.out, &b, 1); - if (len < 0) - die("Could not read output from git ls-files: %s", - strerror(errno)); - finish_command(&cmd); - - return len; + int i; + read_cache(); + for (i = 0; i < active_nr; i++) { + struct cache_entry *ce = active_cache[i]; + if (ce_stage(ce)) + return 1; + } + return 0; } static int reset_index_file(const unsigned char *sha1, int is_hard_reset) @@ -107,19 +95,34 @@ static void print_new_head_line(struct commit *commit) printf("\n"); } -static int update_index_refresh(void) +static int update_index_refresh(int fd, struct lock_file *index_lock) { - const char *argv_update_index[] = {"update-index", "--refresh", NULL}; - return run_command_v_opt(argv_update_index, RUN_GIT_CMD); + int result; + + if (!index_lock) { + index_lock = xcalloc(1, sizeof(struct lock_file)); + fd = hold_locked_index(index_lock, 1); + } + + if (read_cache() < 0) + return error("Could not read index"); + result = refresh_cache(0) ? 1 : 0; + if (write_cache(fd, active_cache, active_nr) || + close(fd) || + commit_locked_index(index_lock)) + return error ("Could not refresh index"); + return result; } static void update_index_from_diff(struct diff_queue_struct *q, struct diff_options *opt, void *data) { int i; + int *discard_flag = data; /* do_diff_cache() mangled the index */ discard_cache(); + *discard_flag = 1; read_cache(); for (i = 0; i < q->nr; i++) { @@ -138,24 +141,28 @@ static void update_index_from_diff(struct diff_queue_struct *q, static int read_from_tree(const char *prefix, const char **argv, unsigned char *tree_sha1) { - struct lock_file *lock = xcalloc(1, sizeof(struct lock_file)); - int index_fd; + struct lock_file *lock = xcalloc(1, sizeof(struct lock_file)); + int index_fd, index_was_discarded = 0; struct diff_options opt; memset(&opt, 0, sizeof(opt)); diff_tree_setup_paths(get_pathspec(prefix, (const char **)argv), &opt); opt.output_format = DIFF_FORMAT_CALLBACK; opt.format_callback = update_index_from_diff; + opt.format_callback_data = &index_was_discarded; index_fd = hold_locked_index(lock, 1); + index_was_discarded = 0; read_cache(); if (do_diff_cache(tree_sha1, &opt)) return 1; diffcore_std(&opt); diff_flush(&opt); - return write_cache(index_fd, active_cache, active_nr) || - close(index_fd) || - commit_locked_index(lock); + + if (!index_was_discarded) + /* The index is still clobbered from do_diff_cache() */ + discard_cache(); + return update_index_refresh(index_fd, lock); } static void prepend_reflog_action(const char *action, char *buf, size_t size) @@ -173,7 +180,7 @@ static const char *reset_type_names[] = { "mixed", "soft", "hard", NULL }; int cmd_reset(int argc, const char **argv, const char *prefix) { - int i = 1, reset_type = NONE, update_ref_status = 0; + int i = 1, reset_type = NONE, update_ref_status = 0, quiet = 0; const char *rev = "HEAD"; unsigned char sha1[20], *orig = NULL, sha1_orig[20], *old_orig = NULL, sha1_old_orig[20]; @@ -185,7 +192,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix) reflog_action = args_to_str(argv); setenv("GIT_REFLOG_ACTION", reflog_action, 0); - if (i < argc) { + while (i < argc) { if (!strcmp(argv[i], "--mixed")) { reset_type = MIXED; i++; @@ -198,6 +205,12 @@ int cmd_reset(int argc, const char **argv, const char *prefix) reset_type = HARD; i++; } + else if (!strcmp(argv[i], "-q")) { + quiet = 1; + i++; + } + else + break; } if (i < argc && argv[i][0] != '-') @@ -225,9 +238,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix) else if (reset_type != NONE) die("Cannot do %s reset with paths.", reset_type_names[reset_type]); - if (read_from_tree(prefix, argv + i, sha1)) - return 1; - return update_index_refresh() ? 1 : 0; + return read_from_tree(prefix, argv + i, sha1); } if (reset_type == NONE) reset_type = MIXED; /* by default */ @@ -258,13 +269,13 @@ int cmd_reset(int argc, const char **argv, const char *prefix) switch (reset_type) { case HARD: - if (!update_ref_status) + if (!update_ref_status && !quiet) print_new_head_line(commit); break; case SOFT: /* Nothing else to do. */ break; case MIXED: /* Report what has not been updated. */ - update_index_refresh(); + update_index_refresh(0, NULL); break; } diff --git a/builtin-rev-list.c b/builtin-rev-list.c index 44393320e8..1cb5f67119 100644 --- a/builtin-rev-list.c +++ b/builtin-rev-list.c @@ -26,6 +26,7 @@ static const char rev_list_usage[] = " --remove-empty\n" " --all\n" " --stdin\n" +" --quiet\n" " ordering output:\n" " --topo-order\n" " --date-order\n" @@ -50,6 +51,7 @@ static int show_timestamp; static int hdr_termination; static const char *header_prefix; +static void finish_commit(struct commit *commit); static void show_commit(struct commit *commit) { if (show_timestamp) @@ -86,12 +88,18 @@ static void show_commit(struct commit *commit) struct strbuf buf; strbuf_init(&buf, 0); pretty_print_commit(revs.commit_format, commit, - &buf, revs.abbrev, NULL, NULL, revs.date_mode); + &buf, revs.abbrev, NULL, NULL, + revs.date_mode, 0); if (buf.len) printf("%s%c", buf.buf, hdr_termination); strbuf_release(&buf); } maybe_flush_or_die(stdout, "stdout"); + finish_commit(commit); +} + +static void finish_commit(struct commit *commit) +{ if (commit->parents) { free_commit_list(commit->parents); commit->parents = NULL; @@ -100,6 +108,12 @@ static void show_commit(struct commit *commit) commit->buffer = NULL; } +static void finish_object(struct object_array_entry *p) +{ + if (p->item->type == OBJ_BLOB && !has_sha1_file(p->item->sha1)) + die("missing blob object '%s'", sha1_to_hex(p->item->sha1)); +} + static void show_object(struct object_array_entry *p) { /* An object with name "foo\n0000000..." can be used to @@ -107,9 +121,7 @@ static void show_object(struct object_array_entry *p) */ const char *ep = strchr(p->name, '\n'); - if (p->item->type == OBJ_BLOB && !has_sha1_file(p->item->sha1)) - die("missing blob object '%s'", sha1_to_hex(p->item->sha1)); - + finish_object(p); if (ep) { printf("%s %.*s\n", sha1_to_hex(p->item->sha1), (int) (ep - p->name), @@ -141,7 +153,7 @@ static int count_distance(struct commit_list *entry) if (commit->object.flags & (UNINTERESTING | COUNTED)) break; - if (!revs.prune_fn || (commit->object.flags & TREECHANGE)) + if (!(commit->object.flags & TREESAME)) nr++; commit->object.flags |= COUNTED; p = commit->parents; @@ -197,7 +209,7 @@ static inline int halfway(struct commit_list *p, int nr) /* * Don't short-cut something we are not going to return! */ - if (revs.prune_fn && !(p->item->object.flags & TREECHANGE)) + if (p->item->object.flags & TREESAME) return 0; if (DEBUG_BISECT) return 0; @@ -233,7 +245,7 @@ static void show_list(const char *debug, int counted, int nr, char *ep, *sp; fprintf(stderr, "%c%c%c ", - (flags & TREECHANGE) ? 'T' : ' ', + (flags & TREESAME) ? ' ' : 'T', (flags & UNINTERESTING) ? 'U' : ' ', (flags & COUNTED) ? 'C' : ' '); if (commit->util) @@ -267,7 +279,7 @@ static struct commit_list *best_bisection(struct commit_list *list, int nr) int distance; unsigned flags = p->item->object.flags; - if (revs.prune_fn && !(flags & TREECHANGE)) + if (flags & TREESAME) continue; distance = weight(p); if (nr - distance < distance) @@ -307,7 +319,7 @@ static struct commit_list *best_bisection_sorted(struct commit_list *list, int n int distance; unsigned flags = p->item->object.flags; - if (revs.prune_fn && !(flags & TREECHANGE)) + if (flags & TREESAME) continue; distance = weight(p); if (nr - distance < distance) @@ -361,7 +373,7 @@ static struct commit_list *do_find_bisection(struct commit_list *list, p->item->util = &weights[n++]; switch (count_interesting_parents(commit)) { case 0: - if (!revs.prune_fn || (flags & TREECHANGE)) { + if (!(flags & TREESAME)) { weight_set(p, 1); counted++; show_list("bisection 2 count one", @@ -434,7 +446,7 @@ static struct commit_list *do_find_bisection(struct commit_list *list, * add one for p itself if p is to be counted, * otherwise inherit it from q directly. */ - if (!revs.prune_fn || (flags & TREECHANGE)) { + if (!(flags & TREESAME)) { weight_set(p, weight(q)+1); counted++; show_list("bisection 2 count one", @@ -481,7 +493,7 @@ static struct commit_list *find_bisection(struct commit_list *list, continue; p->next = last; last = p; - if (!revs.prune_fn || (flags & TREECHANGE)) + if (!(flags & TREESAME)) nr++; on_list++; } @@ -526,6 +538,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) int read_from_stdin = 0; int bisect_show_vars = 0; int bisect_find_all = 0; + int quiet = 0; git_config(git_default_config); init_revisions(&revs, prefix); @@ -564,6 +577,10 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) read_revisions_from_stdin(&revs); continue; } + if (!strcmp(arg, "--quiet")) { + quiet = 1; + continue; + } usage(rev_list_usage); } @@ -639,7 +656,9 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) } } - traverse_commit_list(&revs, show_commit, show_object); + traverse_commit_list(&revs, + quiet ? finish_commit : show_commit, + quiet ? finish_object : show_object); return 0; } diff --git a/builtin-rev-parse.c b/builtin-rev-parse.c index 8d78b69c90..20d1789e01 100644 --- a/builtin-rev-parse.c +++ b/builtin-rev-parse.c @@ -8,6 +8,7 @@ #include "refs.h" #include "quote.h" #include "builtin.h" +#include "parse-options.h" #define DO_REVS 1 #define DO_NOREV 2 @@ -209,13 +210,138 @@ static int try_difference(const char *arg) return 0; } +static int parseopt_dump(const struct option *o, const char *arg, int unset) +{ + struct strbuf *parsed = o->value; + if (unset) + strbuf_addf(parsed, " --no-%s", o->long_name); + else if (o->short_name) + strbuf_addf(parsed, " -%c", o->short_name); + else + strbuf_addf(parsed, " --%s", o->long_name); + if (arg) { + strbuf_addch(parsed, ' '); + sq_quote_buf(parsed, arg); + } + return 0; +} + +static const char *skipspaces(const char *s) +{ + while (isspace(*s)) + s++; + return s; +} + +static int cmd_parseopt(int argc, const char **argv, const char *prefix) +{ + static int keep_dashdash = 0; + static char const * const parseopt_usage[] = { + "git-rev-parse --parseopt [options] -- [<args>...]", + NULL + }; + static struct option parseopt_opts[] = { + OPT_BOOLEAN(0, "keep-dashdash", &keep_dashdash, + "keep the `--` passed as an arg"), + OPT_END(), + }; + + struct strbuf sb, parsed; + const char **usage = NULL; + struct option *opts = NULL; + int onb = 0, osz = 0, unb = 0, usz = 0; + + strbuf_init(&parsed, 0); + strbuf_addstr(&parsed, "set --"); + argc = parse_options(argc, argv, parseopt_opts, parseopt_usage, + PARSE_OPT_KEEP_DASHDASH); + if (argc < 1 || strcmp(argv[0], "--")) + usage_with_options(parseopt_usage, parseopt_opts); + + strbuf_init(&sb, 0); + /* get the usage up to the first line with a -- on it */ + for (;;) { + if (strbuf_getline(&sb, stdin, '\n') == EOF) + die("premature end of input"); + ALLOC_GROW(usage, unb + 1, usz); + if (!strcmp("--", sb.buf)) { + if (unb < 1) + die("no usage string given before the `--' separator"); + usage[unb] = NULL; + break; + } + usage[unb++] = strbuf_detach(&sb, NULL); + } + + /* parse: (<short>|<short>,<long>|<long>)[=?]? SP+ <help> */ + while (strbuf_getline(&sb, stdin, '\n') != EOF) { + const char *s; + struct option *o; + + if (!sb.len) + continue; + + ALLOC_GROW(opts, onb + 1, osz); + memset(opts + onb, 0, sizeof(opts[onb])); + + o = &opts[onb++]; + s = strchr(sb.buf, ' '); + if (!s || *sb.buf == ' ') { + o->type = OPTION_GROUP; + o->help = xstrdup(skipspaces(s)); + continue; + } + + o->type = OPTION_CALLBACK; + o->help = xstrdup(skipspaces(s)); + o->value = &parsed; + o->callback = &parseopt_dump; + switch (s[-1]) { + case '=': + s--; + break; + case '?': + o->flags = PARSE_OPT_OPTARG; + s--; + break; + default: + o->flags = PARSE_OPT_NOARG; + break; + } + + if (s - sb.buf == 1) /* short option only */ + o->short_name = *sb.buf; + else if (sb.buf[1] != ',') /* long option only */ + o->long_name = xmemdupz(sb.buf, s - sb.buf); + else { + o->short_name = *sb.buf; + o->long_name = xmemdupz(sb.buf + 2, s - sb.buf - 2); + } + } + strbuf_release(&sb); + + /* put an OPT_END() */ + ALLOC_GROW(opts, onb + 1, osz); + memset(opts + onb, 0, sizeof(opts[onb])); + argc = parse_options(argc, argv, opts, usage, + keep_dashdash ? PARSE_OPT_KEEP_DASHDASH : 0); + + strbuf_addf(&parsed, " --"); + sq_quote_argv(&parsed, argv, 0); + puts(parsed.buf); + return 0; +} + int cmd_rev_parse(int argc, const char **argv, const char *prefix) { int i, as_is = 0, verify = 0; unsigned char sha1[20]; - git_config(git_default_config); + if (argc > 1 && !strcmp("--parseopt", argv[1])) + return cmd_parseopt(argc - 1, argv + 1, prefix); + prefix = setup_git_directory(); + git_config(git_default_config); for (i = 1; i < argc; i++) { const char *arg = argv[i]; diff --git a/builtin-revert.c b/builtin-revert.c index a9347cf9c5..4bf8eb2f58 100644 --- a/builtin-revert.c +++ b/builtin-revert.c @@ -30,7 +30,7 @@ static const char * const cherry_pick_usage[] = { NULL }; -static int edit, no_replay, no_commit, needed_deref; +static int edit, no_replay, no_commit, mainline; static enum { REVERT, CHERRY_PICK } action; static struct commit *commit; @@ -50,12 +50,14 @@ static void parse_args(int argc, const char **argv) OPT_BOOLEAN('e', "edit", &edit, "edit the commit message"), OPT_BOOLEAN('x', NULL, &no_replay, "append commit name when cherry-picking"), OPT_BOOLEAN('r', NULL, &noop, "no-op (backward compatibility)"), + OPT_INTEGER('m', "mainline", &mainline, "parent number"), OPT_END(), }; if (parse_options(argc, argv, options, usage_str, 0) != 1) usage_with_options(usage_str, options); arg = argv[0]; + if (get_sha1(arg, sha1)) die ("Cannot find '%s'", arg); commit = (struct commit *)parse_object(sha1); @@ -64,7 +66,6 @@ static void parse_args(int argc, const char **argv) if (commit->object.type == OBJ_TAG) { commit = (struct commit *) deref_tag((struct object *)commit, arg, strlen(arg)); - needed_deref = 1; } if (commit->object.type != OBJ_COMMIT) die ("'%s' does not point to a commit", arg); @@ -223,10 +224,31 @@ static int merge_recursive(const char *base_sha1, return run_command_v_opt(argv, RUN_COMMAND_NO_STDIN | RUN_GIT_CMD); } +static char *help_msg(const unsigned char *sha1) +{ + static char helpbuf[1024]; + char *msg = getenv("GIT_CHERRY_PICK_HELP"); + + if (msg) + return msg; + + strcpy(helpbuf, " After resolving the conflicts,\n" + "mark the corrected paths with 'git add <paths>' " + "or 'git rm <paths>' and commit the result."); + + if (action == CHERRY_PICK) { + sprintf(helpbuf + strlen(helpbuf), + "\nWhen commiting, use the option " + "'-c %s' to retain authorship and message.", + find_unique_abbrev(sha1, DEFAULT_ABBREV)); + } + return helpbuf; +} + static int revert_or_cherry_pick(int argc, const char **argv) { unsigned char head[20]; - struct commit *base, *next; + struct commit *base, *next, *parent; int i; char *oneline, *reencoded_message = NULL; const char *message, *encoding; @@ -244,7 +266,9 @@ static int revert_or_cherry_pick(int argc, const char **argv) if (no_commit) { /* * We do not intend to commit immediately. We just want to - * merge the differences in. + * merge the differences in, so let's compute the tree + * that represents the "current" state for merge-recursive + * to work on. */ if (write_tree(head, 0, NULL)) die ("Your index file is unmerged."); @@ -254,15 +278,36 @@ static int revert_or_cherry_pick(int argc, const char **argv) if (get_sha1("HEAD", head)) die ("You do not have a valid HEAD"); wt_status_prepare(&s); - if (s.commitable || s.workdir_dirty) + if (s.commitable) die ("Dirty index: cannot %s", me); discard_cache(); } if (!commit->parents) die ("Cannot %s a root commit", me); - if (commit->parents->next) - die ("Cannot %s a multi-parent commit.", me); + if (commit->parents->next) { + /* Reverting or cherry-picking a merge commit */ + int cnt; + struct commit_list *p; + + if (!mainline) + die("Commit %s is a merge but no -m option was given.", + sha1_to_hex(commit->object.sha1)); + + for (cnt = 1, p = commit->parents; + cnt != mainline && p; + cnt++) + p = p->next; + if (cnt != mainline || !p) + die("Commit %s does not have parent %d", + sha1_to_hex(commit->object.sha1), mainline); + parent = p->item; + } else if (0 < mainline) + die("Mainline was specified but commit %s is not a merge.", + sha1_to_hex(commit->object.sha1)); + else + parent = commit->parents->item; + if (!(message = commit->buffer)) die ("Cannot get commit message for %s", sha1_to_hex(commit->object.sha1)); @@ -291,14 +336,14 @@ static int revert_or_cherry_pick(int argc, const char **argv) char *oneline_body = strchr(oneline, ' '); base = commit; - next = commit->parents->item; + next = parent; add_to_msg("Revert \""); add_to_msg(oneline_body + 1); add_to_msg("\"\n\nThis reverts commit "); add_to_msg(sha1_to_hex(commit->object.sha1)); add_to_msg(".\n"); } else { - base = commit->parents->item; + base = parent; next = commit; set_author_ident_env(message); add_message_to_msg(message); @@ -308,17 +353,6 @@ static int revert_or_cherry_pick(int argc, const char **argv) add_to_msg(")\n"); } } - if (needed_deref) { - add_to_msg("(original 'git "); - add_to_msg(me); - add_to_msg("' arguments: "); - for (i = 0; i < argc; i++) { - if (i) - add_to_msg(" "); - add_to_msg(argv[i]); - } - add_to_msg(")\n"); - } if (merge_recursive(sha1_to_hex(base->object.sha1), sha1_to_hex(head), "HEAD", @@ -339,16 +373,8 @@ static int revert_or_cherry_pick(int argc, const char **argv) } if (close(msg_fd) || commit_lock_file(&msg_file) < 0) die ("Error wrapping up %s", defmsg); - fprintf(stderr, "Automatic %s failed. " - "After resolving the conflicts,\n" - "mark the corrected paths with 'git add <paths>' " - "and commit the result.\n", me); - if (action == CHERRY_PICK) { - fprintf(stderr, "When commiting, use the option " - "'-c %s' to retain authorship and message.\n", - find_unique_abbrev(commit->object.sha1, - DEFAULT_ABBREV)); - } + fprintf(stderr, "Automatic %s failed.%s\n", + me, help_msg(commit->object.sha1)); exit(1); } if (close(msg_fd) || commit_lock_file(&msg_file) < 0) diff --git a/builtin-rm.c b/builtin-rm.c index bca2bd9703..a3d25e6a57 100644 --- a/builtin-rm.c +++ b/builtin-rm.c @@ -155,6 +155,9 @@ int cmd_rm(int argc, const char **argv, const char *prefix) if (!argc) usage_with_options(builtin_rm_usage, builtin_rm_options); + if (!index_only) + setup_work_tree(); + pathspec = get_pathspec(prefix, argv); seen = NULL; for (i = 0; pathspec[i] ; i++) diff --git a/builtin-runstatus.c b/builtin-runstatus.c index 2db25c88bf..8d167a9674 100644 --- a/builtin-runstatus.c +++ b/builtin-runstatus.c @@ -14,6 +14,7 @@ int cmd_runstatus(int argc, const char **argv, const char *prefix) git_config(git_status_config); wt_status_prepare(&s); + s.prefix = prefix; for (i = 1; i < argc; i++) { if (!strcmp(argv[i], "--color")) diff --git a/builtin-send-pack.c b/builtin-send-pack.c new file mode 100644 index 0000000000..25ae1fe860 --- /dev/null +++ b/builtin-send-pack.c @@ -0,0 +1,652 @@ +#include "cache.h" +#include "commit.h" +#include "tag.h" +#include "refs.h" +#include "pkt-line.h" +#include "run-command.h" +#include "remote.h" +#include "send-pack.h" + +static const char send_pack_usage[] = +"git-send-pack [--all | --mirror] [--dry-run] [--force] [--receive-pack=<git-receive-pack>] [--verbose] [--thin] [<host>:]<directory> [<ref>...]\n" +" --all and explicit <ref> specification are mutually exclusive."; + +static struct send_pack_args args = { + /* .receivepack = */ "git-receive-pack", +}; + +/* + * Make a pack stream and spit it out into file descriptor fd + */ +static int pack_objects(int fd, struct ref *refs) +{ + /* + * The child becomes pack-objects --revs; we feed + * the revision parameters to it via its stdin and + * let its stdout go back to the other end. + */ + const char *argv[] = { + "pack-objects", + "--all-progress", + "--revs", + "--stdout", + NULL, + NULL, + }; + struct child_process po; + + if (args.use_thin_pack) + argv[4] = "--thin"; + memset(&po, 0, sizeof(po)); + po.argv = argv; + po.in = -1; + po.out = fd; + po.git_cmd = 1; + if (start_command(&po)) + die("git-pack-objects failed (%s)", strerror(errno)); + + /* + * We feed the pack-objects we just spawned with revision + * parameters by writing to the pipe. + */ + while (refs) { + char buf[42]; + + if (!is_null_sha1(refs->old_sha1) && + has_sha1_file(refs->old_sha1)) { + memcpy(buf + 1, sha1_to_hex(refs->old_sha1), 40); + buf[0] = '^'; + buf[41] = '\n'; + if (!write_or_whine(po.in, buf, 42, + "send-pack: send refs")) + break; + } + if (!is_null_sha1(refs->new_sha1)) { + memcpy(buf, sha1_to_hex(refs->new_sha1), 40); + buf[40] = '\n'; + if (!write_or_whine(po.in, buf, 41, + "send-pack: send refs")) + break; + } + refs = refs->next; + } + + if (finish_command(&po)) + return error("pack-objects died with strange error"); + return 0; +} + +static void unmark_and_free(struct commit_list *list, unsigned int mark) +{ + while (list) { + struct commit_list *temp = list; + temp->item->object.flags &= ~mark; + list = temp->next; + free(temp); + } +} + +static int ref_newer(const unsigned char *new_sha1, + const unsigned char *old_sha1) +{ + struct object *o; + struct commit *old, *new; + struct commit_list *list, *used; + int found = 0; + + /* Both new and old must be commit-ish and new is descendant of + * old. Otherwise we require --force. + */ + o = deref_tag(parse_object(old_sha1), NULL, 0); + if (!o || o->type != OBJ_COMMIT) + return 0; + old = (struct commit *) o; + + o = deref_tag(parse_object(new_sha1), NULL, 0); + if (!o || o->type != OBJ_COMMIT) + return 0; + new = (struct commit *) o; + + if (parse_commit(new) < 0) + return 0; + + used = list = NULL; + commit_list_insert(new, &list); + while (list) { + new = pop_most_recent_commit(&list, 1); + commit_list_insert(new, &used); + if (new == old) { + found = 1; + break; + } + } + unmark_and_free(list, 1); + unmark_and_free(used, 1); + return found; +} + +static struct ref *local_refs, **local_tail; +static struct ref *remote_refs, **remote_tail; + +static int one_local_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data) +{ + struct ref *ref; + int len = strlen(refname) + 1; + ref = xcalloc(1, sizeof(*ref) + len); + hashcpy(ref->new_sha1, sha1); + memcpy(ref->name, refname, len); + *local_tail = ref; + local_tail = &ref->next; + return 0; +} + +static void get_local_heads(void) +{ + local_tail = &local_refs; + for_each_ref(one_local_ref, NULL); +} + +static int receive_status(int in, struct ref *refs) +{ + struct ref *hint; + char line[1000]; + int ret = 0; + int len = packet_read_line(in, line, sizeof(line)); + if (len < 10 || memcmp(line, "unpack ", 7)) + return error("did not receive remote status"); + if (memcmp(line, "unpack ok\n", 10)) { + char *p = line + strlen(line) - 1; + if (*p == '\n') + *p = '\0'; + error("unpack failed: %s", line + 7); + ret = -1; + } + hint = NULL; + while (1) { + char *refname; + char *msg; + len = packet_read_line(in, line, sizeof(line)); + if (!len) + break; + if (len < 3 || + (memcmp(line, "ok ", 3) && memcmp(line, "ng ", 3))) { + fprintf(stderr, "protocol error: %s\n", line); + ret = -1; + break; + } + + line[strlen(line)-1] = '\0'; + refname = line + 3; + msg = strchr(refname, ' '); + if (msg) + *msg++ = '\0'; + + /* first try searching at our hint, falling back to all refs */ + if (hint) + hint = find_ref_by_name(hint, refname); + if (!hint) + hint = find_ref_by_name(refs, refname); + if (!hint) { + warning("remote reported status on unknown ref: %s", + refname); + continue; + } + if (hint->status != REF_STATUS_EXPECTING_REPORT) { + warning("remote reported status on unexpected ref: %s", + refname); + continue; + } + + if (line[0] == 'o' && line[1] == 'k') + hint->status = REF_STATUS_OK; + else { + hint->status = REF_STATUS_REMOTE_REJECT; + ret = -1; + } + if (msg) + hint->remote_status = xstrdup(msg); + /* start our next search from the next ref */ + hint = hint->next; + } + return ret; +} + +static void update_tracking_ref(struct remote *remote, struct ref *ref) +{ + struct refspec rs; + + if (ref->status != REF_STATUS_OK) + return; + + rs.src = ref->name; + rs.dst = NULL; + + if (!remote_find_tracking(remote, &rs)) { + if (args.verbose) + fprintf(stderr, "updating local tracking ref '%s'\n", rs.dst); + if (ref->deletion) { + if (delete_ref(rs.dst, NULL)) + error("Failed to delete"); + } else + update_ref("update by push", rs.dst, + ref->new_sha1, NULL, 0, 0); + free(rs.dst); + } +} + +static const char *prettify_ref(const struct ref *ref) +{ + const char *name = ref->name; + return name + ( + !prefixcmp(name, "refs/heads/") ? 11 : + !prefixcmp(name, "refs/tags/") ? 10 : + !prefixcmp(name, "refs/remotes/") ? 13 : + 0); +} + +#define SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3) + +static void print_ref_status(char flag, const char *summary, struct ref *to, struct ref *from, const char *msg) +{ + fprintf(stderr, " %c %-*s ", flag, SUMMARY_WIDTH, summary); + if (from) + fprintf(stderr, "%s -> %s", prettify_ref(from), prettify_ref(to)); + else + fputs(prettify_ref(to), stderr); + if (msg) { + fputs(" (", stderr); + fputs(msg, stderr); + fputc(')', stderr); + } + fputc('\n', stderr); +} + +static const char *status_abbrev(unsigned char sha1[20]) +{ + const char *abbrev; + abbrev = find_unique_abbrev(sha1, DEFAULT_ABBREV); + return abbrev ? abbrev : sha1_to_hex(sha1); +} + +static void print_ok_ref_status(struct ref *ref) +{ + if (ref->deletion) + print_ref_status('-', "[deleted]", ref, NULL, NULL); + else if (is_null_sha1(ref->old_sha1)) + print_ref_status('*', + (!prefixcmp(ref->name, "refs/tags/") ? "[new tag]" : + "[new branch]"), + ref, ref->peer_ref, NULL); + else { + char quickref[84]; + char type; + const char *msg; + + strcpy(quickref, status_abbrev(ref->old_sha1)); + if (ref->nonfastforward) { + strcat(quickref, "..."); + type = '+'; + msg = "forced update"; + } else { + strcat(quickref, ".."); + type = ' '; + msg = NULL; + } + strcat(quickref, status_abbrev(ref->new_sha1)); + + print_ref_status(type, quickref, ref, ref->peer_ref, msg); + } +} + +static int print_one_push_status(struct ref *ref, const char *dest, int count) +{ + if (!count) + fprintf(stderr, "To %s\n", dest); + + switch(ref->status) { + case REF_STATUS_NONE: + print_ref_status('X', "[no match]", ref, NULL, NULL); + break; + case REF_STATUS_REJECT_NODELETE: + print_ref_status('!', "[rejected]", ref, NULL, + "remote does not support deleting refs"); + break; + case REF_STATUS_UPTODATE: + print_ref_status('=', "[up to date]", ref, + ref->peer_ref, NULL); + break; + case REF_STATUS_REJECT_NONFASTFORWARD: + print_ref_status('!', "[rejected]", ref, ref->peer_ref, + "non-fast forward"); + break; + case REF_STATUS_REMOTE_REJECT: + print_ref_status('!', "[remote rejected]", ref, + ref->deletion ? NULL : ref->peer_ref, + ref->remote_status); + break; + case REF_STATUS_EXPECTING_REPORT: + print_ref_status('!', "[remote failure]", ref, + ref->deletion ? NULL : ref->peer_ref, + "remote failed to report status"); + break; + case REF_STATUS_OK: + print_ok_ref_status(ref); + break; + } + + return 1; +} + +static void print_push_status(const char *dest, struct ref *refs) +{ + struct ref *ref; + int n = 0; + + if (args.verbose) { + for (ref = refs; ref; ref = ref->next) + if (ref->status == REF_STATUS_UPTODATE) + n += print_one_push_status(ref, dest, n); + } + + for (ref = refs; ref; ref = ref->next) + if (ref->status == REF_STATUS_OK) + n += print_one_push_status(ref, dest, n); + + for (ref = refs; ref; ref = ref->next) { + if (ref->status != REF_STATUS_NONE && + ref->status != REF_STATUS_UPTODATE && + ref->status != REF_STATUS_OK) + n += print_one_push_status(ref, dest, n); + } +} + +static int refs_pushed(struct ref *ref) +{ + for (; ref; ref = ref->next) { + switch(ref->status) { + case REF_STATUS_NONE: + case REF_STATUS_UPTODATE: + break; + default: + return 1; + } + } + return 0; +} + +static int do_send_pack(int in, int out, struct remote *remote, const char *dest, int nr_refspec, const char **refspec) +{ + struct ref *ref; + int new_refs; + int ask_for_status_report = 0; + int allow_deleting_refs = 0; + int expect_status_report = 0; + int flags = MATCH_REFS_NONE; + int ret; + + if (args.send_all) + flags |= MATCH_REFS_ALL; + if (args.send_mirror) + flags |= MATCH_REFS_MIRROR; + + /* No funny business with the matcher */ + remote_tail = get_remote_heads(in, &remote_refs, 0, NULL, REF_NORMAL); + get_local_heads(); + + /* Does the other end support the reporting? */ + if (server_supports("report-status")) + ask_for_status_report = 1; + if (server_supports("delete-refs")) + allow_deleting_refs = 1; + + /* match them up */ + if (!remote_tail) + remote_tail = &remote_refs; + if (match_refs(local_refs, remote_refs, &remote_tail, + nr_refspec, refspec, flags)) + return -1; + + if (!remote_refs) { + fprintf(stderr, "No refs in common and none specified; doing nothing.\n" + "Perhaps you should specify a branch such as 'master'.\n"); + return 0; + } + + /* + * Finally, tell the other end! + */ + new_refs = 0; + for (ref = remote_refs; ref; ref = ref->next) { + const unsigned char *new_sha1; + + if (!ref->peer_ref) { + if (!args.send_mirror) + continue; + new_sha1 = null_sha1; + } + else + new_sha1 = ref->peer_ref->new_sha1; + + + ref->deletion = is_null_sha1(new_sha1); + if (ref->deletion && !allow_deleting_refs) { + ref->status = REF_STATUS_REJECT_NODELETE; + continue; + } + if (!ref->deletion && + !hashcmp(ref->old_sha1, new_sha1)) { + ref->status = REF_STATUS_UPTODATE; + continue; + } + + /* This part determines what can overwrite what. + * The rules are: + * + * (0) you can always use --force or +A:B notation to + * selectively force individual ref pairs. + * + * (1) if the old thing does not exist, it is OK. + * + * (2) if you do not have the old thing, you are not allowed + * to overwrite it; you would not know what you are losing + * otherwise. + * + * (3) if both new and old are commit-ish, and new is a + * descendant of old, it is OK. + * + * (4) regardless of all of the above, removing :B is + * always allowed. + */ + + ref->nonfastforward = + !ref->deletion && + !is_null_sha1(ref->old_sha1) && + (!has_sha1_file(ref->old_sha1) + || !ref_newer(new_sha1, ref->old_sha1)); + + if (ref->nonfastforward && !ref->force && !args.force_update) { + ref->status = REF_STATUS_REJECT_NONFASTFORWARD; + continue; + } + + hashcpy(ref->new_sha1, new_sha1); + if (!ref->deletion) + new_refs++; + + if (!args.dry_run) { + char *old_hex = sha1_to_hex(ref->old_sha1); + char *new_hex = sha1_to_hex(ref->new_sha1); + + if (ask_for_status_report) { + packet_write(out, "%s %s %s%c%s", + old_hex, new_hex, ref->name, 0, + "report-status"); + ask_for_status_report = 0; + expect_status_report = 1; + } + else + packet_write(out, "%s %s %s", + old_hex, new_hex, ref->name); + } + ref->status = expect_status_report ? + REF_STATUS_EXPECTING_REPORT : + REF_STATUS_OK; + } + + packet_flush(out); + if (new_refs && !args.dry_run) { + if (pack_objects(out, remote_refs) < 0) { + close(out); + return -1; + } + } + close(out); + + if (expect_status_report) + ret = receive_status(in, remote_refs); + else + ret = 0; + + print_push_status(dest, remote_refs); + + if (!args.dry_run && remote) { + for (ref = remote_refs; ref; ref = ref->next) + update_tracking_ref(remote, ref); + } + + if (!refs_pushed(remote_refs)) + fprintf(stderr, "Everything up-to-date\n"); + if (ret < 0) + return ret; + for (ref = remote_refs; ref; ref = ref->next) { + switch (ref->status) { + case REF_STATUS_NONE: + case REF_STATUS_UPTODATE: + case REF_STATUS_OK: + break; + default: + return -1; + } + } + return 0; +} + +static void verify_remote_names(int nr_heads, const char **heads) +{ + int i; + + for (i = 0; i < nr_heads; i++) { + const char *remote = strchr(heads[i], ':'); + + remote = remote ? (remote + 1) : heads[i]; + switch (check_ref_format(remote)) { + case 0: /* ok */ + case -2: /* ok but a single level -- that is fine for + * a match pattern. + */ + case -3: /* ok but ends with a pattern-match character */ + continue; + } + die("remote part of refspec is not a valid name in %s", + heads[i]); + } +} + +int cmd_send_pack(int argc, const char **argv, const char *prefix) +{ + int i, nr_heads = 0; + const char **heads = NULL; + const char *remote_name = NULL; + struct remote *remote = NULL; + const char *dest = NULL; + + argv++; + for (i = 1; i < argc; i++, argv++) { + const char *arg = *argv; + + if (*arg == '-') { + if (!prefixcmp(arg, "--receive-pack=")) { + args.receivepack = arg + 15; + continue; + } + if (!prefixcmp(arg, "--exec=")) { + args.receivepack = arg + 7; + continue; + } + if (!prefixcmp(arg, "--remote=")) { + remote_name = arg + 9; + continue; + } + if (!strcmp(arg, "--all")) { + args.send_all = 1; + continue; + } + if (!strcmp(arg, "--dry-run")) { + args.dry_run = 1; + continue; + } + if (!strcmp(arg, "--mirror")) { + args.send_mirror = 1; + continue; + } + if (!strcmp(arg, "--force")) { + args.force_update = 1; + continue; + } + if (!strcmp(arg, "--verbose")) { + args.verbose = 1; + continue; + } + if (!strcmp(arg, "--thin")) { + args.use_thin_pack = 1; + continue; + } + usage(send_pack_usage); + } + if (!dest) { + dest = arg; + continue; + } + heads = (const char **) argv; + nr_heads = argc - i; + break; + } + if (!dest) + usage(send_pack_usage); + /* + * --all and --mirror are incompatible; neither makes sense + * with any refspecs. + */ + if ((heads && (args.send_all || args.send_mirror)) || + (args.send_all && args.send_mirror)) + usage(send_pack_usage); + + if (remote_name) { + remote = remote_get(remote_name); + if (!remote_has_url(remote, dest)) { + die("Destination %s is not a uri for %s", + dest, remote_name); + } + } + + return send_pack(&args, dest, remote, nr_heads, heads); +} + +int send_pack(struct send_pack_args *my_args, + const char *dest, struct remote *remote, + int nr_heads, const char **heads) +{ + int fd[2], ret; + struct child_process *conn; + + memcpy(&args, my_args, sizeof(args)); + + verify_remote_names(nr_heads, heads); + + conn = git_connect(fd, dest, args.receivepack, args.verbose ? CONNECT_VERBOSE : 0); + ret = do_send_pack(fd[0], fd[1], remote, dest, nr_heads, heads); + close(fd[0]); + close(fd[1]); + ret |= finish_connect(conn); + return !!ret; +} diff --git a/builtin-shortlog.c b/builtin-shortlog.c index 3fe754677d..b9cc134443 100644 --- a/builtin-shortlog.c +++ b/builtin-shortlog.c @@ -27,45 +27,60 @@ static int compare_by_number(const void *a1, const void *a2) static struct path_list mailmap = {NULL, 0, 0, 0}; -static void insert_author_oneline(struct path_list *list, - const char *author, int authorlen, - const char *oneline, int onelinelen) +static void insert_one_record(struct path_list *list, + const char *author, + const char *oneline) { const char *dot3 = common_repo_prefix; char *buffer, *p; struct path_list_item *item; struct path_list *onelines; + char namebuf[1024]; + size_t len; + const char *eol; + const char *boemail, *eoemail; + + boemail = strchr(author, '<'); + if (!boemail) + return; + eoemail = strchr(boemail, '>'); + if (!eoemail) + return; + if (!map_email(&mailmap, boemail+1, namebuf, sizeof(namebuf))) { + while (author < boemail && isspace(*author)) + author++; + for (len = 0; + len < sizeof(namebuf) - 1 && author + len < boemail; + len++) + namebuf[len] = author[len]; + while (0 < len && isspace(namebuf[len-1])) + len--; + namebuf[len] = '\0'; + } - while (authorlen > 0 && isspace(author[authorlen - 1])) - authorlen--; - - buffer = xmemdupz(author, authorlen); + buffer = xstrdup(namebuf); item = path_list_insert(buffer, list); if (item->util == NULL) item->util = xcalloc(1, sizeof(struct path_list)); else free(buffer); + eol = strchr(oneline, '\n'); + if (!eol) + eol = oneline + strlen(oneline); + while (*oneline && isspace(*oneline) && *oneline != '\n') + oneline++; if (!prefixcmp(oneline, "[PATCH")) { char *eob = strchr(oneline, ']'); - - if (eob) { - while (isspace(eob[1]) && eob[1] != '\n') - eob++; - if (eob - oneline < onelinelen) { - onelinelen -= eob - oneline; - oneline = eob; - } - } + if (eob && (!eol || eob < eol)) + oneline = eob + 1; } - - while (onelinelen > 0 && isspace(oneline[0])) { + while (*oneline && isspace(*oneline) && *oneline != '\n') oneline++; - onelinelen--; - } - while (onelinelen > 0 && isspace(oneline[onelinelen - 1])) - onelinelen--; - buffer = xmemdupz(oneline, onelinelen); + len = eol - oneline; + while (len && isspace(oneline[len-1])) + len--; + buffer = xmemdupz(oneline, len); if (dot3) { int dot3len = strlen(dot3); @@ -92,55 +107,32 @@ static void insert_author_oneline(struct path_list *list, static void read_from_stdin(struct path_list *list) { - char buffer[1024]; - - while (fgets(buffer, sizeof(buffer), stdin) != NULL) { - char *bob; - if ((buffer[0] == 'A' || buffer[0] == 'a') && - !prefixcmp(buffer + 1, "uthor: ") && - (bob = strchr(buffer + 7, '<')) != NULL) { - char buffer2[1024], offset = 0; - - if (map_email(&mailmap, bob + 1, buffer, sizeof(buffer))) - bob = buffer + strlen(buffer); - else { - offset = 8; - while (buffer + offset < bob && - isspace(bob[-1])) - bob--; - } - - while (fgets(buffer2, sizeof(buffer2), stdin) && - buffer2[0] != '\n') - ; /* chomp input */ - if (fgets(buffer2, sizeof(buffer2), stdin)) { - int l2 = strlen(buffer2); - int i; - for (i = 0; i < l2; i++) - if (!isspace(buffer2[i])) - break; - insert_author_oneline(list, - buffer + offset, - bob - buffer - offset, - buffer2 + i, l2 - i); - } - } + char author[1024], oneline[1024]; + + while (fgets(author, sizeof(author), stdin) != NULL) { + if (!(author[0] == 'A' || author[0] == 'a') || + prefixcmp(author + 1, "uthor: ")) + continue; + while (fgets(oneline, sizeof(oneline), stdin) && + oneline[0] != '\n') + ; /* discard headers */ + while (fgets(oneline, sizeof(oneline), stdin) && + oneline[0] == '\n') + ; /* discard blanks */ + insert_one_record(list, author + 8, oneline); } } static void get_from_rev(struct rev_info *rev, struct path_list *list) { - char scratch[1024]; struct commit *commit; prepare_revision_walk(rev); while ((commit = get_revision(rev)) != NULL) { - const char *author = NULL, *oneline, *buffer; - int authorlen = authorlen, onelinelen; + const char *author = NULL, *buffer; - /* get author and oneline */ - for (buffer = commit->buffer; buffer && *buffer != '\0' && - *buffer != '\n'; ) { + buffer = commit->buffer; + while (*buffer && *buffer != '\n') { const char *eol = strchr(buffer, '\n'); if (eol == NULL) @@ -148,50 +140,17 @@ static void get_from_rev(struct rev_info *rev, struct path_list *list) else eol++; - if (!prefixcmp(buffer, "author ")) { - char *bracket = strchr(buffer, '<'); - - if (bracket == NULL || bracket > eol) - die("Invalid commit buffer: %s", - sha1_to_hex(commit->object.sha1)); - - if (map_email(&mailmap, bracket + 1, scratch, - sizeof(scratch))) { - author = scratch; - authorlen = strlen(scratch); - } else { - if (bracket[-1] == ' ') - bracket--; - - author = buffer + 7; - authorlen = bracket - buffer - 7; - } - } + if (!prefixcmp(buffer, "author ")) + author = buffer + 7; buffer = eol; } - - if (author == NULL) - die ("Missing author: %s", - sha1_to_hex(commit->object.sha1)); - - if (buffer == NULL || *buffer == '\0') { - oneline = "<none>"; - onelinelen = sizeof(oneline) + 1; - } else { - char *eol; - - oneline = buffer + 1; - eol = strchr(oneline, '\n'); - if (eol == NULL) - onelinelen = strlen(oneline); - else - onelinelen = eol - oneline; - } - - insert_author_oneline(list, - author, authorlen, oneline, onelinelen); + if (!author) + die("Missing author: %s", + sha1_to_hex(commit->object.sha1)); + if (*buffer) + buffer++; + insert_one_record(list, author, !*buffer ? "<none>" : buffer); } - } static int parse_uint(char const **arg, int comma) diff --git a/builtin-show-branch.c b/builtin-show-branch.c index 07a0c2316b..6dc835d30a 100644 --- a/builtin-show-branch.c +++ b/builtin-show-branch.c @@ -266,7 +266,7 @@ static void show_one_commit(struct commit *commit, int no_name) strbuf_init(&pretty, 0); if (commit->object.parsed) { pretty_print_commit(CMIT_FMT_ONELINE, commit, - &pretty, 0, NULL, NULL, 0); + &pretty, 0, NULL, NULL, 0, 0); pretty_str = pretty.buf; } if (!prefixcmp(pretty_str, "[PATCH] ")) diff --git a/builtin-tag.c b/builtin-tag.c index 66e5a58307..517419fd3d 100644 --- a/builtin-tag.c +++ b/builtin-tag.c @@ -11,17 +11,21 @@ #include "refs.h" #include "tag.h" #include "run-command.h" - -static const char builtin_tag_usage[] = - "git-tag [-n [<num>]] -l [<pattern>] | [-a | -s | -u <key-id>] [-f | -d | -v] [-m <msg> | -F <file>] <tagname> [<head>]"; +#include "parse-options.h" + +static const char * const git_tag_usage[] = { + "git-tag [-a|-s|-u <key-id>] [-f] [-m <msg>|-F <file>] <tagname> [<head>]", + "git-tag -d <tagname>...", + "git-tag [-n [<num>]] -l [<pattern>]", + "git-tag -v <tagname>...", + NULL +}; static char signingkey[1000]; -static void launch_editor(const char *path, struct strbuf *buffer) +void launch_editor(const char *path, struct strbuf *buffer, const char *const *env) { const char *editor, *terminal; - struct child_process child; - const char *args[3]; editor = getenv("GIT_EDITOR"); if (!editor && editor_program) @@ -42,15 +46,15 @@ static void launch_editor(const char *path, struct strbuf *buffer) if (!editor) editor = "vi"; - memset(&child, 0, sizeof(child)); - child.argv = args; - args[0] = editor; - args[1] = path; - args[2] = NULL; + if (strcmp(editor, ":")) { + const char *args[] = { editor, path, NULL }; - if (run_command(&child)) - die("There was a problem with the editor %s.", editor); + if (run_command_v_opt_cd_env(args, 0, NULL, env)) + die("There was a problem with the editor %s.", editor); + } + if (!buffer) + return; if (strbuf_read_file(buffer, path, 0) < 0) die("could not read message file '%s': %s", path, strerror(errno)); @@ -81,17 +85,16 @@ static int show_reference(const char *refname, const unsigned char *sha1, } printf("%-15s ", refname); - sp = buf = read_sha1_file(sha1, &type, &size); - if (!buf) + buf = read_sha1_file(sha1, &type, &size); + if (!buf || !size) return 0; - if (!size) { + + /* skip header */ + sp = strstr(buf, "\n\n"); + if (!sp) { free(buf); return 0; } - /* skip header */ - while (sp + 1 < buf + size && - !(sp[0] == '\n' && sp[1] == '\n')) - sp++; /* only take up to "lines" lines, and strip the signature */ for (i = 0, sp += 2; i < filter->lines && sp < buf + size && @@ -185,7 +188,7 @@ static int do_sign(struct strbuf *buffer) int len; if (!*signingkey) { - if (strlcpy(signingkey, git_committer_info(1), + if (strlcpy(signingkey, git_committer_info(IDENT_ERROR_ON_NO_NAME), sizeof(signingkey)) > sizeof(signingkey) - 1) return error("committer info too long."); bracket = strchr(signingkey, '>'); @@ -247,9 +250,37 @@ static int git_tag_config(const char *var, const char *value) return git_default_config(var, value); } +static void write_tag_body(int fd, const unsigned char *sha1) +{ + unsigned long size; + enum object_type type; + char *buf, *sp, *eob; + size_t len; + + buf = read_sha1_file(sha1, &type, &size); + if (!buf) + return; + /* skip header */ + sp = strstr(buf, "\n\n"); + + if (!sp || !size || type != OBJ_TAG) { + free(buf); + return; + } + sp += 2; /* skip the 2 LFs */ + eob = strstr(sp, "\n" PGP_SIGNATURE "\n"); + if (eob) + len = eob - sp; + else + len = buf + size - sp; + write_or_die(fd, sp, len); + + free(buf); +} + static void create_tag(const unsigned char *object, const char *tag, struct strbuf *buf, int message, int sign, - unsigned char *result) + unsigned char *prev, unsigned char *result) { enum object_type type; char header_buf[1024]; @@ -267,7 +298,7 @@ static void create_tag(const unsigned char *object, const char *tag, sha1_to_hex(object), typename(type), tag, - git_committer_info(1)); + git_committer_info(IDENT_ERROR_ON_NO_NAME)); if (header_len > sizeof(header_buf) - 1) die("tag header too big."); @@ -282,10 +313,14 @@ static void create_tag(const unsigned char *object, const char *tag, if (fd < 0) die("could not create file '%s': %s", path, strerror(errno)); - write_or_die(fd, tag_template, strlen(tag_template)); + + if (!is_null_sha1(prev)) + write_tag_body(fd, prev); + else + write_or_die(fd, tag_template, strlen(tag_template)); close(fd); - launch_editor(path, buf); + launch_editor(path, buf, NULL); unlink(path); free(path); @@ -304,105 +339,101 @@ static void create_tag(const unsigned char *object, const char *tag, die("unable to write tag file"); } +struct msg_arg { + int given; + struct strbuf buf; +}; + +static int parse_msg_arg(const struct option *opt, const char *arg, int unset) +{ + struct msg_arg *msg = opt->value; + + if (!arg) + return -1; + if (msg->buf.len) + strbuf_addstr(&(msg->buf), "\n\n"); + strbuf_addstr(&(msg->buf), arg); + msg->given = 1; + return 0; +} + int cmd_tag(int argc, const char **argv, const char *prefix) { struct strbuf buf; unsigned char object[20], prev[20]; - int annotate = 0, sign = 0, force = 0, lines = 0, message = 0; char ref[PATH_MAX]; const char *object_ref, *tag; - int i; struct ref_lock *lock; + int annotate = 0, sign = 0, force = 0, lines = 0, + delete = 0, verify = 0; + char *list = NULL, *msgfile = NULL, *keyid = NULL; + const char *no_pattern = "NO_PATTERN"; + struct msg_arg msg = { 0, STRBUF_INIT }; + struct option options[] = { + { OPTION_STRING, 'l', NULL, &list, "pattern", "list tag names", + PARSE_OPT_OPTARG, NULL, (intptr_t) no_pattern }, + { OPTION_INTEGER, 'n', NULL, &lines, NULL, + "print n lines of each tag message", + PARSE_OPT_OPTARG, NULL, 1 }, + OPT_BOOLEAN('d', NULL, &delete, "delete tags"), + OPT_BOOLEAN('v', NULL, &verify, "verify tags"), + + OPT_GROUP("Tag creation options"), + OPT_BOOLEAN('a', NULL, &annotate, + "annotated tag, needs a message"), + OPT_CALLBACK('m', NULL, &msg, "msg", + "message for the tag", parse_msg_arg), + OPT_STRING('F', NULL, &msgfile, "file", "message in a file"), + OPT_BOOLEAN('s', NULL, &sign, "annotated and GPG-signed tag"), + OPT_STRING('u', NULL, &keyid, "key-id", + "use another key to sign the tag"), + OPT_BOOLEAN('f', NULL, &force, "replace the tag if exists"), + OPT_END() + }; + git_config(git_tag_config); - strbuf_init(&buf, 0); - for (i = 1; i < argc; i++) { - const char *arg = argv[i]; + argc = parse_options(argc, argv, options, git_tag_usage, 0); - if (arg[0] != '-') - break; - if (!strcmp(arg, "-a")) { - annotate = 1; - continue; - } - if (!strcmp(arg, "-s")) { - annotate = 1; - sign = 1; - continue; - } - if (!strcmp(arg, "-f")) { - force = 1; - continue; - } - if (!strcmp(arg, "-n")) { - if (i + 1 == argc || *argv[i + 1] == '-') - /* no argument */ - lines = 1; - else - lines = isdigit(*argv[++i]) ? - atoi(argv[i]) : 1; - continue; - } - if (!strcmp(arg, "-m")) { - annotate = 1; - i++; - if (i == argc) - die("option -m needs an argument."); - if (message) - die("only one -F or -m option is allowed."); - strbuf_addstr(&buf, argv[i]); - message = 1; - continue; - } - if (!strcmp(arg, "-F")) { - annotate = 1; - i++; - if (i == argc) - die("option -F needs an argument."); - if (message) - die("only one -F or -m option is allowed."); - - if (!strcmp(argv[i], "-")) { + if (sign) + annotate = 1; + + if (list) + return list_tags(list == no_pattern ? NULL : list, lines); + if (delete) + return for_each_tag_name(argv, delete_tag); + if (verify) + return for_each_tag_name(argv, verify_tag); + + strbuf_init(&buf, 0); + if (msg.given || msgfile) { + if (msg.given && msgfile) + die("only one -F or -m option is allowed."); + annotate = 1; + if (msg.given) + strbuf_addbuf(&buf, &(msg.buf)); + else { + if (!strcmp(msgfile, "-")) { if (strbuf_read(&buf, 0, 1024) < 0) - die("cannot read %s", argv[i]); + die("cannot read %s", msgfile); } else { - if (strbuf_read_file(&buf, argv[i], 1024) < 0) + if (strbuf_read_file(&buf, msgfile, 1024) < 0) die("could not open or read '%s': %s", - argv[i], strerror(errno)); + msgfile, strerror(errno)); } - message = 1; - continue; - } - if (!strcmp(arg, "-u")) { - annotate = 1; - sign = 1; - i++; - if (i == argc) - die("option -u needs an argument."); - if (strlcpy(signingkey, argv[i], sizeof(signingkey)) - >= sizeof(signingkey)) - die("argument to option -u too long"); - continue; } - if (!strcmp(arg, "-l")) - return list_tags(argv[i + 1], lines); - if (!strcmp(arg, "-d")) - return for_each_tag_name(argv + i + 1, delete_tag); - if (!strcmp(arg, "-v")) - return for_each_tag_name(argv + i + 1, verify_tag); - usage(builtin_tag_usage); } - if (i == argc) { + if (argc == 0) { if (annotate) - usage(builtin_tag_usage); + usage_with_options(git_tag_usage, options); return list_tags(NULL, lines); } - tag = argv[i++]; + tag = argv[0]; - object_ref = i < argc ? argv[i] : "HEAD"; - if (i + 1 < argc) + object_ref = argc == 2 ? argv[1] : "HEAD"; + if (argc > 2) die("too many params"); if (get_sha1(object_ref, object)) @@ -419,7 +450,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix) die("tag '%s' already exists", tag); if (annotate) - create_tag(object, tag, &buf, message, sign, object); + create_tag(object, tag, &buf, msg.given || msgfile, + sign, prev, object); lock = lock_any_ref_for_update(ref, prev, 0); if (!lock) @@ -24,6 +24,8 @@ extern int cmd_check_attr(int argc, const char **argv, const char *prefix); extern int cmd_check_ref_format(int argc, const char **argv, const char *prefix); extern int cmd_cherry(int argc, const char **argv, const char *prefix); extern int cmd_cherry_pick(int argc, const char **argv, const char *prefix); +extern int cmd_clean(int argc, const char **argv, const char *prefix); +extern int cmd_commit(int argc, const char **argv, const char *prefix); extern int cmd_commit_tree(int argc, const char **argv, const char *prefix); extern int cmd_count_objects(int argc, const char **argv, const char *prefix); extern int cmd_describe(int argc, const char **argv, const char *prefix); @@ -31,6 +33,7 @@ extern int cmd_diff_files(int argc, const char **argv, const char *prefix); extern int cmd_diff_index(int argc, const char **argv, const char *prefix); extern int cmd_diff(int argc, const char **argv, const char *prefix); extern int cmd_diff_tree(int argc, const char **argv, const char *prefix); +extern int cmd_fast_export(int argc, const char **argv, const char *prefix); extern int cmd_fetch(int argc, const char **argv, const char *prefix); extern int cmd_fetch_pack(int argc, const char **argv, const char *prefix); extern int cmd_fetch__tool(int argc, const char **argv, const char *prefix); @@ -48,9 +51,11 @@ extern int cmd_log(int argc, const char **argv, const char *prefix); extern int cmd_log_reflog(int argc, const char **argv, const char *prefix); extern int cmd_ls_files(int argc, const char **argv, const char *prefix); extern int cmd_ls_tree(int argc, const char **argv, const char *prefix); +extern int cmd_ls_remote(int argc, const char **argv, const char *prefix); extern int cmd_mailinfo(int argc, const char **argv, const char *prefix); extern int cmd_mailsplit(int argc, const char **argv, const char *prefix); extern int cmd_merge_base(int argc, const char **argv, const char *prefix); +extern int cmd_merge_ours(int argc, const char **argv, const char *prefix); extern int cmd_merge_file(int argc, const char **argv, const char *prefix); extern int cmd_mv(int argc, const char **argv, const char *prefix); extern int cmd_name_rev(int argc, const char **argv, const char *prefix); @@ -68,10 +73,11 @@ extern int cmd_rev_list(int argc, const char **argv, const char *prefix); extern int cmd_rev_parse(int argc, const char **argv, const char *prefix); extern int cmd_revert(int argc, const char **argv, const char *prefix); extern int cmd_rm(int argc, const char **argv, const char *prefix); -extern int cmd_runstatus(int argc, const char **argv, const char *prefix); +extern int cmd_send_pack(int argc, const char **argv, const char *prefix); extern int cmd_shortlog(int argc, const char **argv, const char *prefix); extern int cmd_show(int argc, const char **argv, const char *prefix); extern int cmd_show_branch(int argc, const char **argv, const char *prefix); +extern int cmd_status(int argc, const char **argv, const char *prefix); extern int cmd_stripspace(int argc, const char **argv, const char *prefix); extern int cmd_symbolic_ref(int argc, const char **argv, const char *prefix); extern int cmd_tag(int argc, const char **argv, const char *prefix); @@ -6,6 +6,7 @@ #include "revision.h" #include "list-objects.h" #include "run-command.h" +#include "refs.h" static const char bundle_signature[] = "# v2 git bundle\n"; @@ -23,7 +24,8 @@ static void add_to_ref_list(const unsigned char *sha1, const char *name, } /* returns an fd */ -int read_bundle_header(const char *path, struct bundle_header *header) { +int read_bundle_header(const char *path, struct bundle_header *header) +{ char buffer[1024]; int fd; long fpos; @@ -231,11 +233,17 @@ int create_bundle(struct bundle_header *header, const char *path, struct object_array_entry *e = revs.pending.objects + i; unsigned char sha1[20]; char *ref; + const char *display_ref; + int flag; if (e->item->flags & UNINTERESTING) continue; if (dwim_ref(e->name, strlen(e->name), sha1, &ref) != 1) continue; + if (!resolve_ref(e->name, sha1, 1, &flag)) + flag = 0; + display_ref = (flag & REF_ISSYMREF) ? e->name : ref; + /* * Make sure the refs we wrote out is correct; --max-count and * other limiting options could have prevented all the tips @@ -286,7 +294,7 @@ int create_bundle(struct bundle_header *header, const char *path, ref_count++; write_or_die(bundle_fd, sha1_to_hex(e->item->sha1), 40); write_or_die(bundle_fd, " ", 1); - write_or_die(bundle_fd, ref, strlen(ref)); + write_or_die(bundle_fd, display_ref, strlen(display_ref)); write_or_die(bundle_fd, "\n", 1); free(ref); } @@ -7,7 +7,7 @@ #include SHA1_HEADER #include <zlib.h> -#if ZLIB_VERNUM < 0x1200 +#if defined(NO_DEFLATE_BOUND) || ZLIB_VERNUM < 0x1200 #define deflateBound(c,s) ((s) + (((s) + 7) >> 3) + (((s) + 63) >> 6) + 11) #endif @@ -175,8 +175,8 @@ extern struct index_state the_index; #define remove_file_from_cache(path) remove_file_from_index(&the_index, (path)) #define add_file_to_cache(path, verbose) add_file_to_index(&the_index, (path), (verbose)) #define refresh_cache(flags) refresh_index(&the_index, (flags), NULL, NULL) -#define ce_match_stat(ce, st, really) ie_match_stat(&the_index, (ce), (st), (really)) -#define ce_modified(ce, st, really) ie_modified(&the_index, (ce), (st), (really)) +#define ce_match_stat(ce, st, options) ie_match_stat(&the_index, (ce), (st), (options)) +#define ce_modified(ce, st, options) ie_modified(&the_index, (ce), (st), (options)) #endif enum object_type { @@ -192,6 +192,13 @@ enum object_type { OBJ_MAX, }; +static inline enum object_type object_type(unsigned int mode) +{ + return S_ISDIR(mode) ? OBJ_TREE : + S_ISGITLINK(mode) ? OBJ_COMMIT : + OBJ_BLOB; +} + #define GIT_DIR_ENVIRONMENT "GIT_DIR" #define GIT_WORK_TREE_ENVIRONMENT "GIT_WORK_TREE" #define DEFAULT_GIT_DIR_ENVIRONMENT ".git" @@ -222,6 +229,7 @@ extern const char *get_git_work_tree(void); #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); extern const char *prefix_path(const char *prefix, int len, const char *path); @@ -267,8 +275,14 @@ extern int remove_file_from_index(struct index_state *, const char *path); extern int add_file_to_index(struct index_state *, const char *path, int verbose); extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, int refresh); extern int ce_same_name(struct cache_entry *a, struct cache_entry *b); -extern int ie_match_stat(struct index_state *, struct cache_entry *, struct stat *, int); -extern int ie_modified(struct index_state *, struct cache_entry *, struct stat *, int); + +/* do stat comparison even if CE_VALID is true */ +#define CE_MATCH_IGNORE_VALID 01 +/* do not check the contents but report dirty on racily-clean entries */ +#define CE_MATCH_RACY_IS_DIRTY 02 +extern int ie_match_stat(struct index_state *, struct cache_entry *, struct stat *, unsigned int); +extern int ie_modified(struct index_state *, struct cache_entry *, struct stat *, unsigned int); + extern int ce_path_match(const struct cache_entry *ce, const char **pathspec); extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, enum object_type type, const char *path); extern int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object); @@ -283,6 +297,7 @@ extern int refresh_index(struct index_state *, unsigned int flags, const char ** struct lock_file { struct lock_file *next; + int fd; pid_t owner; char on_list; char filename[PATH_MAX]; @@ -408,6 +423,10 @@ extern const char *resolve_ref(const char *path, unsigned char *sha1, int, int * extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref); extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref); +extern int refname_match(const char *abbrev_name, const char *full_name, const char **rules); +extern const char *ref_rev_parse_rules[]; +extern const char *ref_fetch_rules[]; + extern int create_symref(const char *ref, const char *refs_heads_master, const char *logmsg); extern int validate_headref(const char *ref); @@ -434,9 +453,13 @@ void datestamp(char *buf, int bufsize); unsigned long approxidate(const char *); enum date_mode parse_date_format(const char *format); +#define IDENT_WARN_ON_NO_NAME 1 +#define IDENT_ERROR_ON_NO_NAME 2 +#define IDENT_NO_DATE 4 extern const char *git_author_info(int); extern const char *git_committer_info(int); extern const char *fmt_ident(const char *name, const char *email, const char *date_str, int); +extern const char *fmt_name(const char *name, const char *email); struct checkout { const char *base_dir; @@ -492,8 +515,20 @@ struct ref { struct ref *next; unsigned char old_sha1[20]; unsigned char new_sha1[20]; - unsigned char force; - unsigned char merge; + unsigned int force:1, + merge:1, + nonfastforward:1, + deletion:1; + enum { + REF_STATUS_NONE = 0, + REF_STATUS_OK, + REF_STATUS_REJECT_NONFASTFORWARD, + REF_STATUS_REJECT_NODELETE, + REF_STATUS_UPTODATE, + REF_STATUS_REMOTE_REJECT, + REF_STATUS_EXPECTING_REPORT, + } status; + char *remote_status; struct ref *peer_ref; /* when renaming */ char name[FLEX_ARRAY]; /* more */ }; @@ -502,8 +537,10 @@ struct ref { #define REF_HEADS (1u << 1) #define REF_TAGS (1u << 2) +extern struct ref *find_ref_by_name(struct ref *list, const char *name); + #define CONNECT_VERBOSE (1u << 0) -extern struct child_process *git_connect(int fd[2], char *url, const char *prog, int flags); +extern struct child_process *git_connect(int fd[2], const char *url, const char *prog, int flags); extern int finish_connect(struct child_process *conn); extern int path_match(const char *path, int nr, char **match); extern int get_ack(int fd, unsigned char *result_sha1); @@ -549,6 +586,7 @@ extern int git_config_bool(const char *, const char *); extern int git_config_set(const char *, const char *); extern int git_config_set_multivar(const char *, const char *, const char *, int); extern int git_config_rename_section(const char *, const char *); +extern const char *git_etc_gitconfig(void); extern int check_repository_format_version(const char *var, const char *value); #define MAX_GITNAME (1000) @@ -574,6 +612,7 @@ extern int pager_in_use; extern int pager_use_color; extern char *editor_program; +extern char *excludes_file; /* base85 */ int decode_85(char *dst, const char *line, int linelen); @@ -589,13 +628,16 @@ extern void alloc_report(void); /* trace.c */ extern void trace_printf(const char *format, ...); -extern void trace_argv_printf(const char **argv, int count, const char *format, ...); +extern void trace_argv_printf(const char **argv, const char *format, ...); /* convert.c */ /* returns 1 if *dst was used */ extern int convert_to_git(const char *path, const char *src, size_t len, struct strbuf *dst); extern int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst); +/* add */ +void add_files_to_cache(int verbose, const char *prefix, const char **pathspec); + /* diff.c */ extern int diff_auto_refresh_index; @@ -614,4 +656,9 @@ extern unsigned whitespace_rule_cfg; extern unsigned whitespace_rule(const char *); extern unsigned parse_whitespace_rule(const char *); +/* ls-files */ +int pathspec_match(const char **spec, char *matched, const char *filename, int skiplen); +int report_path_error(const char *ps_matched, const char **pathspec, int prefix_offset); +void overlay_tree_on_cache(const char *tree_name, const char *prefix); + #endif /* CACHE_H */ diff --git a/check-racy.c b/check-racy.c index d6a08b4a55..00d92a1663 100644 --- a/check-racy.c +++ b/check-racy.c @@ -18,7 +18,7 @@ int main(int ac, char **av) if (ce_match_stat(ce, &st, 0)) dirty++; - else if (ce_match_stat(ce, &st, 2)) + else if (ce_match_stat(ce, &st, CE_MATCH_RACY_IS_DIRTY)) racy++; else clean++; @@ -116,23 +116,31 @@ bad: die("bad config value '%s' for variable '%s'", value, var); } -int git_config_colorbool(const char *var, const char *value) +int git_config_colorbool(const char *var, const char *value, int stdout_is_tty) { - if (!value) - return 1; - if (!strcasecmp(value, "auto")) { - if (isatty(1) || (pager_in_use && pager_use_color)) { - char *term = getenv("TERM"); - if (term && strcmp(term, "dumb")) - return 1; - } - return 0; + if (value) { + if (!strcasecmp(value, "never")) + return 0; + if (!strcasecmp(value, "always")) + return 1; + if (!strcasecmp(value, "auto")) + goto auto_color; } - if (!strcasecmp(value, "never")) + + /* Missing or explicit false to turn off colorization */ + if (!git_config_bool(var, value)) return 0; - if (!strcasecmp(value, "always")) - return 1; - return git_config_bool(var, value); + + /* any normal truth value defaults to 'auto' */ + auto_color: + if (stdout_is_tty < 0) + stdout_is_tty = isatty(1); + if (stdout_is_tty || (pager_in_use && pager_use_color)) { + char *term = getenv("TERM"); + if (term && strcmp(term, "dumb")) + return 1; + } + return 0; } static int color_vfprintf(FILE *fp, const char *color, const char *fmt, @@ -4,7 +4,7 @@ /* "\033[1;38;5;2xx;48;5;2xxm\0" is 23 bytes */ #define COLOR_MAXLEN 24 -int git_config_colorbool(const char *var, const char *value); +int git_config_colorbool(const char *var, const char *value, int stdout_is_tty); void color_parse(const char *var, const char *value, char *dst); int color_fprintf(FILE *fp, const char *color, const char *fmt, ...); int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...); diff --git a/combine-diff.c b/combine-diff.c index fe5a2a1953..5a658dc0d5 100644 --- a/combine-diff.c +++ b/combine-diff.c @@ -664,7 +664,7 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent, int mode_differs = 0; int i, show_hunks; int working_tree_file = is_null_sha1(elem->sha1); - int abbrev = opt->full_index ? 40 : DEFAULT_ABBREV; + int abbrev = DIFF_OPT_TST(opt, FULL_INDEX) ? 40 : DEFAULT_ABBREV; mmfile_t result_file; context = opt->context; @@ -784,7 +784,7 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent, if (show_hunks || mode_differs || working_tree_file) { const char *abb; - int use_color = opt->color_diff; + int use_color = DIFF_OPT_TST(opt, COLOR_DIFF); const char *c_meta = diff_get_color(use_color, DIFF_METAINFO); const char *c_reset = diff_get_color(use_color, DIFF_RESET); int added = 0; @@ -836,7 +836,7 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent, dump_quoted_path("+++ /dev/", "null", c_meta, c_reset); else dump_quoted_path("+++ b/", elem->path, c_meta, c_reset); - dump_sline(sline, cnt, num_parent, opt->color_diff); + dump_sline(sline, cnt, num_parent, DIFF_OPT_TST(opt, COLOR_DIFF)); } free(result); @@ -929,8 +929,8 @@ void diff_tree_combined(const unsigned char *sha1, diffopts = *opt; diffopts.output_format = DIFF_FORMAT_NO_OUTPUT; - diffopts.recursive = 1; - diffopts.allow_external = 0; + DIFF_OPT_SET(&diffopts, RECURSIVE); + DIFF_OPT_CLR(&diffopts, ALLOW_EXTERNAL); show_log_first = !!rev->loginfo && !rev->no_commit_id; needsep = 0; diff --git a/command-list.txt b/command-list.txt new file mode 100644 index 0000000000..28342da959 --- /dev/null +++ b/command-list.txt @@ -0,0 +1,130 @@ +# List of known git commands. +# command name category [deprecated] [common] +git-add mainporcelain common +git-am mainporcelain +git-annotate ancillaryinterrogators +git-apply plumbingmanipulators +git-archimport foreignscminterface +git-archive mainporcelain +git-bisect mainporcelain common +git-blame ancillaryinterrogators +git-branch mainporcelain common +git-bundle mainporcelain +git-cat-file plumbinginterrogators +git-check-attr purehelpers +git-checkout mainporcelain common +git-checkout-index plumbingmanipulators +git-check-ref-format purehelpers +git-cherry ancillaryinterrogators +git-cherry-pick mainporcelain +git-citool mainporcelain +git-clean mainporcelain +git-clone mainporcelain common +git-commit mainporcelain common +git-commit-tree plumbingmanipulators +git-config ancillarymanipulators +git-count-objects ancillaryinterrogators +git-cvsexportcommit foreignscminterface +git-cvsimport foreignscminterface +git-cvsserver foreignscminterface +git-daemon synchingrepositories +git-describe mainporcelain +git-diff mainporcelain common +git-diff-files plumbinginterrogators +git-diff-index plumbinginterrogators +git-diff-tree plumbinginterrogators +git-fast-export ancillarymanipulators +git-fast-import ancillarymanipulators +git-fetch mainporcelain common +git-fetch-pack synchingrepositories +git-filter-branch ancillarymanipulators +git-fmt-merge-msg purehelpers +git-for-each-ref plumbinginterrogators +git-format-patch mainporcelain +git-fsck ancillaryinterrogators +git-gc mainporcelain +git-get-tar-commit-id ancillaryinterrogators +git-grep mainporcelain common +git-gui mainporcelain +git-hash-object plumbingmanipulators +git-help ancillaryinterrogators +git-http-fetch synchelpers +git-http-push synchelpers +git-imap-send foreignscminterface +git-index-pack plumbingmanipulators +git-init mainporcelain common +git-instaweb ancillaryinterrogators +gitk mainporcelain +git-log mainporcelain common +git-lost-found ancillarymanipulators deprecated +git-ls-files plumbinginterrogators +git-ls-remote plumbinginterrogators +git-ls-tree plumbinginterrogators +git-mailinfo purehelpers +git-mailsplit purehelpers +git-merge mainporcelain common +git-merge-base plumbinginterrogators +git-merge-file plumbingmanipulators +git-merge-index plumbingmanipulators +git-merge-one-file purehelpers +git-mergetool ancillarymanipulators +git-merge-tree ancillaryinterrogators +git-mktag plumbingmanipulators +git-mktree plumbingmanipulators +git-mv mainporcelain common +git-name-rev plumbinginterrogators +git-pack-objects plumbingmanipulators +git-pack-redundant plumbinginterrogators +git-pack-refs ancillarymanipulators +git-parse-remote synchelpers +git-patch-id purehelpers +git-peek-remote purehelpers deprecated +git-prune ancillarymanipulators +git-prune-packed plumbingmanipulators +git-pull mainporcelain common +git-push mainporcelain common +git-quiltimport foreignscminterface +git-read-tree plumbingmanipulators +git-rebase mainporcelain common +git-receive-pack synchelpers +git-reflog ancillarymanipulators +git-relink ancillarymanipulators +git-remote ancillarymanipulators +git-repack ancillarymanipulators +git-request-pull foreignscminterface +git-rerere ancillaryinterrogators +git-reset mainporcelain common +git-revert mainporcelain +git-rev-list plumbinginterrogators +git-rev-parse ancillaryinterrogators +git-rm mainporcelain common +git-runstatus ancillaryinterrogators +git-send-email foreignscminterface +git-send-pack synchingrepositories +git-shell synchelpers +git-shortlog mainporcelain +git-show mainporcelain common +git-show-branch ancillaryinterrogators +git-show-index plumbinginterrogators +git-show-ref plumbinginterrogators +git-sh-setup purehelpers +git-stash mainporcelain +git-status mainporcelain common +git-stripspace purehelpers +git-submodule mainporcelain +git-svn foreignscminterface +git-symbolic-ref plumbingmanipulators +git-tag mainporcelain common +git-tar-tree plumbinginterrogators deprecated +git-unpack-file plumbinginterrogators +git-unpack-objects plumbingmanipulators +git-update-index plumbingmanipulators +git-update-ref plumbingmanipulators +git-update-server-info synchingrepositories +git-upload-archive synchelpers +git-upload-pack synchelpers +git-var plumbinginterrogators +git-verify-pack plumbinginterrogators +git-verify-tag ancillaryinterrogators +git-whatchanged ancillaryinterrogators +git-write-tree plumbingmanipulators @@ -3,70 +3,13 @@ #include "commit.h" #include "pkt-line.h" #include "utf8.h" -#include "interpolate.h" #include "diff.h" #include "revision.h" int save_commit_buffer = 1; -struct sort_node -{ - /* - * the number of children of the associated commit - * that also occur in the list being sorted. - */ - unsigned int indegree; - - /* - * reference to original list item that we will re-use - * on output. - */ - struct commit_list * list_item; - -}; - const char *commit_type = "commit"; -static struct cmt_fmt_map { - const char *n; - size_t cmp_len; - enum cmit_fmt v; -} cmt_fmts[] = { - { "raw", 1, CMIT_FMT_RAW }, - { "medium", 1, CMIT_FMT_MEDIUM }, - { "short", 1, CMIT_FMT_SHORT }, - { "email", 1, CMIT_FMT_EMAIL }, - { "full", 5, CMIT_FMT_FULL }, - { "fuller", 5, CMIT_FMT_FULLER }, - { "oneline", 1, CMIT_FMT_ONELINE }, - { "format:", 7, CMIT_FMT_USERFORMAT}, -}; - -static char *user_format; - -enum cmit_fmt get_commit_format(const char *arg) -{ - int i; - - if (!arg || !*arg) - return CMIT_FMT_DEFAULT; - if (*arg == '=') - arg++; - if (!prefixcmp(arg, "format:")) { - if (user_format) - free(user_format); - user_format = xstrdup(arg + 7); - return CMIT_FMT_USERFORMAT; - } - for (i = 0; i < ARRAY_SIZE(cmt_fmts); i++) { - if (!strncmp(arg, cmt_fmts[i].n, cmt_fmts[i].cmp_len) && - !strncmp(arg, cmt_fmts[i].n, strlen(arg))) - return cmt_fmts[i].v; - } - - die("invalid --pretty format: %s", arg); -} - static struct commit *check_commit(struct object *obj, const unsigned char *sha1, int quiet) @@ -460,684 +403,6 @@ void clear_commit_marks(struct commit *commit, unsigned int mark) } } -/* - * Generic support for pretty-printing the header - */ -static int get_one_line(const char *msg) -{ - int ret = 0; - - for (;;) { - char c = *msg++; - if (!c) - break; - ret++; - if (c == '\n') - break; - } - return ret; -} - -/* High bit set, or ISO-2022-INT */ -static int non_ascii(int ch) -{ - ch = (ch & 0xff); - return ((ch & 0x80) || (ch == 0x1b)); -} - -static int is_rfc2047_special(char ch) -{ - return (non_ascii(ch) || (ch == '=') || (ch == '?') || (ch == '_')); -} - -static void add_rfc2047(struct strbuf *sb, const char *line, int len, - const char *encoding) -{ - int i, last; - - for (i = 0; i < len; i++) { - int ch = line[i]; - if (non_ascii(ch)) - goto needquote; - if ((i + 1 < len) && (ch == '=' && line[i+1] == '?')) - goto needquote; - } - strbuf_add(sb, line, len); - return; - -needquote: - strbuf_grow(sb, len * 3 + strlen(encoding) + 100); - strbuf_addf(sb, "=?%s?q?", encoding); - for (i = last = 0; i < len; i++) { - unsigned ch = line[i] & 0xFF; - /* - * We encode ' ' using '=20' even though rfc2047 - * allows using '_' for readability. Unfortunately, - * many programs do not understand this and just - * leave the underscore in place. - */ - if (is_rfc2047_special(ch) || ch == ' ') { - strbuf_add(sb, line + last, i - last); - strbuf_addf(sb, "=%02X", ch); - last = i + 1; - } - } - strbuf_add(sb, line + last, len - last); - strbuf_addstr(sb, "?="); -} - -static void add_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb, - const char *line, enum date_mode dmode, - const char *encoding) -{ - char *date; - int namelen; - unsigned long time; - int tz; - const char *filler = " "; - - if (fmt == CMIT_FMT_ONELINE) - return; - date = strchr(line, '>'); - if (!date) - return; - namelen = ++date - line; - time = strtoul(date, &date, 10); - tz = strtol(date, NULL, 10); - - if (fmt == CMIT_FMT_EMAIL) { - char *name_tail = strchr(line, '<'); - int display_name_length; - if (!name_tail) - return; - while (line < name_tail && isspace(name_tail[-1])) - name_tail--; - display_name_length = name_tail - line; - filler = ""; - strbuf_addstr(sb, "From: "); - add_rfc2047(sb, line, display_name_length, encoding); - strbuf_add(sb, name_tail, namelen - display_name_length); - strbuf_addch(sb, '\n'); - } else { - strbuf_addf(sb, "%s: %.*s%.*s\n", what, - (fmt == CMIT_FMT_FULLER) ? 4 : 0, - filler, namelen, line); - } - switch (fmt) { - case CMIT_FMT_MEDIUM: - strbuf_addf(sb, "Date: %s\n", show_date(time, tz, dmode)); - break; - case CMIT_FMT_EMAIL: - strbuf_addf(sb, "Date: %s\n", show_date(time, tz, DATE_RFC2822)); - break; - case CMIT_FMT_FULLER: - strbuf_addf(sb, "%sDate: %s\n", what, show_date(time, tz, dmode)); - break; - default: - /* notin' */ - break; - } -} - -static int is_empty_line(const char *line, int *len_p) -{ - int len = *len_p; - while (len && isspace(line[len-1])) - len--; - *len_p = len; - return !len; -} - -static void add_merge_info(enum cmit_fmt fmt, struct strbuf *sb, - const struct commit *commit, int abbrev) -{ - struct commit_list *parent = commit->parents; - - if ((fmt == CMIT_FMT_ONELINE) || (fmt == CMIT_FMT_EMAIL) || - !parent || !parent->next) - return; - - strbuf_addstr(sb, "Merge:"); - - while (parent) { - struct commit *p = parent->item; - const char *hex = NULL; - const char *dots; - if (abbrev) - hex = find_unique_abbrev(p->object.sha1, abbrev); - if (!hex) - hex = sha1_to_hex(p->object.sha1); - dots = (abbrev && strlen(hex) != 40) ? "..." : ""; - parent = parent->next; - - strbuf_addf(sb, " %s%s", hex, dots); - } - strbuf_addch(sb, '\n'); -} - -static char *get_header(const struct commit *commit, const char *key) -{ - int key_len = strlen(key); - const char *line = commit->buffer; - - for (;;) { - const char *eol = strchr(line, '\n'), *next; - - if (line == eol) - return NULL; - if (!eol) { - eol = line + strlen(line); - next = NULL; - } else - next = eol + 1; - if (eol - line > key_len && - !strncmp(line, key, key_len) && - line[key_len] == ' ') { - return xmemdupz(line + key_len + 1, eol - line - key_len - 1); - } - line = next; - } -} - -static char *replace_encoding_header(char *buf, const char *encoding) -{ - struct strbuf tmp; - size_t start, len; - char *cp = buf; - - /* guess if there is an encoding header before a \n\n */ - while (strncmp(cp, "encoding ", strlen("encoding "))) { - cp = strchr(cp, '\n'); - if (!cp || *++cp == '\n') - return buf; - } - start = cp - buf; - cp = strchr(cp, '\n'); - if (!cp) - return buf; /* should not happen but be defensive */ - len = cp + 1 - (buf + start); - - strbuf_init(&tmp, 0); - strbuf_attach(&tmp, buf, strlen(buf), strlen(buf) + 1); - if (is_encoding_utf8(encoding)) { - /* we have re-coded to UTF-8; drop the header */ - strbuf_remove(&tmp, start, len); - } else { - /* just replaces XXXX in 'encoding XXXX\n' */ - strbuf_splice(&tmp, start + strlen("encoding "), - len - strlen("encoding \n"), - encoding, strlen(encoding)); - } - return strbuf_detach(&tmp, NULL); -} - -static char *logmsg_reencode(const struct commit *commit, - const char *output_encoding) -{ - static const char *utf8 = "utf-8"; - const char *use_encoding; - char *encoding; - char *out; - - if (!*output_encoding) - return NULL; - encoding = get_header(commit, "encoding"); - use_encoding = encoding ? encoding : utf8; - if (!strcmp(use_encoding, output_encoding)) - if (encoding) /* we'll strip encoding header later */ - out = xstrdup(commit->buffer); - else - return NULL; /* nothing to do */ - else - out = reencode_string(commit->buffer, - output_encoding, use_encoding); - if (out) - out = replace_encoding_header(out, output_encoding); - - free(encoding); - return out; -} - -static void fill_person(struct interp *table, const char *msg, int len) -{ - int start, end, tz = 0; - unsigned long date; - char *ep; - - /* parse name */ - for (end = 0; end < len && msg[end] != '<'; end++) - ; /* do nothing */ - start = end + 1; - while (end > 0 && isspace(msg[end - 1])) - end--; - table[0].value = xmemdupz(msg, end); - - if (start >= len) - return; - - /* parse email */ - for (end = start + 1; end < len && msg[end] != '>'; end++) - ; /* do nothing */ - - if (end >= len) - return; - - table[1].value = xmemdupz(msg + start, end - start); - - /* parse date */ - for (start = end + 1; start < len && isspace(msg[start]); start++) - ; /* do nothing */ - if (start >= len) - return; - date = strtoul(msg + start, &ep, 10); - if (msg + start == ep) - return; - - table[5].value = xmemdupz(msg + start, ep - (msg + start)); - - /* parse tz */ - for (start = ep - msg + 1; start < len && isspace(msg[start]); start++) - ; /* do nothing */ - if (start + 1 < len) { - tz = strtoul(msg + start + 1, NULL, 10); - if (msg[start] == '-') - tz = -tz; - } - - interp_set_entry(table, 2, show_date(date, tz, DATE_NORMAL)); - interp_set_entry(table, 3, show_date(date, tz, DATE_RFC2822)); - interp_set_entry(table, 4, show_date(date, tz, DATE_RELATIVE)); - interp_set_entry(table, 6, show_date(date, tz, DATE_ISO8601)); -} - -void format_commit_message(const struct commit *commit, - const void *format, struct strbuf *sb) -{ - struct interp table[] = { - { "%H" }, /* commit hash */ - { "%h" }, /* abbreviated commit hash */ - { "%T" }, /* tree hash */ - { "%t" }, /* abbreviated tree hash */ - { "%P" }, /* parent hashes */ - { "%p" }, /* abbreviated parent hashes */ - { "%an" }, /* author name */ - { "%ae" }, /* author email */ - { "%ad" }, /* author date */ - { "%aD" }, /* author date, RFC2822 style */ - { "%ar" }, /* author date, relative */ - { "%at" }, /* author date, UNIX timestamp */ - { "%ai" }, /* author date, ISO 8601 */ - { "%cn" }, /* committer name */ - { "%ce" }, /* committer email */ - { "%cd" }, /* committer date */ - { "%cD" }, /* committer date, RFC2822 style */ - { "%cr" }, /* committer date, relative */ - { "%ct" }, /* committer date, UNIX timestamp */ - { "%ci" }, /* committer date, ISO 8601 */ - { "%e" }, /* encoding */ - { "%s" }, /* subject */ - { "%b" }, /* body */ - { "%Cred" }, /* red */ - { "%Cgreen" }, /* green */ - { "%Cblue" }, /* blue */ - { "%Creset" }, /* reset color */ - { "%n" }, /* newline */ - { "%m" }, /* left/right/bottom */ - }; - enum interp_index { - IHASH = 0, IHASH_ABBREV, - ITREE, ITREE_ABBREV, - IPARENTS, IPARENTS_ABBREV, - IAUTHOR_NAME, IAUTHOR_EMAIL, - IAUTHOR_DATE, IAUTHOR_DATE_RFC2822, IAUTHOR_DATE_RELATIVE, - IAUTHOR_TIMESTAMP, IAUTHOR_ISO8601, - ICOMMITTER_NAME, ICOMMITTER_EMAIL, - ICOMMITTER_DATE, ICOMMITTER_DATE_RFC2822, - ICOMMITTER_DATE_RELATIVE, ICOMMITTER_TIMESTAMP, - ICOMMITTER_ISO8601, - IENCODING, - ISUBJECT, - IBODY, - IRED, IGREEN, IBLUE, IRESET_COLOR, - INEWLINE, - ILEFT_RIGHT, - }; - struct commit_list *p; - char parents[1024]; - unsigned long len; - int i; - enum { HEADER, SUBJECT, BODY } state; - const char *msg = commit->buffer; - - if (ILEFT_RIGHT + 1 != ARRAY_SIZE(table)) - die("invalid interp table!"); - - /* these are independent of the commit */ - interp_set_entry(table, IRED, "\033[31m"); - interp_set_entry(table, IGREEN, "\033[32m"); - interp_set_entry(table, IBLUE, "\033[34m"); - interp_set_entry(table, IRESET_COLOR, "\033[m"); - interp_set_entry(table, INEWLINE, "\n"); - - /* these depend on the commit */ - if (!commit->object.parsed) - parse_object(commit->object.sha1); - interp_set_entry(table, IHASH, sha1_to_hex(commit->object.sha1)); - interp_set_entry(table, IHASH_ABBREV, - find_unique_abbrev(commit->object.sha1, - DEFAULT_ABBREV)); - interp_set_entry(table, ITREE, sha1_to_hex(commit->tree->object.sha1)); - interp_set_entry(table, ITREE_ABBREV, - find_unique_abbrev(commit->tree->object.sha1, - DEFAULT_ABBREV)); - interp_set_entry(table, ILEFT_RIGHT, - (commit->object.flags & BOUNDARY) - ? "-" - : (commit->object.flags & SYMMETRIC_LEFT) - ? "<" - : ">"); - - parents[1] = 0; - for (i = 0, p = commit->parents; - p && i < sizeof(parents) - 1; - p = p->next) - i += snprintf(parents + i, sizeof(parents) - i - 1, " %s", - sha1_to_hex(p->item->object.sha1)); - interp_set_entry(table, IPARENTS, parents + 1); - - parents[1] = 0; - for (i = 0, p = commit->parents; - p && i < sizeof(parents) - 1; - p = p->next) - i += snprintf(parents + i, sizeof(parents) - i - 1, " %s", - find_unique_abbrev(p->item->object.sha1, - DEFAULT_ABBREV)); - interp_set_entry(table, IPARENTS_ABBREV, parents + 1); - - for (i = 0, state = HEADER; msg[i] && state < BODY; i++) { - int eol; - for (eol = i; msg[eol] && msg[eol] != '\n'; eol++) - ; /* do nothing */ - - if (state == SUBJECT) { - table[ISUBJECT].value = xmemdupz(msg + i, eol - i); - i = eol; - } - if (i == eol) { - state++; - /* strip empty lines */ - while (msg[eol + 1] == '\n') - eol++; - } else if (!prefixcmp(msg + i, "author ")) - fill_person(table + IAUTHOR_NAME, - msg + i + 7, eol - i - 7); - else if (!prefixcmp(msg + i, "committer ")) - fill_person(table + ICOMMITTER_NAME, - msg + i + 10, eol - i - 10); - else if (!prefixcmp(msg + i, "encoding ")) - table[IENCODING].value = - xmemdupz(msg + i + 9, eol - i - 9); - i = eol; - } - if (msg[i]) - table[IBODY].value = xstrdup(msg + i); - - len = interpolate(sb->buf + sb->len, strbuf_avail(sb), - format, table, ARRAY_SIZE(table)); - if (len > strbuf_avail(sb)) { - strbuf_grow(sb, len); - interpolate(sb->buf + sb->len, strbuf_avail(sb) + 1, - format, table, ARRAY_SIZE(table)); - } - strbuf_setlen(sb, sb->len + len); - interp_clear_table(table, ARRAY_SIZE(table)); -} - -static void pp_header(enum cmit_fmt fmt, - int abbrev, - enum date_mode dmode, - const char *encoding, - const struct commit *commit, - const char **msg_p, - struct strbuf *sb) -{ - int parents_shown = 0; - - for (;;) { - const char *line = *msg_p; - int linelen = get_one_line(*msg_p); - - if (!linelen) - return; - *msg_p += linelen; - - if (linelen == 1) - /* End of header */ - return; - - if (fmt == CMIT_FMT_RAW) { - strbuf_add(sb, line, linelen); - continue; - } - - if (!memcmp(line, "parent ", 7)) { - if (linelen != 48) - die("bad parent line in commit"); - continue; - } - - if (!parents_shown) { - struct commit_list *parent; - int num; - for (parent = commit->parents, num = 0; - parent; - parent = parent->next, num++) - ; - /* with enough slop */ - strbuf_grow(sb, num * 50 + 20); - add_merge_info(fmt, sb, commit, abbrev); - parents_shown = 1; - } - - /* - * MEDIUM == DEFAULT shows only author with dates. - * FULL shows both authors but not dates. - * FULLER shows both authors and dates. - */ - if (!memcmp(line, "author ", 7)) { - strbuf_grow(sb, linelen + 80); - add_user_info("Author", fmt, sb, line + 7, dmode, encoding); - } - if (!memcmp(line, "committer ", 10) && - (fmt == CMIT_FMT_FULL || fmt == CMIT_FMT_FULLER)) { - strbuf_grow(sb, linelen + 80); - add_user_info("Commit", fmt, sb, line + 10, dmode, encoding); - } - } -} - -static void pp_title_line(enum cmit_fmt fmt, - const char **msg_p, - struct strbuf *sb, - const char *subject, - const char *after_subject, - const char *encoding, - int plain_non_ascii) -{ - struct strbuf title; - - strbuf_init(&title, 80); - - for (;;) { - const char *line = *msg_p; - int linelen = get_one_line(line); - - *msg_p += linelen; - if (!linelen || is_empty_line(line, &linelen)) - break; - - strbuf_grow(&title, linelen + 2); - if (title.len) { - if (fmt == CMIT_FMT_EMAIL) { - strbuf_addch(&title, '\n'); - } - strbuf_addch(&title, ' '); - } - strbuf_add(&title, line, linelen); - } - - strbuf_grow(sb, title.len + 1024); - if (subject) { - strbuf_addstr(sb, subject); - add_rfc2047(sb, title.buf, title.len, encoding); - } else { - strbuf_addbuf(sb, &title); - } - strbuf_addch(sb, '\n'); - - if (plain_non_ascii) { - const char *header_fmt = - "MIME-Version: 1.0\n" - "Content-Type: text/plain; charset=%s\n" - "Content-Transfer-Encoding: 8bit\n"; - strbuf_addf(sb, header_fmt, encoding); - } - if (after_subject) { - strbuf_addstr(sb, after_subject); - } - if (fmt == CMIT_FMT_EMAIL) { - strbuf_addch(sb, '\n'); - } - strbuf_release(&title); -} - -static void pp_remainder(enum cmit_fmt fmt, - const char **msg_p, - struct strbuf *sb, - int indent) -{ - int first = 1; - for (;;) { - const char *line = *msg_p; - int linelen = get_one_line(line); - *msg_p += linelen; - - if (!linelen) - break; - - if (is_empty_line(line, &linelen)) { - if (first) - continue; - if (fmt == CMIT_FMT_SHORT) - break; - } - first = 0; - - strbuf_grow(sb, linelen + indent + 20); - if (indent) { - memset(sb->buf + sb->len, ' ', indent); - strbuf_setlen(sb, sb->len + indent); - } - strbuf_add(sb, line, linelen); - strbuf_addch(sb, '\n'); - } -} - -void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, - struct strbuf *sb, int abbrev, - const char *subject, const char *after_subject, - enum date_mode dmode) -{ - unsigned long beginning_of_body; - int indent = 4; - const char *msg = commit->buffer; - int plain_non_ascii = 0; - char *reencoded; - const char *encoding; - - if (fmt == CMIT_FMT_USERFORMAT) { - format_commit_message(commit, user_format, sb); - return; - } - - encoding = (git_log_output_encoding - ? git_log_output_encoding - : git_commit_encoding); - if (!encoding) - encoding = "utf-8"; - reencoded = logmsg_reencode(commit, encoding); - if (reencoded) { - msg = reencoded; - } - - if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL) - indent = 0; - - /* After-subject is used to pass in Content-Type: multipart - * MIME header; in that case we do not have to do the - * plaintext content type even if the commit message has - * non 7-bit ASCII character. Otherwise, check if we need - * to say this is not a 7-bit ASCII. - */ - if (fmt == CMIT_FMT_EMAIL && !after_subject) { - int i, ch, in_body; - - for (in_body = i = 0; (ch = msg[i]); i++) { - if (!in_body) { - /* author could be non 7-bit ASCII but - * the log may be so; skip over the - * header part first. - */ - if (ch == '\n' && msg[i+1] == '\n') - in_body = 1; - } - else if (non_ascii(ch)) { - plain_non_ascii = 1; - break; - } - } - } - - pp_header(fmt, abbrev, dmode, encoding, commit, &msg, sb); - if (fmt != CMIT_FMT_ONELINE && !subject) { - strbuf_addch(sb, '\n'); - } - - /* Skip excess blank lines at the beginning of body, if any... */ - for (;;) { - int linelen = get_one_line(msg); - int ll = linelen; - if (!linelen) - break; - if (!is_empty_line(msg, &ll)) - break; - msg += linelen; - } - - /* These formats treat the title line specially. */ - if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL) - pp_title_line(fmt, &msg, sb, subject, - after_subject, encoding, plain_non_ascii); - - beginning_of_body = sb->len; - if (fmt != CMIT_FMT_ONELINE) - pp_remainder(fmt, &msg, sb, indent); - strbuf_rtrim(sb); - - /* Make sure there is an EOLN for the non-oneline case */ - if (fmt != CMIT_FMT_ONELINE) - strbuf_addch(sb, '\n'); - - /* - * The caller may append additional body text in e-mail - * format. Make sure we did not strip the blank line - * between the header and the body. - */ - if (fmt == CMIT_FMT_EMAIL && sb->len <= beginning_of_body) - strbuf_addch(sb, '\n'); - free(reencoded); -} - struct commit *pop_commit(struct commit_list **stack) { struct commit_list *top = *stack; @@ -1150,69 +415,38 @@ struct commit *pop_commit(struct commit_list **stack) return item; } -void topo_sort_default_setter(struct commit *c, void *data) -{ - c->util = data; -} - -void *topo_sort_default_getter(struct commit *c) -{ - return c->util; -} - /* * Performs an in-place topological sort on the list supplied. */ void sort_in_topological_order(struct commit_list ** list, int lifo) { - sort_in_topological_order_fn(list, lifo, topo_sort_default_setter, - topo_sort_default_getter); -} - -void sort_in_topological_order_fn(struct commit_list ** list, int lifo, - topo_sort_set_fn_t setter, - topo_sort_get_fn_t getter) -{ - struct commit_list * next = *list; - struct commit_list * work = NULL, **insert; - struct commit_list ** pptr = list; - struct sort_node * nodes; - struct sort_node * next_nodes; - int count = 0; - - /* determine the size of the list */ - while (next) { - next = next->next; - count++; - } + struct commit_list *next, *orig = *list; + struct commit_list *work, **insert; + struct commit_list **pptr; - if (!count) + if (!orig) return; - /* allocate an array to help sort the list */ - nodes = xcalloc(count, sizeof(*nodes)); - /* link the list to the array */ - next_nodes = nodes; - next=*list; - while (next) { - next_nodes->list_item = next; - setter(next->item, next_nodes); - next_nodes++; - next = next->next; + *list = NULL; + + /* Mark them and clear the indegree */ + for (next = orig; next; next = next->next) { + struct commit *commit = next->item; + commit->object.flags |= TOPOSORT; + commit->indegree = 0; } + /* update the indegree */ - next=*list; - while (next) { + for (next = orig; next; next = next->next) { struct commit_list * parents = next->item->parents; while (parents) { - struct commit * parent=parents->item; - struct sort_node * pn = (struct sort_node *) getter(parent); + struct commit *parent = parents->item; - if (pn) - pn->indegree++; - parents=parents->next; + if (parent->object.flags & TOPOSORT) + parent->indegree++; + parents = parents->next; } - next=next->next; } + /* * find the tips * @@ -1220,55 +454,56 @@ void sort_in_topological_order_fn(struct commit_list ** list, int lifo, * * the tips serve as a starting set for the work queue. */ - next=*list; + work = NULL; insert = &work; - while (next) { - struct sort_node * node = (struct sort_node *) getter(next->item); + for (next = orig; next; next = next->next) { + struct commit *commit = next->item; - if (node->indegree == 0) { - insert = &commit_list_insert(next->item, insert)->next; - } - next=next->next; + if (!commit->indegree) + insert = &commit_list_insert(commit, insert)->next; } /* process the list in topological order */ if (!lifo) sort_by_date(&work); + + pptr = list; + *list = NULL; while (work) { - struct commit * work_item = pop_commit(&work); - struct sort_node * work_node = (struct sort_node *) getter(work_item); - struct commit_list * parents = work_item->parents; + struct commit *commit; + struct commit_list *parents, *work_item; - while (parents) { - struct commit * parent=parents->item; - struct sort_node * pn = (struct sort_node *) getter(parent); - - if (pn) { - /* - * parents are only enqueued for emission - * when all their children have been emitted thereby - * guaranteeing topological order. - */ - pn->indegree--; - if (!pn->indegree) { - if (!lifo) - insert_by_date(parent, &work); - else - commit_list_insert(parent, &work); - } + work_item = work; + work = work_item->next; + work_item->next = NULL; + + commit = work_item->item; + for (parents = commit->parents; parents ; parents = parents->next) { + struct commit *parent=parents->item; + + if (!(parent->object.flags & TOPOSORT)) + continue; + + /* + * parents are only enqueued for emission + * when all their children have been emitted thereby + * guaranteeing topological order. + */ + if (!--parent->indegree) { + if (!lifo) + insert_by_date(parent, &work); + else + commit_list_insert(parent, &work); } - parents=parents->next; } /* * work_item is a commit all of whose children * have already been emitted. we can emit it now. */ - *pptr = work_node->list_item; - pptr = &(*pptr)->next; - *pptr = NULL; - setter(work_item, NULL); + commit->object.flags &= ~TOPOSORT; + *pptr = work_item; + pptr = &work_item->next; } - free(nodes); } /* merge-base stuff */ @@ -14,6 +14,7 @@ struct commit_list { struct commit { struct object object; void *util; + unsigned int indegree; unsigned long date; struct commit_list *parents; struct tree *tree; @@ -61,13 +62,15 @@ enum cmit_fmt { CMIT_FMT_UNSPECIFIED, }; +extern int non_ascii(int); extern enum cmit_fmt get_commit_format(const char *arg); extern void format_commit_message(const struct commit *commit, const void *format, struct strbuf *sb); extern void pretty_print_commit(enum cmit_fmt fmt, const struct commit*, struct strbuf *, int abbrev, const char *subject, - const char *after_subject, enum date_mode); + const char *after_subject, enum date_mode, + int non_ascii_present); /** Removes the first commit from a list sorted by date, and adds all * of its parents. @@ -82,31 +85,12 @@ void clear_commit_marks(struct commit *commit, unsigned int mark); /* * Performs an in-place topological sort of list supplied. * - * Pre-conditions for sort_in_topological_order: - * all commits in input list and all parents of those - * commits must have object.util == NULL - * - * Pre-conditions for sort_in_topological_order_fn: - * all commits in input list and all parents of those - * commits must have getter(commit) == NULL - * - * Post-conditions: * invariant of resulting list is: * a reachable from b => ord(b) < ord(a) * in addition, when lifo == 0, commits on parallel tracks are * sorted in the dates order. */ - -typedef void (*topo_sort_set_fn_t)(struct commit*, void *data); -typedef void* (*topo_sort_get_fn_t)(struct commit*); - -void topo_sort_default_setter(struct commit *c, void *data); -void *topo_sort_default_getter(struct commit *c); - void sort_in_topological_order(struct commit_list ** list, int lifo); -void sort_in_topological_order_fn(struct commit_list ** list, int lifo, - topo_sort_set_fn_t setter, - topo_sort_get_fn_t getter); struct commit_graft { unsigned char sha1[20]; @@ -129,8 +113,12 @@ extern struct commit_list *get_shallow_commits(struct object_array *heads, int in_merge_bases(struct commit *, struct commit **, int); -extern int interactive_add(void); -extern void add_files_to_cache(int verbose, const char *prefix, const char **files); +extern int interactive_add(int argc, const char **argv, const char *prefix); extern int rerere(void); +static inline int single_parent(struct commit *commit) +{ + return commit->parents && !commit->parents->next; +} + #endif /* COMMIT_H */ diff --git a/compat/inet_ntop.c b/compat/inet_ntop.c index 4d7ab9d975..f44498258d 100644 --- a/compat/inet_ntop.c +++ b/compat/inet_ntop.c @@ -18,7 +18,6 @@ #include <errno.h> #include <sys/types.h> #include <sys/socket.h> -#include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdio.h> diff --git a/compat/inet_pton.c b/compat/inet_pton.c index 5704e0d2b6..4078fc0877 100644 --- a/compat/inet_pton.c +++ b/compat/inet_pton.c @@ -18,7 +18,6 @@ #include <errno.h> #include <sys/types.h> #include <sys/socket.h> -#include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdio.h> @@ -6,6 +6,7 @@ * */ #include "cache.h" +#include "exec_cmd.h" #define MAXNAME (256) @@ -431,6 +432,13 @@ int git_default_config(const char *var, const char *value) return 0; } + if (!strcmp(var, "core.excludesfile")) { + if (!value) + die("core.excludesfile without value"); + excludes_file = xstrdup(value); + return 0; + } + if (!strcmp(var, "core.whitespace")) { whitespace_rule_cfg = parse_whitespace_rule(value); return 0; @@ -457,6 +465,21 @@ int git_config_from_file(config_fn_t fn, const char *filename) return ret; } +const char *git_etc_gitconfig(void) +{ + static const char *system_wide; + if (!system_wide) { + system_wide = ETC_GITCONFIG; + if (!is_absolute_path(system_wide)) { + /* interpret path relative to exec-dir */ + const char *exec_path = git_exec_path(); + system_wide = prefix_path(exec_path, strlen(exec_path), + system_wide); + } + } + return system_wide; +} + int git_config(config_fn_t fn) { int ret = 0; @@ -469,8 +492,8 @@ int git_config(config_fn_t fn) * config file otherwise. */ filename = getenv(CONFIG_ENVIRONMENT); if (!filename) { - if (!access(ETC_GITCONFIG, R_OK)) - ret += git_config_from_file(fn, ETC_GITCONFIG); + if (!access(git_etc_gitconfig(), R_OK)) + ret += git_config_from_file(fn, git_etc_gitconfig()); home = getenv("HOME"); filename = getenv(CONFIG_LOCAL_ENVIRONMENT); if (!filename) @@ -628,13 +651,19 @@ static int store_write_pair(int fd, const char* key, const char* value) int length = strlen(key+store.baselen+1); int quote = 0; - /* Check to see if the value needs to be quoted. */ + /* + * Check to see if the value needs to be surrounded with a dq pair. + * Note that problematic characters are always backslash-quoted; this + * check is about not losing leading or trailing SP and strings that + * follow beginning-of-comment characters (i.e. ';' and '#') by the + * configuration parser. + */ if (value[0] == ' ') quote = 1; for (i = 0; value[i]; i++) if (value[i] == ';' || value[i] == '#') quote = 1; - if (value[i-1] == ' ') + if (i && value[i-1] == ' ') quote = 1; if (write_in_full(fd, "\t", 1) != 1 || diff --git a/config.mak.in b/config.mak.in index a3032e389f..7d5df9bf3c 100644 --- a/config.mak.in +++ b/config.mak.in @@ -35,6 +35,11 @@ NO_SOCKADDR_STORAGE=@NO_SOCKADDR_STORAGE@ NO_IPV6=@NO_IPV6@ NO_C99_FORMAT=@NO_C99_FORMAT@ NO_STRCASESTR=@NO_STRCASESTR@ +NO_MEMMEM=@NO_MEMMEM@ NO_STRLCPY=@NO_STRLCPY@ +NO_STRTOUMAX=@NO_STRTOUMAX@ NO_SETENV=@NO_SETENV@ +NO_MKDTEMP=@NO_MKDTEMP@ NO_ICONV=@NO_ICONV@ +OLD_ICONV=@OLD_ICONV@ +NO_DEFLATE_BOUND=@NO_DEFLATE_BOUND@ diff --git a/configure.ac b/configure.ac index ed7cc895d2..dd4b4eb982 100644 --- a/configure.ac +++ b/configure.ac @@ -73,7 +73,7 @@ fi \ AC_ARG_WITH([lib], [AS_HELP_STRING([--with-lib=ARG], [ARG specifies alternative name for lib directory])], - [if test "$withval" = "no" -o "$withval" = "yes"; then \ + [if test "$withval" = "no" || test "$withval" = "yes"; then \ AC_MSG_WARN([You should provide name for --with-lib=ARG]); \ else \ GIT_CONF_APPEND_LINE(lib=$withval); \ @@ -182,6 +182,26 @@ AC_SUBST(NEEDS_LIBICONV) AC_SUBST(NO_ICONV) test -n "$NEEDS_LIBICONV" && LIBS="$LIBS -liconv" # +# Define NO_DEFLATE_BOUND if deflateBound is missing from zlib. +AC_DEFUN([ZLIBTEST_SRC], [ +#include <zlib.h> + +int main(void) +{ + deflateBound(0, 0); + return 0; +} +]) +AC_MSG_CHECKING([for deflateBound in -lz]) +old_LIBS="$LIBS" +LIBS="$LIBS -lz" +AC_LINK_IFELSE(ZLIBTEST_SRC, + [AC_MSG_RESULT([yes])], + [AC_MSG_RESULT([no]) + NO_DEFLATE_BOUND=yes]) +LIBS="$old_LIBS" +AC_SUBST(NO_DEFLATE_BOUND) +# # Define NEEDS_SOCKET if linking with libc is not enough (SunOS, # Patrick Mauritz). AC_CHECK_LIB([c], [socket], @@ -192,6 +212,28 @@ test -n "$NEEDS_SOCKET" && LIBS="$LIBS -lsocket" ## Checks for header files. +AC_MSG_NOTICE([CHECKS for header files]) +# +# Define OLD_ICONV if your library has an old iconv(), where the second +# (input buffer pointer) parameter is declared with type (const char **). +AC_DEFUN([OLDICONVTEST_SRC], [[ +#include <iconv.h> + +extern size_t iconv(iconv_t cd, + char **inbuf, size_t *inbytesleft, + char **outbuf, size_t *outbytesleft); + +int main(void) +{ + return 0; +} +]]) +AC_MSG_CHECKING([for old iconv()]) +AC_COMPILE_IFELSE(OLDICONVTEST_SRC, + [AC_MSG_RESULT([no])], + [AC_MSG_RESULT([yes]) + OLD_ICONV=UnfortunatelyYes]) +AC_SUBST(OLD_ICONV) ## Checks for typedefs, structures, and compiler characteristics. @@ -245,9 +287,9 @@ AC_RUN_IFELSE( [AC_LANG_PROGRAM([AC_INCLUDES_DEFAULT], [[char buf[64]; if (sprintf(buf, "%lld%hhd%jd%zd%td", (long long int)1, (char)2, (intmax_t)3, (size_t)4, (ptrdiff_t)5) != 5) - exit(1); + return 1; else if (strcmp(buf, "12345")) - exit(2);]])], + return 2;]])], [ac_cv_c_c99_format=yes], [ac_cv_c_c99_format=no]) ]) @@ -269,18 +311,36 @@ AC_CHECK_FUNC(strcasestr, [NO_STRCASESTR=YesPlease]) AC_SUBST(NO_STRCASESTR) # +# Define NO_MEMMEM if you don't have memmem. +AC_CHECK_FUNC(memmem, +[NO_MEMMEM=], +[NO_MEMMEM=YesPlease]) +AC_SUBST(NO_MEMMEM) +# # Define NO_STRLCPY if you don't have strlcpy. AC_CHECK_FUNC(strlcpy, [NO_STRLCPY=], [NO_STRLCPY=YesPlease]) AC_SUBST(NO_STRLCPY) # +# Define NO_STRTOUMAX if you don't have strtoumax in the C library. +AC_CHECK_FUNC(strtoumax, +[NO_STRTOUMAX=], +[NO_STRTOUMAX=YesPlease]) +AC_SUBST(NO_STRTOUMAX) +# # Define NO_SETENV if you don't have setenv in the C library. AC_CHECK_FUNC(setenv, [NO_SETENV=], [NO_SETENV=YesPlease]) AC_SUBST(NO_SETENV) # +# Define NO_MKDTEMP if you don't have mkdtemp in the C library. +AC_CHECK_FUNC(mkdtemp, +[NO_MKDTEMP=], +[NO_MKDTEMP=YesPlease]) +AC_SUBST(NO_MKDTEMP) +# # Define NO_MMAP if you want to avoid mmap. # # Define NO_ICONV if your libc does not properly support iconv. @@ -377,7 +437,7 @@ GIT_PARSE_WITH(iconv)) # times (my ext3 doesn't). # # Define USE_STDEV below if you want git to care about the underlying device -# change being considered an inode change from the update-cache perspective. +# change being considered an inode change from the update-index perspective. ## Output files @@ -36,6 +36,11 @@ static int check_ref(const char *name, int len, unsigned int flags) return !(flags & ~REF_NORMAL); } +int check_ref_type(const struct ref *ref, int flags) +{ + return check_ref(ref->name, strlen(ref->name), flags); +} + /* * Read all the refs from the other end */ @@ -476,9 +481,10 @@ char *get_port(char *host) * * If it returns, the connect is successful; it just dies on errors. */ -struct child_process *git_connect(int fd[2], char *url, +struct child_process *git_connect(int fd[2], const char *url_orig, const char *prog, int flags) { + char *url = xstrdup(url_orig); char *host, *path = url; char *end; int c; @@ -568,6 +574,7 @@ struct child_process *git_connect(int fd[2], char *url, prog, path, 0, target_host, 0); free(target_host); + free(url); if (free_path) free(path); return NULL; @@ -619,6 +626,7 @@ struct child_process *git_connect(int fd[2], char *url, fd[0] = conn->out; /* read from child's stdout */ fd[1] = conn->in; /* write to child's stdin */ strbuf_release(&cmd); + free(url); if (free_path) free(path); return conn; diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 599b2fc571..58e0e53cd6 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -551,6 +551,20 @@ _git_describe () _git_diff () { + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp "--cached --stat --numstat --shortstat --summary + --patch-with-stat --name-only --name-status --color + --no-color --color-words --no-renames --check + --full-index --binary --abbrev --diff-filter + --find-copies-harder --pickaxe-all --pickaxe-regex + --text --ignore-space-at-eol --ignore-space-change + --ignore-all-space --exit-code --quiet --ext-diff + --no-ext-diff" + return + ;; + esac __git_complete_file } diff --git a/contrib/examples/git-clean.sh b/contrib/examples/git-clean.sh new file mode 100755 index 0000000000..01c95e9fe8 --- /dev/null +++ b/contrib/examples/git-clean.sh @@ -0,0 +1,118 @@ +#!/bin/sh +# +# Copyright (c) 2005-2006 Pavel Roskin +# + +OPTIONS_KEEPDASHDASH= +OPTIONS_SPEC="\ +git-clean [options] <paths>... + +Clean untracked files from the working directory + +When optional <paths>... arguments are given, the paths +affected are further limited to those that match them. +-- +d remove directories as well +f override clean.requireForce and clean anyway +n don't remove anything, just show what would be done +q be quiet, only report errors +x remove ignored files as well +X remove only ignored files" + +SUBDIRECTORY_OK=Yes +. git-sh-setup +require_work_tree + +ignored= +ignoredonly= +cleandir= +rmf="rm -f --" +rmrf="rm -rf --" +rm_refuse="echo Not removing" +echo1="echo" + +disabled=$(git config --bool clean.requireForce) + +while test $# != 0 +do + case "$1" in + -d) + cleandir=1 + ;; + -f) + disabled=false + ;; + -n) + disabled=false + rmf="echo Would remove" + rmrf="echo Would remove" + rm_refuse="echo Would not remove" + echo1=":" + ;; + -q) + echo1=":" + ;; + -x) + ignored=1 + ;; + -X) + ignoredonly=1 + ;; + --) + shift + break + ;; + *) + usage # should not happen + ;; + esac + shift +done + +# requireForce used to default to false but now it defaults to true. +# IOW, lack of explicit "clean.requireForce = false" is taken as +# "clean.requireForce = true". +case "$disabled" in +"") + die "clean.requireForce not set and -n or -f not given; refusing to clean" + ;; +"true") + die "clean.requireForce set and -n or -f not given; refusing to clean" + ;; +esac + +if [ "$ignored,$ignoredonly" = "1,1" ]; then + die "-x and -X cannot be set together" +fi + +if [ -z "$ignored" ]; then + excl="--exclude-per-directory=.gitignore" + excl_info= excludes_file= + if [ -f "$GIT_DIR/info/exclude" ]; then + excl_info="--exclude-from=$GIT_DIR/info/exclude" + fi + if cfg_excl=$(git config core.excludesfile) && test -f "$cfg_excl" + then + excludes_file="--exclude-from=$cfg_excl" + fi + if [ "$ignoredonly" ]; then + excl="$excl --ignored" + fi +fi + +git ls-files --others --directory \ + $excl ${excl_info:+"$excl_info"} ${excludes_file:+"$excludes_file"} \ + -- "$@" | +while read -r file; do + if [ -d "$file" -a ! -L "$file" ]; then + if [ -z "$cleandir" ]; then + $rm_refuse "$file" + continue + fi + $echo1 "Removing $file" + $rmrf "$file" + else + $echo1 "Removing $file" + $rmf "$file" + fi +done diff --git a/git-commit.sh b/contrib/examples/git-commit.sh index fcb8443bdf..2c4a4062a5 100755 --- a/git-commit.sh +++ b/contrib/examples/git-commit.sh @@ -5,6 +5,7 @@ USAGE='[-a | --interactive] [-s] [-v] [--no-verify] [-m <message> | -F <logfile> | (-C|-c) <commit> | --amend] [-u] [-e] [--author <author>] [--template <file>] [[-i | -o] <path>...]' SUBDIRECTORY_OK=Yes +OPTIONS_SPEC= . git-sh-setup require_work_tree @@ -26,7 +27,7 @@ refuse_partial () { } TMP_INDEX= -THIS_INDEX="$GIT_DIR/index" +THIS_INDEX="${GIT_INDEX_FILE:-$GIT_DIR/index}" NEXT_INDEX="$GIT_DIR/next-index$$" rm -f "$NEXT_INDEX" save_index () { @@ -73,6 +74,7 @@ trap ' all= also= +allow_empty=f interactive= only= logfile= @@ -113,6 +115,10 @@ do -a|--a|--al|--all) all=t ;; + --allo|--allow|--allow-|--allow-e|--allow-em|--allow-emp|\ + --allow-empt|--allow-empty) + allow_empty=t + ;; --au=*|--aut=*|--auth=*|--autho=*|--author=*) force_author="${1#*=}" ;; @@ -282,9 +288,9 @@ unset only case "$all,$interactive,$also,$#" in *t,*t,*) die "Cannot use -a, --interactive or -i at the same time." ;; -t,,[1-9]*) +t,,,[1-9]*) die "Paths with -a does not make sense." ;; -,t,[1-9]*) +,t,,[1-9]*) die "Paths with --interactive does not make sense." ;; ,,t,0) die "No paths with -i does not make sense." ;; @@ -514,13 +520,18 @@ else # we need to check if there is anything to commit run_status >/dev/null fi -if [ "$?" != "0" -a ! -f "$GIT_DIR/MERGE_HEAD" ] -then +case "$allow_empty,$?,$PARENTS" in +t,* | ?,0,* | ?,*,-p' '?*-p' '?*) + # an explicit --allow-empty, or a merge commit can record the + # same tree as its parent. Otherwise having commitable paths + # is required. + ;; +*) rm -f "$GIT_DIR/COMMIT_EDITMSG" "$GIT_DIR/SQUASH_MSG" use_status_color=t run_status exit 1 -fi +esac case "$no_edit" in '') diff --git a/git-ls-remote.sh b/contrib/examples/git-ls-remote.sh index fec70bbf88..fec70bbf88 100755 --- a/git-ls-remote.sh +++ b/contrib/examples/git-ls-remote.sh diff --git a/git-merge-ours.sh b/contrib/examples/git-merge-ours.sh index c81a790aa6..29dba4ba3a 100755 --- a/git-merge-ours.sh +++ b/contrib/examples/git-merge-ours.sh @@ -9,6 +9,6 @@ # because the current index is what we will be committing as the # merge result. -git diff-index --quiet --cached HEAD || exit 2 +git diff-index --quiet --cached HEAD -- || exit 2 exit 0 diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index bf33f74b70..c80a6da252 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -71,6 +71,79 @@ def isP4Exec(kind): a plus sign, it is also executable""" return (re.search(r"(^[cku]?x)|\+.*x", kind) != None) +def setP4ExecBit(file, mode): + # Reopens an already open file and changes the execute bit to match + # the execute bit setting in the passed in mode. + + p4Type = "+x" + + if not isModeExec(mode): + p4Type = getP4OpenedType(file) + p4Type = re.sub('^([cku]?)x(.*)', '\\1\\2', p4Type) + p4Type = re.sub('(.*?\+.*?)x(.*?)', '\\1\\2', p4Type) + if p4Type[-1] == "+": + p4Type = p4Type[0:-1] + + system("p4 reopen -t %s %s" % (p4Type, file)) + +def getP4OpenedType(file): + # Returns the perforce file type for the given file. + + result = read_pipe("p4 opened %s" % file) + match = re.match(".*\((.+)\)$", result) + if match: + return match.group(1) + else: + die("Could not determine file type for %s" % file) + +def diffTreePattern(): + # This is a simple generator for the diff tree regex pattern. This could be + # a class variable if this and parseDiffTreeEntry were a part of a class. + pattern = re.compile(':(\d+) (\d+) (\w+) (\w+) ([A-Z])(\d+)?\t(.*?)((\t(.*))|$)') + while True: + yield pattern + +def parseDiffTreeEntry(entry): + """Parses a single diff tree entry into its component elements. + + See git-diff-tree(1) manpage for details about the format of the diff + output. This method returns a dictionary with the following elements: + + src_mode - The mode of the source file + dst_mode - The mode of the destination file + src_sha1 - The sha1 for the source file + dst_sha1 - The sha1 fr the destination file + status - The one letter status of the diff (i.e. 'A', 'M', 'D', etc) + status_score - The score for the status (applicable for 'C' and 'R' + statuses). This is None if there is no score. + src - The path for the source file. + dst - The path for the destination file. This is only present for + copy or renames. If it is not present, this is None. + + If the pattern is not matched, None is returned.""" + + match = diffTreePattern().next().match(entry) + if match: + return { + 'src_mode': match.group(1), + 'dst_mode': match.group(2), + 'src_sha1': match.group(3), + 'dst_sha1': match.group(4), + 'status': match.group(5), + 'status_score': match.group(6), + 'src': match.group(7), + 'dst': match.group(10) + } + return None + +def isModeExec(mode): + # Returns True if the given git mode represents an executable file, + # otherwise False. + return mode[-3:] == "755" + +def isModeExecChanged(src_mode, dst_mode): + return isModeExec(src_mode) != isModeExec(dst_mode) + def p4CmdList(cmd, stdin=None, stdin_mode='w+b'): cmd = "p4 -G %s" % cmd if verbose: @@ -494,18 +567,23 @@ class P4Submit(Command): else: print "Applying %s" % (read_pipe("git log --max-count=1 --pretty=oneline %s" % id)) diffOpts = ("", "-M")[self.detectRename] - diff = read_pipe_lines("git diff-tree -r --name-status %s \"%s^\" \"%s\"" % (diffOpts, id, id)) + diff = read_pipe_lines("git diff-tree -r %s \"%s^\" \"%s\"" % (diffOpts, id, id)) filesToAdd = set() filesToDelete = set() editedFiles = set() + filesToChangeExecBit = {} for line in diff: - modifier = line[0] - path = line[1:].strip() + diff = parseDiffTreeEntry(line) + modifier = diff['status'] + path = diff['src'] if modifier == "M": system("p4 edit \"%s\"" % path) + if isModeExecChanged(diff['src_mode'], diff['dst_mode']): + filesToChangeExecBit[path] = diff['dst_mode'] editedFiles.add(path) elif modifier == "A": filesToAdd.add(path) + filesToChangeExecBit[path] = diff['dst_mode'] if path in filesToDelete: filesToDelete.remove(path) elif modifier == "D": @@ -513,9 +591,11 @@ class P4Submit(Command): if path in filesToAdd: filesToAdd.remove(path) elif modifier == "R": - src, dest = line.strip().split("\t")[1:3] + src, dest = diff['src'], diff['dst'] system("p4 integrate -Dt \"%s\" \"%s\"" % (src, dest)) system("p4 edit \"%s\"" % (dest)) + if isModeExecChanged(diff['src_mode'], diff['dst_mode']): + filesToChangeExecBit[dest] = diff['dst_mode'] os.unlink(dest) editedFiles.add(dest) filesToDelete.add(src) @@ -568,6 +648,11 @@ class P4Submit(Command): system("p4 revert \"%s\"" % f) system("p4 delete \"%s\"" % f) + # Set/clear executable bits + for f in filesToChangeExecBit.keys(): + mode = filesToChangeExecBit[f] + setP4ExecBit(f, mode) + logMessage = "" if not self.directSubmit: logMessage = extractLogMessageFromGitCommit(id) @@ -1056,7 +1141,7 @@ class P4Sync(Command): l = p4CmdList("labels %s..." % ' '.join (self.depotPaths)) if len(l) > 0 and not self.silent: - print "Finding files belonging to labels in %s" % `self.depotPath` + print "Finding files belonging to labels in %s" % `self.depotPaths` for output in l: label = output["label"] @@ -1122,6 +1207,15 @@ class P4Sync(Command): for branch in lostAndFoundBranches: self.knownBranches[branch] = branch + def getBranchMappingFromGitBranches(self): + branches = p4BranchesInGit(self.importIntoRemotes) + for branch in branches.keys(): + if branch == "master": + branch = "main" + else: + branch = branch[len(self.projectName):] + self.knownBranches[branch] = branch + def listExistingP4GitBranches(self): # branches holds mapping from name to commit branches = p4BranchesInGit(self.importIntoRemotes) @@ -1456,8 +1550,10 @@ class P4Sync(Command): ## FIXME - what's a P4 projectName ? self.projectName = self.guessProjectName() - if not self.hasOrigin: - self.getBranchMapping(); + if self.hasOrigin: + self.getBranchMappingFromGitBranches() + else: + self.getBranchMapping() if self.verbose: print "p4-git branches: %s" % self.p4BranchesInGit print "initial parents: %s" % self.initialParents diff --git a/contrib/gitview/gitview b/contrib/gitview/gitview index 449ee69bf4..4c99dfb903 100755 --- a/contrib/gitview/gitview +++ b/contrib/gitview/gitview @@ -27,20 +27,17 @@ import math import string import fcntl +have_gtksourceview2 = False +have_gtksourceview = False try: import gtksourceview2 have_gtksourceview2 = True except ImportError: - have_gtksourceview2 = False - -try: - import gtksourceview - have_gtksourceview = True -except ImportError: - have_gtksourceview = False - -if not have_gtksourceview2 and not have_gtksourceview: - print "Running without gtksourceview2 or gtksourceview module" + 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})') diff --git a/contrib/hg-to-git/hg-to-git.py b/contrib/hg-to-git/hg-to-git.py index 7a1c3e497f..9befb92c41 100755 --- a/contrib/hg-to-git/hg-to-git.py +++ b/contrib/hg-to-git/hg-to-git.py @@ -211,7 +211,7 @@ for cset in range(int(tip) + 1): os.system('git-ls-files -x .hg --deleted | git-update-index --remove --stdin') # commit - os.system(getgitenv(user, date) + 'git-commit -a -F %s' % filecomment) + os.system(getgitenv(user, date) + 'git commit --allow-empty -a -F %s' % filecomment) os.unlink(filecomment) # tag diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email index 2aa9bb501c..7511ea0797 100644 --- a/contrib/hooks/post-receive-email +++ b/contrib/hooks/post-receive-email @@ -2,24 +2,26 @@ # # Copyright (c) 2007 Andy Parkins # -# An example hook script to mail out commit update information. This hook sends emails -# listing new revisions to the repository introduced by the change being reported. The -# rule is that (for branch updates) each commit will appear on one email and one email -# only. +# An example hook script to mail out commit update information. This hook +# sends emails listing new revisions to the repository introduced by the +# change being reported. The rule is that (for branch updates) each commit +# will appear on one email and one email only. # -# This hook is stored in the contrib/hooks directory. Your distribution will have put -# this somewhere standard. You should make this script executable then link to it in -# the repository you would like to use it in. For example, on debian the hook is stored -# in /usr/share/doc/git-core/contrib/hooks/post-receive-email: +# This hook is stored in the contrib/hooks directory. Your distribution +# will have put this somewhere standard. You should make this script +# executable then link to it in the repository you would like to use it in. +# For example, on debian the hook is stored in +# /usr/share/doc/git-core/contrib/hooks/post-receive-email: # # chmod a+x post-receive-email # cd /path/to/your/repository.git # ln -sf /usr/share/doc/git-core/contrib/hooks/post-receive-email hooks/post-receive # -# This hook script assumes it is enabled on the central repository of a project, with -# all users pushing only to it and not between each other. It will still work if you -# don't operate in that style, but it would become possible for the email to be from -# someone other than the person doing the push. +# This hook script assumes it is enabled on the central repository of a +# project, with all users pushing only to it and not between each other. It +# will still work if you don't operate in that style, but it would become +# possible for the email to be from someone other than the person doing the +# push. # # Config # ------ @@ -28,15 +30,17 @@ # emails for every ref update. # hooks.announcelist # This is the list that all pushes of annotated tags will go to. Leave it -# blank to default to the mailinglist field. The announce emails lists the -# short log summary of the changes since the last annotated tag. -# hook.envelopesender -# If set then the -f option is passed to sendmail to allow the envelope sender -# address to be set +# blank to default to the mailinglist field. The announce emails lists +# the short log summary of the changes since the last annotated tag. +# hooks.envelopesender +# If set then the -f option is passed to sendmail to allow the envelope +# sender address to be set +# hooks.emailprefix +# All emails have their subjects prefixed with this prefix, or "[SCM]" +# if emailprefix is unset, to aid filtering # # Notes # ----- -# All emails have their subjects prefixed with "[SCM]" to aid filtering. # All emails include the headers "X-Git-Refname", "X-Git-Oldrev", # "X-Git-Newrev", and "X-Git-Reftype" to enable fine tuned filtering and # give information for debugging. @@ -49,8 +53,8 @@ # this is and calls the appropriate body-generation routine after outputting # the common header # -# Note this function doesn't actually generate any email output, that is taken -# care of by the functions it calls: +# Note this function doesn't actually generate any email output, that is +# taken care of by the functions it calls: # - generate_email_header # - generate_create_XXXX_email # - generate_update_XXXX_email @@ -152,10 +156,6 @@ generate_email() fi # Email parameters - # The committer will be obtained from the latest existing rev; so - # for a deletion it will be the oldrev, for the others, then newrev - committer=$(git show --pretty=full -s $rev | sed -ne "s/^Commit: //p" | - sed -ne 's/\(.*\) </"\1" </p') # The email subject will contain the best description of the ref # that we can build from the parameters describe=$(git describe $rev 2>/dev/null) @@ -186,7 +186,7 @@ generate_email_header() # Generate header cat <<-EOF To: $recipients - Subject: ${EMAILPREFIX}$projectdesc $refname_type, $short_refname, ${change_type}d. $describe + Subject: ${emailprefix}$projectdesc $refname_type, $short_refname, ${change_type}d. $describe X-Git-Refname: $refname X-Git-Reftype: $refname_type X-Git-Oldrev: $oldrev @@ -225,8 +225,9 @@ generate_create_branch_email() echo $LOGBEGIN # This shows all log entries that are not already covered by # another ref - i.e. commits that are now accessible from this - # ref that were previously not accessible (see generate_update_branch_email - # for the explanation of this command) + # ref that were previously not accessible + # (see generate_update_branch_email for the explanation of this + # command) git rev-parse --not --branches | grep -v $(git rev-parse $refname) | git rev-list --pretty --stdin $newrev echo $LOGEND @@ -254,9 +255,10 @@ generate_update_branch_email() # # git-rev-list N ^O ^X ^N # - # So, we need to build up the list more carefully. git-rev-parse will - # generate a list of revs that may be fed into git-rev-list. We can get - # it to make the "--not --all" part and then filter out the "^N" with: + # So, we need to build up the list more carefully. git-rev-parse + # will generate a list of revs that may be fed into git-rev-list. + # We can get it to make the "--not --all" part and then filter out + # the "^N" with: # # git-rev-parse --not --all | grep -v N # @@ -266,16 +268,17 @@ generate_update_branch_email() # git-rev-list N ^O ^X # # This leaves a problem when someone else updates the repository - # while this script is running. Their new value of the ref we're working - # on would be included in the "--not --all" output; and as our $newrev - # would be an ancestor of that commit, it would exclude all of our - # commits. What we really want is to exclude the current value of - # $refname from the --not list, rather than N itself. So: + # while this script is running. Their new value of the ref we're + # working on would be included in the "--not --all" output; and as + # our $newrev would be an ancestor of that commit, it would exclude + # all of our commits. What we really want is to exclude the current + # value of $refname from the --not list, rather than N itself. So: # # git-rev-parse --not --all | grep -v $(git-rev-parse $refname) # - # Get's us to something pretty safe (apart from the small time between - # refname being read, and git-rev-parse running - for that, I give up) + # Get's us to something pretty safe (apart from the small time + # between refname being read, and git-rev-parse running - for that, + # I give up) # # # Next problem, consider this: @@ -283,18 +286,18 @@ generate_update_branch_email() # \ # * --- X --- * --- N ($newrev) # - # That is to say, there is no guarantee that oldrev is a strict subset of - # newrev (it would have required a --force, but that's allowed). So, we - # can't simply say rev-list $oldrev..$newrev. Instead we find the common - # base of the two revs and list from there. + # That is to say, there is no guarantee that oldrev is a strict + # subset of newrev (it would have required a --force, but that's + # allowed). So, we can't simply say rev-list $oldrev..$newrev. + # Instead we find the common base of the two revs and list from + # there. # - # As above, we need to take into account the presence of X; if another - # branch is already in the repository and points at some of the revisions - # that we are about to output - we don't want them. The solution is as - # before: git-rev-parse output filtered. + # As above, we need to take into account the presence of X; if + # another branch is already in the repository and points at some of + # the revisions that we are about to output - we don't want them. + # The solution is as before: git-rev-parse output filtered. # - # Finally, tags: - # 1 --- 2 --- O --- T --- 3 --- 4 --- N + # Finally, tags: 1 --- 2 --- O --- T --- 3 --- 4 --- N # # Tags pushed into the repository generate nice shortlog emails that # summarise the commits between them and the previous tag. However, @@ -302,13 +305,14 @@ generate_update_branch_email() # for a branch update. Therefore we still want to output revisions # that have been output on a tag email. # - # Luckily, git-rev-parse includes just the tool. Instead of using "--all" - # we use "--branches"; this has the added benefit that "remotes/" will - # be ignored as well. - - # List all of the revisions that were removed by this update, in a fast forward - # update, this list will be empty, because rev-list O ^N is empty. For a non - # fast forward, O ^N is the list of removed revisions + # Luckily, git-rev-parse includes just the tool. Instead of using + # "--all" we use "--branches"; this has the added benefit that + # "remotes/" will be ignored as well. + + # List all of the revisions that were removed by this update, in a + # fast forward update, this list will be empty, because rev-list O + # ^N is empty. For a non fast forward, O ^N is the list of removed + # revisions fast_forward="" rev="" for rev in $(git rev-list $newrev..$oldrev) @@ -321,10 +325,10 @@ generate_update_branch_email() fi # List all the revisions from baserev to newrev in a kind of - # "table-of-contents"; note this list can include revisions that have - # already had notification emails and is present to show the full detail - # of the change from rolling back the old revision to the base revision and - # then forward to the new revision + # "table-of-contents"; note this list can include revisions that + # have already had notification emails and is present to show the + # full detail of the change from rolling back the old revision to + # the base revision and then forward to the new revision for rev in $(git rev-list $oldrev..$newrev) do revtype=$(git cat-file -t "$rev") @@ -334,19 +338,20 @@ generate_update_branch_email() if [ "$fast_forward" ]; then echo " from $oldrev ($oldrev_type)" else - # 1. Existing revisions were removed. In this case newrev is a - # subset of oldrev - this is the reverse of a fast-forward, - # a rewind - # 2. New revisions were added on top of an old revision, this is - # a rewind and addition. + # 1. Existing revisions were removed. In this case newrev + # is a subset of oldrev - this is the reverse of a + # fast-forward, a rewind + # 2. New revisions were added on top of an old revision, + # this is a rewind and addition. - # (1) certainly happened, (2) possibly. When (2) hasn't happened, - # we set a flag to indicate that no log printout is required. + # (1) certainly happened, (2) possibly. When (2) hasn't + # happened, we set a flag to indicate that no log printout + # is required. echo "" - # Find the common ancestor of the old and new revisions and compare - # it with newrev + # Find the common ancestor of the old and new revisions and + # compare it with newrev baserev=$(git merge-base $oldrev $newrev) rewind_only="" if [ "$baserev" = "$newrev" ]; then @@ -387,21 +392,22 @@ generate_update_branch_email() git rev-parse --not --branches | grep -v $(git rev-parse $refname) | git rev-list --pretty --stdin $oldrev..$newrev - # XXX: Need a way of detecting whether git rev-list actually outputted - # anything, so that we can issue a "no new revisions added by this - # update" message + # XXX: Need a way of detecting whether git rev-list actually + # outputted anything, so that we can issue a "no new + # revisions added by this update" message echo $LOGEND else echo "No new revisions were added by this update." fi - # The diffstat is shown from the old revision to the new revision. This - # is to show the truth of what happened in this change. There's no point - # showing the stat from the base to the new revision because the base - # is effectively a random revision at this point - the user will be - # interested in what this revision changed - including the undoing of - # previous revisions in the case of non-fast forward updates. + # The diffstat is shown from the old revision to the new revision. + # This is to show the truth of what happened in this change. + # There's no point showing the stat from the base to the new + # revision because the base is effectively a random revision at this + # point - the user will be interested in what this revision changed + # - including the undoing of previous revisions in the case of + # non-fast forward updates. echo "" echo "Summary of changes:" git diff-tree --stat --summary --find-copies-harder $oldrev..$newrev @@ -448,7 +454,8 @@ generate_update_atag_email() # generate_atag_email() { - # Use git-for-each-ref to pull out the individual fields from the tag + # Use git-for-each-ref to pull out the individual fields from the + # tag eval $(git for-each-ref --shell --format=' tagobject=%(*objectname) tagtype=%(*objecttype) @@ -459,8 +466,10 @@ generate_atag_email() echo " tagging $tagobject ($tagtype)" case "$tagtype" in commit) + # If the tagged object is a commit, then we assume this is a - # release, and so we calculate which tag this tag is replacing + # release, and so we calculate which tag this tag is + # replacing prevtag=$(git describe --abbrev=0 $newrev^ 2>/dev/null) if [ -n "$prevtag" ]; then @@ -477,25 +486,27 @@ generate_atag_email() echo "" echo $LOGBEGIN - # Show the content of the tag message; this might contain a change log - # or release notes so is worth displaying. + # Show the content of the tag message; this might contain a change + # log or release notes so is worth displaying. git cat-file tag $newrev | sed -e '1,/^$/d' echo "" case "$tagtype" in commit) - # Only commit tags make sense to have rev-list operations performed - # on them + # Only commit tags make sense to have rev-list operations + # performed on them if [ -n "$prevtag" ]; then # Show changes since the previous release git rev-list --pretty=short "$prevtag..$newrev" | git shortlog else - # No previous tag, show all the changes since time began + # No previous tag, show all the changes since time + # began git rev-list --pretty=short $newrev | git shortlog fi ;; *) - # XXX: Is there anything useful we can do for non-commit objects? + # XXX: Is there anything useful we can do for non-commit + # objects? ;; esac @@ -544,13 +555,14 @@ generate_update_general_email() # generate_general_email() { - # Unannotated tags are more about marking a point than releasing a version; - # therefore we don't do the shortlog summary that we do for annotated tags - # above - we simply show that the point has been marked, and print the log - # message for the marked point for reference purposes + # Unannotated tags are more about marking a point than releasing a + # version; therefore we don't do the shortlog summary that we do for + # annotated tags above - we simply show that the point has been + # marked, and print the log message for the marked point for + # reference purposes # - # Note this section also catches any other reference type (although there - # aren't any) and deals with them in the same way. + # Note this section also catches any other reference type (although + # there aren't any) and deals with them in the same way. echo "" if [ "$newrev_type" = "commit" ]; then @@ -558,10 +570,10 @@ generate_general_email() git show --no-color --root -s $newrev echo $LOGEND else - # What can we do here? The tag marks an object that is not a commit, - # so there is no log for us to display. It's probably not wise to - # output git-cat-file as it could be a binary blob. We'll just say how - # big it is + # What can we do here? The tag marks an object that is not + # a commit, so there is no log for us to display. It's + # probably not wise to output git-cat-file as it could be a + # binary blob. We'll just say how big it is echo "$newrev is a $newrev_type, and is $(git cat-file -s $newrev) bytes long." fi } @@ -590,7 +602,6 @@ send_mail() # ---------------------------- main() # --- Constants -EMAILPREFIX="[SCM] " LOGBEGIN="- Log -----------------------------------------------------------------" LOGEND="-----------------------------------------------------------------------" @@ -604,8 +615,8 @@ if [ -z "$GIT_DIR" ]; then fi projectdesc=$(sed -ne '1p' "$GIT_DIR/description") -# Check if the description is unchanged from it's default, and shorten it to a -# more manageable length if it is +# Check if the description is unchanged from it's default, and shorten it to +# a more manageable length if it is if expr "$projectdesc" : "Unnamed repository.*$" >/dev/null then projectdesc="UNNAMED PROJECT" @@ -614,13 +625,15 @@ fi recipients=$(git repo-config hooks.mailinglist) announcerecipients=$(git repo-config hooks.announcelist) envelopesender=$(git-repo-config hooks.envelopesender) +emailprefix=$(git-repo-config hooks.emailprefix || echo '[SCM] ') # --- Main loop -# Allow dual mode: run from the command line just like the update hook, or if -# no arguments are given then run as a hook script +# Allow dual mode: run from the command line just like the update hook, or +# if no arguments are given then run as a hook script if [ -n "$1" -a -n "$2" -a -n "$3" ]; then # Output to the terminal in command line mode - if someone wanted to - # resend an email; they could redirect the output to sendmail themselves + # resend an email; they could redirect the output to sendmail + # themselves PAGER= generate_email $2 $3 $1 else while read oldrev newrev refname diff --git a/contrib/remotes2config.sh b/contrib/remotes2config.sh index 5838b3ab05..1cda19f66a 100644..100755 --- a/contrib/remotes2config.sh +++ b/contrib/remotes2config.sh @@ -11,11 +11,11 @@ if [ -d "$GIT_DIR"/remotes ]; then { cd "$GIT_DIR"/remotes ls | while read f; do - name=$(printf "$f" | tr -c "A-Za-z0-9" ".") + name=$(printf "$f" | tr -c "A-Za-z0-9-" ".") sed -n \ - -e "s/^URL: \(.*\)$/remote.$name.url \1 ./p" \ - -e "s/^Pull: \(.*\)$/remote.$name.fetch \1 ^$ /p" \ - -e "s/^Push: \(.*\)$/remote.$name.push \1 ^$ /p" \ + -e "s/^URL:[ ]*\(.*\)$/remote.$name.url \1 ./p" \ + -e "s/^Pull:[ ]*\(.*\)$/remote.$name.fetch \1 ^$ /p" \ + -e "s/^Push:[ ]*\(.*\)$/remote.$name.push \1 ^$ /p" \ < "$f" done echo done diff --git a/csum-file.c b/csum-file.c index 3729e73e19..9728a99541 100644 --- a/csum-file.c +++ b/csum-file.c @@ -18,7 +18,8 @@ static void sha1flush(struct sha1file *f, unsigned int count) for (;;) { int ret = xwrite(f->fd, buf, count); if (ret > 0) { - display_throughput(f->tp, ret); + f->total += ret; + display_throughput(f->tp, f->total); buf = (char *) buf + ret; count -= ret; if (count) @@ -87,21 +88,12 @@ struct sha1file *sha1fd(int fd, const char *name) struct sha1file *sha1fd_throughput(int fd, const char *name, struct progress *tp) { - struct sha1file *f; - unsigned len; - - f = xmalloc(sizeof(*f)); - - len = strlen(name); - if (len >= PATH_MAX) - die("you wascally wabbit, you"); - f->namelen = len; - memcpy(f->name, name, len+1); - + struct sha1file *f = xmalloc(sizeof(*f)); f->fd = fd; - f->error = 0; f->offset = 0; + f->total = 0; f->tp = tp; + f->name = name; f->do_crc = 0; SHA1_Init(&f->ctx); return f; diff --git a/csum-file.h b/csum-file.h index 4d1b231292..1af76562f3 100644 --- a/csum-file.h +++ b/csum-file.h @@ -5,11 +5,12 @@ struct progress; /* A SHA1-protected file */ struct sha1file { - int fd, error; - unsigned int offset, namelen; + int fd; + unsigned int offset; SHA_CTX ctx; + off_t total; struct progress *tp; - char name[PATH_MAX]; + const char *name; int do_crc; uint32_t crc32; unsigned char buffer[8192]; @@ -406,7 +406,8 @@ static struct daemon_service daemon_service[] = { { "receive-pack", "receivepack", receive_pack, 0, 1 }, }; -static void enable_service(const char *name, int ena) { +static void enable_service(const char *name, int ena) +{ int i; for (i = 0; i < ARRAY_SIZE(daemon_service); i++) { if (!strcmp(daemon_service[i].name, name)) { @@ -417,7 +418,8 @@ static void enable_service(const char *name, int ena) { die("No such service %s", name); } -static void make_service_overridable(const char *name, int ena) { +static void make_service_overridable(const char *name, int ena) +{ int i; for (i = 0; i < ARRAY_SIZE(daemon_service); i++) { if (!strcmp(daemon_service[i].name, name)) { @@ -540,7 +542,7 @@ static int execute(struct sockaddr *addr) if (addr->sa_family == AF_INET) { struct sockaddr_in *sin_addr = (void *) addr; inet_ntop(addr->sa_family, &sin_addr->sin_addr, addrbuf, sizeof(addrbuf)); - port = sin_addr->sin_port; + port = ntohs(sin_addr->sin_port); #ifndef NO_IPV6 } else if (addr && addr->sa_family == AF_INET6) { struct sockaddr_in6 *sin6_addr = (void *) addr; @@ -550,7 +552,7 @@ static int execute(struct sockaddr *addr) inet_ntop(AF_INET6, &sin6_addr->sin6_addr, buf, sizeof(addrbuf) - 1); strcat(buf, "]"); - port = sin6_addr->sin6_port; + port = ntohs(sin6_addr->sin6_port); #endif } loginfo("Connection from %s:%d", addrbuf, port); diff --git a/diff-lib.c b/diff-lib.c index da5571302d..d85d8f34ba 100644 --- a/diff-lib.c +++ b/diff-lib.c @@ -121,7 +121,7 @@ static int queue_diff(struct diff_options *o, } else { struct diff_filespec *d1, *d2; - if (o->reverse_diff) { + if (DIFF_OPT_TST(o, REVERSE_DIFF)) { unsigned tmp; const char *tmp_c; tmp = mode1; mode1 = mode2; mode2 = tmp; @@ -173,9 +173,10 @@ static int is_in_index(const char *path) } static int handle_diff_files_args(struct rev_info *revs, - int argc, const char **argv, int *silent) + int argc, const char **argv, + unsigned int *options) { - *silent = 0; + *options = 0; /* revs->max_count == -2 means --no-index */ while (1 < argc && argv[1][0] == '-') { @@ -188,11 +189,11 @@ static int handle_diff_files_args(struct rev_info *revs, else if (!strcmp(argv[1], "-n") || !strcmp(argv[1], "--no-index")) { revs->max_count = -2; - revs->diffopt.exit_with_status = 1; - revs->diffopt.no_index = 1; + DIFF_OPT_SET(&revs->diffopt, EXIT_WITH_STATUS); + DIFF_OPT_SET(&revs->diffopt, NO_INDEX); } else if (!strcmp(argv[1], "-q")) - *silent = 1; + *options |= DIFF_SILENT_ON_REMOVED; else return error("invalid option: %s", argv[1]); argv++; argc--; @@ -207,7 +208,7 @@ static int handle_diff_files_args(struct rev_info *revs, if (!is_in_index(revs->diffopt.paths[0]) || !is_in_index(revs->diffopt.paths[1])) { revs->max_count = -2; - revs->diffopt.no_index = 1; + DIFF_OPT_SET(&revs->diffopt, NO_INDEX); } } @@ -230,7 +231,7 @@ static int handle_diff_files_args(struct rev_info *revs, static int is_outside_repo(const char *path, int nongit, const char *prefix) { int i; - if (nongit || !strcmp(path, "-") || path[0] == '/') + if (nongit || !strcmp(path, "-") || is_absolute_path(path)) return 1; if (prefixcmp(path, "../")) return 0; @@ -258,7 +259,7 @@ int setup_diff_no_index(struct rev_info *revs, break; } else if (i < argc - 3 && !strcmp(argv[i], "--no-index")) { i = argc - 3; - revs->diffopt.exit_with_status = 1; + DIFF_OPT_SET(&revs->diffopt, EXIT_WITH_STATUS); break; } if (argc != i + 2 || (!is_outside_repo(argv[i + 1], nongit, prefix) && @@ -296,7 +297,7 @@ int setup_diff_no_index(struct rev_info *revs, else revs->diffopt.paths = argv + argc - 2; revs->diffopt.nr_paths = 2; - revs->diffopt.no_index = 1; + DIFF_OPT_SET(&revs->diffopt, NO_INDEX); revs->max_count = -2; if (diff_setup_done(&revs->diffopt) < 0) die("diff_setup_done failed"); @@ -305,12 +306,12 @@ int setup_diff_no_index(struct rev_info *revs, int run_diff_files_cmd(struct rev_info *revs, int argc, const char **argv) { - int silent_on_removed; + unsigned int options; - if (handle_diff_files_args(revs, argc, argv, &silent_on_removed)) + if (handle_diff_files_args(revs, argc, argv, &options)) return -1; - if (revs->diffopt.no_index) { + if (DIFF_OPT_TST(&revs->diffopt, NO_INDEX)) { if (revs->diffopt.nr_paths != 2) return error("need two files/directories with --no-index"); if (queue_diff(&revs->diffopt, revs->diffopt.paths[0], @@ -329,13 +330,16 @@ int run_diff_files_cmd(struct rev_info *revs, int argc, const char **argv) perror("read_cache"); return -1; } - return run_diff_files(revs, silent_on_removed); + return run_diff_files(revs, options); } -int run_diff_files(struct rev_info *revs, int silent_on_removed) +int run_diff_files(struct rev_info *revs, unsigned int option) { int entries, i; int diff_unmerged_stage = revs->max_count; + int silent_on_removed = option & DIFF_SILENT_ON_REMOVED; + unsigned ce_option = ((option & DIFF_RACY_IS_MODIFIED) + ? CE_MATCH_RACY_IS_DIRTY : 0); if (diff_unmerged_stage < 0) diff_unmerged_stage = 2; @@ -346,7 +350,8 @@ int run_diff_files(struct rev_info *revs, int silent_on_removed) struct cache_entry *ce = active_cache[i]; int changed; - if (revs->diffopt.quiet && revs->diffopt.has_changes) + if (DIFF_OPT_TST(&revs->diffopt, QUIET) && + DIFF_OPT_TST(&revs->diffopt, HAS_CHANGES)) break; if (!ce_path_match(ce, revs->prune_data)) @@ -441,8 +446,8 @@ int run_diff_files(struct rev_info *revs, int silent_on_removed) ce->sha1, ce->name, NULL); continue; } - changed = ce_match_stat(ce, &st, 0); - if (!changed && !revs->diffopt.find_copies_harder) + changed = ce_match_stat(ce, &st, ce_option); + if (!changed && !DIFF_OPT_TST(&revs->diffopt, FIND_COPIES_HARDER)) continue; oldmode = ntohl(ce->ce_mode); newmode = ntohl(ce_mode_from_stat(ce, st.st_mode)); @@ -561,7 +566,7 @@ static int show_modified(struct rev_info *revs, oldmode = old->ce_mode; if (mode == oldmode && !hashcmp(sha1, old->sha1) && - !revs->diffopt.find_copies_harder) + !DIFF_OPT_TST(&revs->diffopt, FIND_COPIES_HARDER)) return 0; mode = ntohl(mode); @@ -581,7 +586,8 @@ static int diff_cache(struct rev_info *revs, struct cache_entry *ce = *ac; int same = (entries > 1) && ce_same_name(ce, ac[1]); - if (revs->diffopt.quiet && revs->diffopt.has_changes) + if (DIFF_OPT_TST(&revs->diffopt, QUIET) && + DIFF_OPT_TST(&revs->diffopt, HAS_CHANGES)) break; if (!ce_path_match(ce, pathspec)) @@ -146,7 +146,7 @@ int git_diff_ui_config(const char *var, const char *value) return 0; } if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff")) { - diff_use_color_default = git_config_colorbool(var, value); + diff_use_color_default = git_config_colorbool(var, value, -1); return 0; } if (!strcmp(var, "diff.renames")) { @@ -828,10 +828,10 @@ static void show_stats(struct diffstat_t* data, struct diff_options *options) } /* Find the longest filename and max number of changes */ - reset = diff_get_color(options->color_diff, DIFF_RESET); - set = diff_get_color(options->color_diff, DIFF_PLAIN); - add_c = diff_get_color(options->color_diff, DIFF_FILE_NEW); - del_c = diff_get_color(options->color_diff, DIFF_FILE_OLD); + reset = diff_get_color_opt(options, DIFF_RESET); + set = diff_get_color_opt(options, DIFF_PLAIN); + add_c = diff_get_color_opt(options, DIFF_FILE_NEW); + del_c = diff_get_color_opt(options, DIFF_FILE_OLD); for (i = 0; i < data->nr; i++) { struct diffstat_file *file = data->files[i]; @@ -1259,8 +1259,8 @@ static void builtin_diff(const char *name_a, mmfile_t mf1, mf2; const char *lbl[2]; char *a_one, *b_two; - const char *set = diff_get_color(o->color_diff, DIFF_METAINFO); - const char *reset = diff_get_color(o->color_diff, DIFF_RESET); + const char *set = diff_get_color_opt(o, DIFF_METAINFO); + const char *reset = diff_get_color_opt(o, DIFF_RESET); a_one = quote_two("a/", name_a + (*name_a == '/')); b_two = quote_two("b/", name_b + (*name_b == '/')); @@ -1293,7 +1293,7 @@ static void builtin_diff(const char *name_a, goto free_ab_and_return; if (complete_rewrite) { emit_rewrite_diff(name_a, name_b, one, two, - o->color_diff); + DIFF_OPT_TST(o, COLOR_DIFF)); o->found_changes = 1; goto free_ab_and_return; } @@ -1302,13 +1302,13 @@ static void builtin_diff(const char *name_a, if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0) die("unable to read files to diff"); - if (!o->text && + if (!DIFF_OPT_TST(o, TEXT) && (diff_filespec_is_binary(one) || diff_filespec_is_binary(two))) { /* Quite common confusing case */ if (mf1.size == mf2.size && !memcmp(mf1.ptr, mf2.ptr, mf1.size)) goto free_ab_and_return; - if (o->binary) + if (DIFF_OPT_TST(o, BINARY)) emit_binary_diff(&mf1, &mf2); else printf("Binary files %s and %s differ\n", @@ -1331,7 +1331,7 @@ static void builtin_diff(const char *name_a, memset(&xecfg, 0, sizeof(xecfg)); memset(&ecbdata, 0, sizeof(ecbdata)); ecbdata.label_path = lbl; - ecbdata.color_diff = o->color_diff; + ecbdata.color_diff = DIFF_OPT_TST(o, COLOR_DIFF); ecbdata.found_changesp = &o->found_changes; ecbdata.ws_rule = whitespace_rule(name_b ? name_b : name_a); xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts; @@ -1348,11 +1348,11 @@ static void builtin_diff(const char *name_a, ecb.outf = xdiff_outf; ecb.priv = &ecbdata; ecbdata.xm.consume = fn_out_consume; - if (o->color_diff_words) + if (DIFF_OPT_TST(o, COLOR_DIFF_WORDS)) ecbdata.diff_words = xcalloc(1, sizeof(struct diff_words_data)); xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb); - if (o->color_diff_words) + if (DIFF_OPT_TST(o, COLOR_DIFF_WORDS)) free_diff_words_data(&ecbdata); } @@ -1426,7 +1426,7 @@ static void builtin_checkdiff(const char *name_a, const char *name_b, data.xm.consume = checkdiff_consume; data.filename = name_b ? name_b : name_a; data.lineno = 0; - data.color_diff = o->color_diff; + data.color_diff = DIFF_OPT_TST(o, COLOR_DIFF); data.ws_rule = whitespace_rule(data.filename); if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0) @@ -1871,7 +1871,7 @@ static void run_diff_cmd(const char *pgm, struct diff_options *o, int complete_rewrite) { - if (!o->allow_external) + if (!DIFF_OPT_TST(o, ALLOW_EXTERNAL)) pgm = NULL; else { const char *cmd = external_diff_attr(name); @@ -1969,9 +1969,9 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o) } if (hashcmp(one->sha1, two->sha1)) { - int abbrev = o->full_index ? 40 : DEFAULT_ABBREV; + int abbrev = DIFF_OPT_TST(o, FULL_INDEX) ? 40 : DEFAULT_ABBREV; - if (o->binary) { + if (DIFF_OPT_TST(o, BINARY)) { mmfile_t mf; if ((!fill_mmfile(&mf, one) && diff_filespec_is_binary(one)) || (!fill_mmfile(&mf, two) && diff_filespec_is_binary(two))) @@ -2063,7 +2063,10 @@ void diff_setup(struct diff_options *options) options->change = diff_change; options->add_remove = diff_addremove; - options->color_diff = diff_use_color_default; + if (diff_use_color_default) + DIFF_OPT_SET(options, COLOR_DIFF); + else + DIFF_OPT_CLR(options, COLOR_DIFF); options->detect_rename = diff_detect_rename_default; } @@ -2082,7 +2085,7 @@ int diff_setup_done(struct diff_options *options) if (count > 1) die("--name-only, --name-status, --check and -s are mutually exclusive"); - if (options->find_copies_harder) + if (DIFF_OPT_TST(options, FIND_COPIES_HARDER)) options->detect_rename = DIFF_DETECT_COPY; if (options->output_format & (DIFF_FORMAT_NAME | @@ -2106,12 +2109,12 @@ int diff_setup_done(struct diff_options *options) DIFF_FORMAT_SHORTSTAT | DIFF_FORMAT_SUMMARY | DIFF_FORMAT_CHECKDIFF)) - options->recursive = 1; + DIFF_OPT_SET(options, RECURSIVE); /* * Also pickaxe would not work very well if you do not say recursive */ if (options->pickaxe) - options->recursive = 1; + DIFF_OPT_SET(options, RECURSIVE); if (options->detect_rename && options->rename_limit < 0) options->rename_limit = diff_rename_limit_default; @@ -2133,9 +2136,9 @@ int diff_setup_done(struct diff_options *options) * to have found. It does not make sense not to return with * exit code in such a case either. */ - if (options->quiet) { + if (DIFF_OPT_TST(options, QUIET)) { options->output_format = DIFF_FORMAT_NO_OUTPUT; - options->exit_with_status = 1; + DIFF_OPT_SET(options, EXIT_WITH_STATUS); } /* @@ -2143,7 +2146,7 @@ int diff_setup_done(struct diff_options *options) * upon the first hit. We need to run diff as usual. */ if (options->pickaxe || options->filter) - options->quiet = 0; + DIFF_OPT_CLR(options, QUIET); return 0; } @@ -2200,21 +2203,32 @@ static int diff_scoreopt_parse(const char *opt); int diff_opt_parse(struct diff_options *options, const char **av, int ac) { const char *arg = av[0]; + + /* Output format options */ if (!strcmp(arg, "-p") || !strcmp(arg, "-u")) options->output_format |= DIFF_FORMAT_PATCH; else if (opt_arg(arg, 'U', "unified", &options->context)) options->output_format |= DIFF_FORMAT_PATCH; else if (!strcmp(arg, "--raw")) options->output_format |= DIFF_FORMAT_RAW; - else if (!strcmp(arg, "--patch-with-raw")) { + else if (!strcmp(arg, "--patch-with-raw")) options->output_format |= DIFF_FORMAT_PATCH | DIFF_FORMAT_RAW; - } - else if (!strcmp(arg, "--numstat")) { + else if (!strcmp(arg, "--numstat")) options->output_format |= DIFF_FORMAT_NUMSTAT; - } - else if (!strcmp(arg, "--shortstat")) { + else if (!strcmp(arg, "--shortstat")) options->output_format |= DIFF_FORMAT_SHORTSTAT; - } + else if (!strcmp(arg, "--check")) + options->output_format |= DIFF_FORMAT_CHECKDIFF; + else if (!strcmp(arg, "--summary")) + options->output_format |= DIFF_FORMAT_SUMMARY; + else if (!strcmp(arg, "--patch-with-stat")) + options->output_format |= DIFF_FORMAT_PATCH | DIFF_FORMAT_DIFFSTAT; + else if (!strcmp(arg, "--name-only")) + options->output_format |= DIFF_FORMAT_NAME; + else if (!strcmp(arg, "--name-status")) + options->output_format |= DIFF_FORMAT_NAME_STATUS; + else if (!strcmp(arg, "-s")) + options->output_format |= DIFF_FORMAT_NO_OUTPUT; else if (!prefixcmp(arg, "--stat")) { char *end; int width = options->stat_width; @@ -2242,99 +2256,89 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) options->stat_name_width = name_width; options->stat_width = width; } - else if (!strcmp(arg, "--check")) - options->output_format |= DIFF_FORMAT_CHECKDIFF; - else if (!strcmp(arg, "--summary")) - options->output_format |= DIFF_FORMAT_SUMMARY; - else if (!strcmp(arg, "--patch-with-stat")) { - options->output_format |= DIFF_FORMAT_PATCH | DIFF_FORMAT_DIFFSTAT; - } - else if (!strcmp(arg, "-z")) - options->line_termination = 0; - else if (!prefixcmp(arg, "-l")) - options->rename_limit = strtoul(arg+2, NULL, 10); - else if (!strcmp(arg, "--full-index")) - options->full_index = 1; - else if (!strcmp(arg, "--binary")) { - options->output_format |= DIFF_FORMAT_PATCH; - options->binary = 1; - } - else if (!strcmp(arg, "-a") || !strcmp(arg, "--text")) { - options->text = 1; - } - else if (!strcmp(arg, "--name-only")) - options->output_format |= DIFF_FORMAT_NAME; - else if (!strcmp(arg, "--name-status")) - options->output_format |= DIFF_FORMAT_NAME_STATUS; - else if (!strcmp(arg, "-R")) - options->reverse_diff = 1; - else if (!prefixcmp(arg, "-S")) - options->pickaxe = arg + 2; - else if (!strcmp(arg, "-s")) { - options->output_format |= DIFF_FORMAT_NO_OUTPUT; - } - else if (!prefixcmp(arg, "-O")) - options->orderfile = arg + 2; - else if (!prefixcmp(arg, "--diff-filter=")) - options->filter = arg + 14; - else if (!strcmp(arg, "--pickaxe-all")) - options->pickaxe_opts = DIFF_PICKAXE_ALL; - else if (!strcmp(arg, "--pickaxe-regex")) - options->pickaxe_opts = DIFF_PICKAXE_REGEX; + + /* renames options */ else if (!prefixcmp(arg, "-B")) { - if ((options->break_opt = - diff_scoreopt_parse(arg)) == -1) + if ((options->break_opt = diff_scoreopt_parse(arg)) == -1) return -1; } else if (!prefixcmp(arg, "-M")) { - if ((options->rename_score = - diff_scoreopt_parse(arg)) == -1) + if ((options->rename_score = diff_scoreopt_parse(arg)) == -1) return -1; options->detect_rename = DIFF_DETECT_RENAME; } else if (!prefixcmp(arg, "-C")) { if (options->detect_rename == DIFF_DETECT_COPY) - options->find_copies_harder = 1; - if ((options->rename_score = - diff_scoreopt_parse(arg)) == -1) + DIFF_OPT_SET(options, FIND_COPIES_HARDER); + if ((options->rename_score = diff_scoreopt_parse(arg)) == -1) return -1; options->detect_rename = DIFF_DETECT_COPY; } - else if (!strcmp(arg, "--find-copies-harder")) - options->find_copies_harder = 1; - else if (!strcmp(arg, "--follow")) - options->follow_renames = 1; - else if (!strcmp(arg, "--abbrev")) - options->abbrev = DEFAULT_ABBREV; - else if (!prefixcmp(arg, "--abbrev=")) { - options->abbrev = strtoul(arg + 9, NULL, 10); - if (options->abbrev < MINIMUM_ABBREV) - options->abbrev = MINIMUM_ABBREV; - else if (40 < options->abbrev) - options->abbrev = 40; - } - else if (!strcmp(arg, "--color")) - options->color_diff = 1; - else if (!strcmp(arg, "--no-color")) - options->color_diff = 0; + else if (!strcmp(arg, "--no-renames")) + options->detect_rename = 0; + + /* xdiff options */ else if (!strcmp(arg, "-w") || !strcmp(arg, "--ignore-all-space")) options->xdl_opts |= XDF_IGNORE_WHITESPACE; else if (!strcmp(arg, "-b") || !strcmp(arg, "--ignore-space-change")) options->xdl_opts |= XDF_IGNORE_WHITESPACE_CHANGE; else if (!strcmp(arg, "--ignore-space-at-eol")) options->xdl_opts |= XDF_IGNORE_WHITESPACE_AT_EOL; + + /* flags options */ + else if (!strcmp(arg, "--binary")) { + options->output_format |= DIFF_FORMAT_PATCH; + DIFF_OPT_SET(options, BINARY); + } + else if (!strcmp(arg, "--full-index")) + DIFF_OPT_SET(options, FULL_INDEX); + else if (!strcmp(arg, "-a") || !strcmp(arg, "--text")) + DIFF_OPT_SET(options, TEXT); + else if (!strcmp(arg, "-R")) + DIFF_OPT_SET(options, REVERSE_DIFF); + else if (!strcmp(arg, "--find-copies-harder")) + DIFF_OPT_SET(options, FIND_COPIES_HARDER); + else if (!strcmp(arg, "--follow")) + DIFF_OPT_SET(options, FOLLOW_RENAMES); + else if (!strcmp(arg, "--color")) + DIFF_OPT_SET(options, COLOR_DIFF); + else if (!strcmp(arg, "--no-color")) + DIFF_OPT_CLR(options, COLOR_DIFF); else if (!strcmp(arg, "--color-words")) - options->color_diff = options->color_diff_words = 1; - else if (!strcmp(arg, "--no-renames")) - options->detect_rename = 0; + options->flags |= DIFF_OPT_COLOR_DIFF | DIFF_OPT_COLOR_DIFF_WORDS; else if (!strcmp(arg, "--exit-code")) - options->exit_with_status = 1; + DIFF_OPT_SET(options, EXIT_WITH_STATUS); else if (!strcmp(arg, "--quiet")) - options->quiet = 1; + DIFF_OPT_SET(options, QUIET); else if (!strcmp(arg, "--ext-diff")) - options->allow_external = 1; + DIFF_OPT_SET(options, ALLOW_EXTERNAL); else if (!strcmp(arg, "--no-ext-diff")) - options->allow_external = 0; + DIFF_OPT_CLR(options, ALLOW_EXTERNAL); + + /* misc options */ + else if (!strcmp(arg, "-z")) + options->line_termination = 0; + else if (!prefixcmp(arg, "-l")) + options->rename_limit = strtoul(arg+2, NULL, 10); + else if (!prefixcmp(arg, "-S")) + options->pickaxe = arg + 2; + else if (!strcmp(arg, "--pickaxe-all")) + options->pickaxe_opts = DIFF_PICKAXE_ALL; + else if (!strcmp(arg, "--pickaxe-regex")) + options->pickaxe_opts = DIFF_PICKAXE_REGEX; + else if (!prefixcmp(arg, "-O")) + options->orderfile = arg + 2; + else if (!prefixcmp(arg, "--diff-filter=")) + options->filter = arg + 14; + else if (!strcmp(arg, "--abbrev")) + options->abbrev = DEFAULT_ABBREV; + else if (!prefixcmp(arg, "--abbrev=")) { + options->abbrev = strtoul(arg + 9, NULL, 10); + if (options->abbrev < MINIMUM_ABBREV) + options->abbrev = MINIMUM_ABBREV; + else if (40 < options->abbrev) + options->abbrev = 40; + } else return 0; return 1; @@ -2730,7 +2734,7 @@ static void diff_summary(struct diff_filepair *p) break; default: if (p->score) { - puts(" rewrite "); + fputs(" rewrite ", stdout); write_name_quoted(p->two->path, stdout, ' '); printf("(%d%%)\n", similarity_index(p)); } @@ -3089,7 +3093,7 @@ static void diffcore_skip_stat_unmatch(struct diff_options *diffopt) * to determine how many paths were dirty only * due to stat info mismatch. */ - if (!diffopt->no_index) + if (!DIFF_OPT_TST(diffopt, NO_INDEX)) diffopt->skip_stat_unmatch++; diff_free_filepair(p); } @@ -3100,10 +3104,10 @@ static void diffcore_skip_stat_unmatch(struct diff_options *diffopt) void diffcore_std(struct diff_options *options) { - if (options->quiet) + if (DIFF_OPT_TST(options, QUIET)) return; - if (options->skip_stat_unmatch && !options->find_copies_harder) + if (options->skip_stat_unmatch && !DIFF_OPT_TST(options, FIND_COPIES_HARDER)) diffcore_skip_stat_unmatch(options); if (options->break_opt != -1) diffcore_break(options->break_opt); @@ -3118,7 +3122,10 @@ void diffcore_std(struct diff_options *options) diff_resolve_rename_copy(); diffcore_apply_filter(options->filter); - options->has_changes = !!diff_queued_diff.nr; + if (diff_queued_diff.nr) + DIFF_OPT_SET(options, HAS_CHANGES); + else + DIFF_OPT_CLR(options, HAS_CHANGES); } @@ -3142,7 +3149,7 @@ void diff_addremove(struct diff_options *options, * Before the final output happens, they are pruned after * merged into rename/copy pairs as appropriate. */ - if (options->reverse_diff) + if (DIFF_OPT_TST(options, REVERSE_DIFF)) addremove = (addremove == '+' ? '-' : addremove == '-' ? '+' : addremove); @@ -3157,7 +3164,7 @@ void diff_addremove(struct diff_options *options, fill_filespec(two, sha1, mode); diff_queue(&diff_queued_diff, one, two); - options->has_changes = 1; + DIFF_OPT_SET(options, HAS_CHANGES); } void diff_change(struct diff_options *options, @@ -3169,7 +3176,7 @@ void diff_change(struct diff_options *options, char concatpath[PATH_MAX]; struct diff_filespec *one, *two; - if (options->reverse_diff) { + if (DIFF_OPT_TST(options, REVERSE_DIFF)) { unsigned tmp; const unsigned char *tmp_c; tmp = old_mode; old_mode = new_mode; new_mode = tmp; @@ -3183,7 +3190,7 @@ void diff_change(struct diff_options *options, fill_filespec(two, new_sha1, new_mode); diff_queue(&diff_queued_diff, one, two); - options->has_changes = 1; + DIFF_OPT_SET(options, HAS_CHANGES); } void diff_unmerge(struct diff_options *options, @@ -43,26 +43,32 @@ typedef void (*diff_format_fn_t)(struct diff_queue_struct *q, #define DIFF_FORMAT_CALLBACK 0x1000 +#define DIFF_OPT_RECURSIVE (1 << 0) +#define DIFF_OPT_TREE_IN_RECURSIVE (1 << 1) +#define DIFF_OPT_BINARY (1 << 2) +#define DIFF_OPT_TEXT (1 << 3) +#define DIFF_OPT_FULL_INDEX (1 << 4) +#define DIFF_OPT_SILENT_ON_REMOVE (1 << 5) +#define DIFF_OPT_FIND_COPIES_HARDER (1 << 6) +#define DIFF_OPT_FOLLOW_RENAMES (1 << 7) +#define DIFF_OPT_COLOR_DIFF (1 << 8) +#define DIFF_OPT_COLOR_DIFF_WORDS (1 << 9) +#define DIFF_OPT_HAS_CHANGES (1 << 10) +#define DIFF_OPT_QUIET (1 << 11) +#define DIFF_OPT_NO_INDEX (1 << 12) +#define DIFF_OPT_ALLOW_EXTERNAL (1 << 13) +#define DIFF_OPT_EXIT_WITH_STATUS (1 << 14) +#define DIFF_OPT_REVERSE_DIFF (1 << 15) +#define DIFF_OPT_TST(opts, flag) ((opts)->flags & DIFF_OPT_##flag) +#define DIFF_OPT_SET(opts, flag) ((opts)->flags |= DIFF_OPT_##flag) +#define DIFF_OPT_CLR(opts, flag) ((opts)->flags &= ~DIFF_OPT_##flag) + struct diff_options { const char *filter; const char *orderfile; const char *pickaxe; const char *single_follow; - unsigned recursive:1, - tree_in_recursive:1, - binary:1, - text:1, - full_index:1, - silent_on_remove:1, - find_copies_harder:1, - follow_renames:1, - color_diff:1, - color_diff_words:1, - has_changes:1, - quiet:1, - no_index:1, - allow_external:1, - exit_with_status:1; + unsigned flags; int context; int break_opt; int detect_rename; @@ -71,7 +77,6 @@ struct diff_options { int output_format; int pickaxe_opts; int rename_score; - int reverse_diff; int rename_limit; int setup; int abbrev; @@ -105,6 +110,9 @@ enum color_diff { DIFF_WHITESPACE = 7, }; const char *diff_get_color(int diff_use_color, enum color_diff ix); +#define diff_get_color_opt(o, ix) \ + diff_get_color(DIFF_OPT_TST((o), COLOR_DIFF), ix) + extern const char mime_boundary_leader[]; @@ -224,7 +232,11 @@ extern void diff_flush(struct diff_options*); extern const char *diff_unique_abbrev(const unsigned char *, int); -extern int run_diff_files(struct rev_info *revs, int silent_on_removed); +/* do not report anything on removed paths */ +#define DIFF_SILENT_ON_REMOVED 01 +/* report racily-clean paths as modified */ +#define DIFF_RACY_IS_MODIFIED 02 +extern int run_diff_files(struct rev_info *revs, unsigned int option); extern int setup_diff_no_index(struct rev_info *revs, int argc, const char ** argv, int nongit, const char *prefix); extern int run_diff_files_cmd(struct rev_info *revs, int argc, const char **argv); diff --git a/diffcore-break.c b/diffcore-break.c index c71a22621a..31cdcfe8bc 100644 --- a/diffcore-break.c +++ b/diffcore-break.c @@ -52,8 +52,10 @@ static int should_break(struct diff_filespec *src, * is the default. */ - if (!S_ISREG(src->mode) || !S_ISREG(dst->mode)) - return 0; /* leave symlink rename alone */ + if (S_ISREG(src->mode) != S_ISREG(dst->mode)) { + *merge_score_p = (int)MAX_SCORE; + return 1; /* even their types are different */ + } if (src->sha1_valid && dst->sha1_valid && !hashcmp(src->sha1, dst->sha1)) @@ -168,11 +170,13 @@ void diffcore_break(int break_score) struct diff_filepair *p = q->queue[i]; int score; - /* We deal only with in-place edit of non directory. + /* + * We deal only with in-place edit of blobs. * We do not break anything else. */ if (DIFF_FILE_VALID(p->one) && DIFF_FILE_VALID(p->two) && - !S_ISDIR(p->one->mode) && !S_ISDIR(p->two->mode) && + object_type(p->one->mode) == OBJ_BLOB && + object_type(p->two->mode) == OBJ_BLOB && !strcmp(p->one->path, p->two->path)) { if (should_break(p->one, p->two, break_score, &score)) { diff --git a/diffcore-rename.c b/diffcore-rename.c index f9ebea5640..3d377251be 100644 --- a/diffcore-rename.c +++ b/diffcore-rename.c @@ -244,28 +244,35 @@ static int find_identical_files(struct file_similarity *src, * Walk over all the destinations ... */ do { - struct diff_filespec *one = dst->filespec; + struct diff_filespec *target = dst->filespec; struct file_similarity *p, *best; - int i = 100; + int i = 100, best_score = -1; /* * .. to find the best source match */ best = NULL; for (p = src; p; p = p->next) { - struct diff_filespec *two = p->filespec; + int score; + struct diff_filespec *source = p->filespec; /* False hash collission? */ - if (hashcmp(one->sha1, two->sha1)) + if (hashcmp(source->sha1, target->sha1)) continue; /* Non-regular files? If so, the modes must match! */ - if (!S_ISREG(one->mode) || !S_ISREG(two->mode)) { - if (one->mode != two->mode) + if (!S_ISREG(source->mode) || !S_ISREG(target->mode)) { + if (source->mode != target->mode) continue; } - best = p; - if (basename_same(one, two)) - break; + /* Give higher scores to sources that haven't been used already */ + score = !source->rename_used; + score += basename_same(source, target); + if (score > best_score) { + best = p; + best_score = score; + if (score == 2) + break; + } /* Too many identical alternatives? Pick one */ if (!--i) @@ -490,6 +497,19 @@ void diffcore_rename(struct diff_options *options) qsort(mx, num_create * num_src, sizeof(*mx), score_compare); for (i = 0; i < num_create * num_src; i++) { struct diff_rename_dst *dst = &rename_dst[mx[i].dst]; + struct diff_filespec *src; + if (dst->pair) + continue; /* already done, either exact or fuzzy. */ + if (mx[i].score < minimum_score) + break; /* there is no more usable pair. */ + src = rename_src[mx[i].src].one; + if (src->rename_used) + continue; + record_rename_pair(mx[i].dst, mx[i].src, mx[i].score); + rename_count++; + } + for (i = 0; i < num_create * num_src; i++) { + struct diff_rename_dst *dst = &rename_dst[mx[i].dst]; if (dst->pair) continue; /* already done, either exact or fuzzy. */ if (mx[i].score < minimum_score) @@ -144,17 +144,14 @@ void add_exclude(const char *string, const char *base, x->flags |= EXC_FLAG_NOWILDCARD; if (*string == '*' && no_wildcard(string+1)) x->flags |= EXC_FLAG_ENDSWITH; - if (which->nr == which->alloc) { - which->alloc = alloc_nr(which->alloc); - which->excludes = xrealloc(which->excludes, - which->alloc * sizeof(x)); - } + ALLOC_GROW(which->excludes, which->nr + 1, which->alloc); which->excludes[which->nr++] = x; } static int add_excludes_from_file_1(const char *fname, const char *base, int baselen, + char **buf_p, struct exclude_list *which) { struct stat st; @@ -175,6 +172,8 @@ static int add_excludes_from_file_1(const char *fname, goto err; close(fd); + if (buf_p) + *buf_p = buf; buf[size++] = '\n'; entry = buf; for (i = 0; i < size; i++) { @@ -196,31 +195,63 @@ static int add_excludes_from_file_1(const char *fname, void add_excludes_from_file(struct dir_struct *dir, const char *fname) { - if (add_excludes_from_file_1(fname, "", 0, + if (add_excludes_from_file_1(fname, "", 0, NULL, &dir->exclude_list[EXC_FILE]) < 0) die("cannot use %s as an exclude file", fname); } -int push_exclude_per_directory(struct dir_struct *dir, const char *base, int baselen) +static void prep_exclude(struct dir_struct *dir, const char *base, int baselen) { - char exclude_file[PATH_MAX]; - struct exclude_list *el = &dir->exclude_list[EXC_DIRS]; - int current_nr = el->nr; - - if (dir->exclude_per_dir) { - memcpy(exclude_file, base, baselen); - strcpy(exclude_file + baselen, dir->exclude_per_dir); - add_excludes_from_file_1(exclude_file, base, baselen, el); + struct exclude_list *el; + struct exclude_stack *stk = NULL; + int current; + + if ((!dir->exclude_per_dir) || + (baselen + strlen(dir->exclude_per_dir) >= PATH_MAX)) + return; /* too long a path -- ignore */ + + /* Pop the ones that are not the prefix of the path being checked. */ + el = &dir->exclude_list[EXC_DIRS]; + while ((stk = dir->exclude_stack) != NULL) { + if (stk->baselen <= baselen && + !strncmp(dir->basebuf, base, stk->baselen)) + break; + dir->exclude_stack = stk->prev; + while (stk->exclude_ix < el->nr) + free(el->excludes[--el->nr]); + free(stk->filebuf); + free(stk); } - return current_nr; -} -void pop_exclude_per_directory(struct dir_struct *dir, int stk) -{ - struct exclude_list *el = &dir->exclude_list[EXC_DIRS]; + /* Read from the parent directories and push them down. */ + current = stk ? stk->baselen : -1; + while (current < baselen) { + struct exclude_stack *stk = xcalloc(1, sizeof(*stk)); + const char *cp; - while (stk < el->nr) - free(el->excludes[--el->nr]); + if (current < 0) { + cp = base; + current = 0; + } + else { + cp = strchr(base + current + 1, '/'); + if (!cp) + die("oops in prep_exclude"); + cp++; + } + stk->prev = dir->exclude_stack; + stk->baselen = cp - base; + stk->exclude_ix = el->nr; + memcpy(dir->basebuf + current, base + current, + stk->baselen - current); + strcpy(dir->basebuf + stk->baselen, dir->exclude_per_dir); + add_excludes_from_file_1(dir->basebuf, + dir->basebuf, stk->baselen, + &stk->filebuf, el); + dir->exclude_stack = stk; + current = stk->baselen; + } + dir->basebuf[baselen] = '\0'; } /* Scan the list and let the last match determines the fate. @@ -287,6 +318,7 @@ int excluded(struct dir_struct *dir, const char *pathname) const char *basename = strrchr(pathname, '/'); basename = (basename) ? basename+1 : pathname; + prep_exclude(dir, pathname, basename-pathname); for (st = EXC_CMDL; st <= EXC_FILE; st++) { switch (excluded_1(pathname, pathlen, basename, &dir->exclude_list[st])) { case 0: @@ -298,7 +330,8 @@ int excluded(struct dir_struct *dir, const char *pathname) return 0; } -static struct dir_entry *dir_entry_new(const char *pathname, int len) { +static struct dir_entry *dir_entry_new(const char *pathname, int len) +{ struct dir_entry *ent; ent = xmalloc(sizeof(*ent) + len + 1); @@ -503,13 +536,10 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co int contents = 0; if (fdir) { - int exclude_stk; struct dirent *de; char fullname[PATH_MAX + 1]; memcpy(fullname, base, baselen); - exclude_stk = push_exclude_per_directory(dir, base, baselen); - while ((de = readdir(fdir)) != NULL) { int len, dtype; int exclude; @@ -583,8 +613,6 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co } exit_early: closedir(fdir); - - pop_exclude_per_directory(dir, exclude_stk); } return contents; @@ -654,32 +682,6 @@ int read_directory(struct dir_struct *dir, const char *path, const char *base, i { struct path_simplify *simplify = create_simplify(pathspec); - /* - * Make sure to do the per-directory exclude for all the - * directories leading up to our base. - */ - if (baselen) { - if (dir->exclude_per_dir) { - char *p, *pp = xmalloc(baselen+1); - memcpy(pp, base, baselen+1); - p = pp; - while (1) { - char save = *p; - *p = 0; - push_exclude_per_directory(dir, pp, p-pp); - *p++ = save; - if (!save) - break; - p = strchr(p, '/'); - if (p) - p++; - else - p = pp + baselen; - } - free(pp); - } - } - read_directory_recursive(dir, path, base, baselen, 0, simplify); free_simplify(simplify); qsort(dir->entries, dir->nr, sizeof(struct dir_entry *), cmp_name); @@ -687,11 +689,10 @@ int read_directory(struct dir_struct *dir, const char *path, const char *base, i return dir->nr; } -int -file_exists(const char *f) +int file_exists(const char *f) { - struct stat sb; - return stat(f, &sb) == 0; + struct stat sb; + return lstat(f, &sb) == 0; } /* @@ -777,3 +778,15 @@ int remove_dir_recursively(struct strbuf *path, int only_empty) ret = rmdir(path->buf); return ret; } + +void setup_standard_excludes(struct dir_struct *dir) +{ + const char *path; + + dir->exclude_per_dir = ".gitignore"; + path = git_path("info/exclude"); + if (!access(path, R_OK)) + add_excludes_from_file(dir, path); + if (excludes_file && !access(excludes_file, R_OK)) + add_excludes_from_file(dir, excludes_file); +} @@ -1,17 +1,6 @@ #ifndef DIR_H #define DIR_H -/* - * We maintain three exclude pattern lists: - * EXC_CMDL lists patterns explicitly given on the command line. - * EXC_DIRS lists patterns obtained from per-directory ignore files. - * EXC_FILE lists patterns from fallback ignore files. - */ -#define EXC_CMDL 0 -#define EXC_DIRS 1 -#define EXC_FILE 2 - - struct dir_entry { unsigned int len; char name[FLEX_ARRAY]; /* more */ @@ -34,6 +23,13 @@ struct exclude_list { } **excludes; }; +struct exclude_stack { + struct exclude_stack *prev; + char *filebuf; + int baselen; + int exclude_ix; +}; + struct dir_struct { int nr, alloc; int ignored_nr, ignored_alloc; @@ -48,6 +44,18 @@ struct dir_struct { /* Exclude info */ const char *exclude_per_dir; struct exclude_list exclude_list[3]; + /* + * We maintain three exclude pattern lists: + * EXC_CMDL lists patterns explicitly given on the command line. + * EXC_DIRS lists patterns obtained from per-directory ignore files. + * EXC_FILE lists patterns from fallback ignore files. + */ +#define EXC_CMDL 0 +#define EXC_DIRS 1 +#define EXC_FILE 2 + + struct exclude_stack *exclude_stack; + char basebuf[PATH_MAX]; }; extern int common_prefix(const char **pathspec); @@ -58,8 +66,6 @@ extern int common_prefix(const char **pathspec); extern int match_pathspec(const char **pathspec, const char *name, int namelen, int prefix, char *seen); extern int read_directory(struct dir_struct *, const char *path, const char *base, int baselen, const char **pathspec); -extern int push_exclude_per_directory(struct dir_struct *, const char *, int); -extern void pop_exclude_per_directory(struct dir_struct *, int); extern int excluded(struct dir_struct *, const char *); extern void add_excludes_from_file(struct dir_struct *, const char *fname); @@ -71,6 +77,7 @@ extern struct dir_entry *dir_add_name(struct dir_struct *dir, const char *pathna extern char *get_relative_cwd(char *buffer, int size, const char *dir); extern int is_inside_dir(const char *dir); +extern void setup_standard_excludes(struct dir_struct *dir); extern int remove_dir_recursively(struct strbuf *path, int only_empty); #endif @@ -203,7 +203,7 @@ int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *t strcpy(path + len, ce->name); if (!lstat(path, &st)) { - unsigned changed = ce_match_stat(ce, &st, 1); + unsigned changed = ce_match_stat(ce, &st, CE_MATCH_IGNORE_VALID); if (!changed) return 0; if (!state->force) { diff --git a/environment.c b/environment.c index 2fbbc8e430..f3e3d4138d 100644 --- a/environment.c +++ b/environment.c @@ -34,6 +34,7 @@ char *pager_program; int pager_in_use; int pager_use_color = 1; char *editor_program; +char *excludes_file; int auto_crlf = 0; /* 1: both ways, -1: only when adding git objects */ unsigned whitespace_rule_cfg = WS_DEFAULT_RULE; diff --git a/exec_cmd.c b/exec_cmd.c index 2d0a758512..e189caca62 100644 --- a/exec_cmd.c +++ b/exec_cmd.c @@ -80,7 +80,7 @@ int execv_git_cmd(const char **argv) tmp = argv[0]; argv[0] = cmd.buf; - trace_argv_printf(argv, -1, "trace: exec:"); + trace_argv_printf(argv, "trace: exec:"); /* execvp() can only ever return if it fails */ execvp(cmd.buf, (char **)argv); diff --git a/fast-import.c b/fast-import.c index f93d7d6c9b..98c2bd5359 100644 --- a/fast-import.c +++ b/fast-import.c @@ -153,13 +153,16 @@ Format of STDIN stream: #define PACK_ID_BITS 16 #define MAX_PACK_ID ((1<<PACK_ID_BITS)-1) +#define DEPTH_BITS 13 +#define MAX_DEPTH ((1<<DEPTH_BITS)-1) struct object_entry { struct object_entry *next; uint32_t offset; - unsigned type : TYPE_BITS; - unsigned pack_id : PACK_ID_BITS; + uint32_t type : TYPE_BITS, + pack_id : PACK_ID_BITS, + depth : DEPTH_BITS; unsigned char sha1[20]; }; @@ -1083,7 +1086,7 @@ static int store_object( unsigned pos = sizeof(hdr) - 1; delta_count_by_type[type]++; - last->depth++; + e->depth = last->depth + 1; hdrlen = encode_header(OBJ_OFS_DELTA, deltalen, hdr); write_or_die(pack_data->pack_fd, hdr, hdrlen); @@ -1095,8 +1098,7 @@ static int store_object( write_or_die(pack_data->pack_fd, hdr + pos, sizeof(hdr) - pos); pack_size += sizeof(hdr) - pos; } else { - if (last) - last->depth = 0; + e->depth = 0; hdrlen = encode_header(type, dat->len, hdr); write_or_die(pack_data->pack_fd, hdr, hdrlen); pack_size += hdrlen; @@ -1114,6 +1116,7 @@ static int store_object( strbuf_swap(&last->data, dat); } last->offset = e->offset; + last->depth = e->depth; } return 0; } @@ -1160,7 +1163,7 @@ static void load_tree(struct tree_entry *root) if (myoe && myoe->pack_id != MAX_PACK_ID) { if (myoe->type != OBJ_TREE) die("Not a tree: %s", sha1_to_hex(sha1)); - t->delta_depth = 0; + t->delta_depth = myoe->depth; buf = gfi_unpack_entry(myoe, &size); } else { enum object_type type; @@ -2289,8 +2292,11 @@ int main(int argc, const char **argv) } else if (!prefixcmp(a, "--max-pack-size=")) max_packsize = strtoumax(a + 16, NULL, 0) * 1024 * 1024; - else if (!prefixcmp(a, "--depth=")) + else if (!prefixcmp(a, "--depth=")) { max_depth = strtoul(a + 8, NULL, 0); + if (max_depth > MAX_DEPTH) + die("--depth cannot exceed %u", MAX_DEPTH); + } else if (!prefixcmp(a, "--active-branches=")) max_active_branches = strtoul(a + 18, NULL, 0); else if (!prefixcmp(a, "--import-marks=")) diff --git a/generate-cmdlist.sh b/generate-cmdlist.sh index 17df47b950..a2913c2a2c 100755 --- a/generate-cmdlist.sh +++ b/generate-cmdlist.sh @@ -9,35 +9,8 @@ struct cmdname_help static struct cmdname_help common_cmds[] = {" -sort <<\EOF | -add -apply -archive -bisect -branch -checkout -cherry-pick -clone -commit -diff -fetch -grep -init -log -merge -mv -prune -pull -push -rebase -reset -revert -rm -show -show-branch -status -tag -EOF +sed -n -e 's/^git-\([^ ]*\)[ ].* common.*/\1/p' command-list.txt | +sort | while read cmd do sed -n ' diff --git a/git-add--interactive.perl b/git-add--interactive.perl index ac598f88e6..0cdd80073b 100755 --- a/git-add--interactive.perl +++ b/git-add--interactive.perl @@ -1,6 +1,58 @@ #!/usr/bin/perl -w use strict; +use Git; + +# Prompt colors: +my ($prompt_color, $header_color, $help_color, $normal_color); +# Diff colors: +my ($new_color, $old_color, $fraginfo_color, $metainfo_color, $whitespace_color); + +my ($use_color, $diff_use_color); +my $repo = Git->repository(); + +$use_color = $repo->get_colorbool('color.interactive'); + +if ($use_color) { + # Set interactive colors: + + # Grab the 3 main colors in git color string format, with sane + # (visible) defaults: + $prompt_color = $repo->get_color("color.interactive.prompt", "bold blue"); + $header_color = $repo->get_color("color.interactive.header", "bold"); + $help_color = $repo->get_color("color.interactive.help", "red bold"); + $normal_color = $repo->get_color("", "reset"); + + # Do we also set diff colors? + $diff_use_color = $repo->get_colorbool('color.diff'); + if ($diff_use_color) { + $new_color = $repo->get_color("color.diff.new", "green"); + $old_color = $repo->get_color("color.diff.old", "red"); + $fraginfo_color = $repo->get_color("color.diff.frag", "cyan"); + $metainfo_color = $repo->get_color("color.diff.meta", "bold"); + $whitespace_color = $repo->get_color("color.diff.whitespace", "normal red"); + } +} + +sub colored { + my $color = shift; + my $string = join("", @_); + + if ($use_color) { + # Put a color code at the beginning of each line, a reset at the end + # color after newlines that are not at the end of the string + $string =~ s/(\n+)(.)/$1$color$2/g; + # reset before newlines + $string =~ s/(\n+)/$normal_color$1/g; + # codes at beginning and end (if necessary): + $string =~ s/^/$color/; + $string =~ s/$/$normal_color/ unless $string =~ /\n$/; + } + return $string; +} + +# command line options +my $patch_mode; sub run_cmd_pipe { if ($^O eq 'MSWin32') { @@ -37,17 +89,13 @@ sub list_untracked { chomp $_; $_; } - run_cmd_pipe(qw(git ls-files --others - --exclude-per-directory=.gitignore), - "--exclude-from=$GIT_DIR/info/exclude", - '--', @_); + run_cmd_pipe(qw(git ls-files --others --exclude-standard --), @ARGV); } my $status_fmt = '%12s %12s %s'; my $status_head = sprintf($status_fmt, 'staged', 'unstaged', 'path'); # Returns list of hashes, contents of each of which are: -# PRINT: print message # VALUE: pathname # BINARY: is a binary path # INDEX: is index different from HEAD? @@ -59,9 +107,17 @@ sub list_modified { my ($only) = @_; my (%data, @return); my ($add, $del, $adddel, $file); + my @tracked = (); + + if (@ARGV) { + @tracked = map { + chomp $_; $_; + } run_cmd_pipe(qw(git ls-files --exclude-standard --), @ARGV); + return if (!@tracked); + } for (run_cmd_pipe(qw(git diff-index --cached - --numstat --summary HEAD))) { + --numstat --summary HEAD --), @tracked)) { if (($add, $del, $file) = /^([-\d]+) ([-\d]+) (.*)/) { my ($change, $bin); @@ -84,7 +140,7 @@ sub list_modified { } } - for (run_cmd_pipe(qw(git diff-files --numstat --summary))) { + for (run_cmd_pipe(qw(git diff-files --numstat --summary --), @tracked)) { if (($add, $del, $file) = /^([-\d]+) ([-\d]+) (.*)/) { if (!exists $data{$file}) { @@ -125,8 +181,6 @@ sub list_modified { } push @return, +{ VALUE => $_, - PRINT => (sprintf $status_fmt, - $it->{INDEX}, $it->{FILE}, $_), %$it, }; } @@ -162,10 +216,106 @@ sub find_unique { return $found; } +# inserts string into trie and updates count for each character +sub update_trie { + my ($trie, $string) = @_; + foreach (split //, $string) { + $trie = $trie->{$_} ||= {COUNT => 0}; + $trie->{COUNT}++; + } +} + +# returns an array of tuples (prefix, remainder) +sub find_unique_prefixes { + my @stuff = @_; + my @return = (); + + # any single prefix exceeding the soft limit is omitted + # if any prefix exceeds the hard limit all are omitted + # 0 indicates no limit + my $soft_limit = 0; + my $hard_limit = 3; + + # build a trie modelling all possible options + my %trie; + foreach my $print (@stuff) { + if ((ref $print) eq 'ARRAY') { + $print = $print->[0]; + } + elsif ((ref $print) eq 'HASH') { + $print = $print->{VALUE}; + } + update_trie(\%trie, $print); + push @return, $print; + } + + # use the trie to find the unique prefixes + for (my $i = 0; $i < @return; $i++) { + my $ret = $return[$i]; + my @letters = split //, $ret; + my %search = %trie; + my ($prefix, $remainder); + my $j; + for ($j = 0; $j < @letters; $j++) { + my $letter = $letters[$j]; + if ($search{$letter}{COUNT} == 1) { + $prefix = substr $ret, 0, $j + 1; + $remainder = substr $ret, $j + 1; + last; + } + else { + my $prefix = substr $ret, 0, $j; + return () + if ($hard_limit && $j + 1 > $hard_limit); + } + %search = %{$search{$letter}}; + } + if ($soft_limit && $j + 1 > $soft_limit) { + $prefix = undef; + $remainder = $ret; + } + $return[$i] = [$prefix, $remainder]; + } + return @return; +} + +# filters out prefixes which have special meaning to list_and_choose() +sub is_valid_prefix { + my $prefix = shift; + return (defined $prefix) && + !($prefix =~ /[\s,]/) && # separators + !($prefix =~ /^-/) && # deselection + !($prefix =~ /^\d+/) && # selection + ($prefix ne '*') && # "all" wildcard + ($prefix ne '?'); # prompt help +} + +# given a prefix/remainder tuple return a string with the prefix highlighted +# for now use square brackets; later might use ANSI colors (underline, bold) +sub highlight_prefix { + my $prefix = shift; + my $remainder = shift; + + if (!defined $prefix) { + return $remainder; + } + + if (!is_valid_prefix($prefix)) { + return "$prefix$remainder"; + } + + if (!$use_color) { + return "[$prefix]$remainder"; + } + + return "$prompt_color$prefix$normal_color$remainder"; +} + sub list_and_choose { my ($opts, @stuff) = @_; my (@chosen, @return); my $i; + my @prefixes = find_unique_prefixes(@stuff) unless $opts->{LIST_ONLY}; TOPLOOP: while (1) { @@ -175,18 +325,26 @@ sub list_and_choose { if (!$opts->{LIST_FLAT}) { print " "; } - print "$opts->{HEADER}\n"; + print colored $header_color, "$opts->{HEADER}\n"; } for ($i = 0; $i < @stuff; $i++) { my $chosen = $chosen[$i] ? '*' : ' '; my $print = $stuff[$i]; - if (ref $print) { - if ((ref $print) eq 'ARRAY') { - $print = $print->[0]; - } - else { - $print = $print->{PRINT}; - } + my $ref = ref $print; + my $highlighted = highlight_prefix(@{$prefixes[$i]}) + if @prefixes; + if ($ref eq 'ARRAY') { + $print = $highlighted || $print->[0]; + } + elsif ($ref eq 'HASH') { + my $value = $highlighted || $print->{VALUE}; + $print = sprintf($status_fmt, + $print->{INDEX}, + $print->{FILE}, + $value); + } + else { + $print = $highlighted || $print; } printf("%s%2d: %s", $chosen, $i+1, $print); if (($opts->{LIST_FLAT}) && @@ -205,7 +363,7 @@ sub list_and_choose { return if ($opts->{LIST_ONLY}); - print $opts->{PROMPT}; + print colored $prompt_color, $opts->{PROMPT}; if ($opts->{SINGLETON}) { print "> "; } @@ -220,6 +378,12 @@ sub list_and_choose { } chomp $line; last if $line eq ''; + if ($line eq '?') { + $opts->{SINGLETON} ? + singleton_prompt_help_cmd() : + prompt_help_cmd(); + next TOPLOOP; + } for my $choice (split(/[\s,]+/, $line)) { my $choose = 1; my ($bottom, $top); @@ -255,7 +419,7 @@ sub list_and_choose { $chosen[$i] = $choose; } } - last if ($opts->{IMMEDIATE}); + last if ($opts->{IMMEDIATE} || $line eq '*'); } for ($i = 0; $i < @stuff; $i++) { if ($chosen[$i]) { @@ -265,6 +429,28 @@ sub list_and_choose { return @return; } +sub singleton_prompt_help_cmd { + print colored $help_color, <<\EOF ; +Prompt help: +1 - select a numbered item +foo - select item based on unique prefix + - (empty) select nothing +EOF +} + +sub prompt_help_cmd { + print colored $help_color, <<\EOF ; +Prompt help: +1 - select a single item +3-5 - select a range of items +2-3,6-9 - select multiple ranges +foo - select item based on unique prefix +-... - unselect specified items +* - choose all items + - (empty) finish selecting +EOF +} + sub status_cmd { list_and_choose({ LIST_ONLY => 1, HEADER => $status_head }, list_modified()); @@ -339,13 +525,19 @@ sub add_untracked_cmd { sub parse_diff { my ($path) = @_; my @diff = run_cmd_pipe(qw(git diff-files -p --), $path); - my (@hunk) = { TEXT => [] }; + my @colored = (); + if ($diff_use_color) { + @colored = run_cmd_pipe(qw(git diff-files -p --color --), $path); + } + my (@hunk) = { TEXT => [], DISPLAY => [] }; - for (@diff) { - if (/^@@ /) { - push @hunk, { TEXT => [] }; + for (my $i = 0; $i < @diff; $i++) { + if ($diff[$i] =~ /^@@ /) { + push @hunk, { TEXT => [], DISPLAY => [] }; } - push @{$hunk[-1]{TEXT}}, $_; + push @{$hunk[-1]{TEXT}}, $diff[$i]; + push @{$hunk[-1]{DISPLAY}}, + ($diff_use_color ? $colored[$i] : $diff[$i]); } return @hunk; } @@ -367,9 +559,11 @@ sub parse_hunk_header { } sub split_hunk { - my ($text) = @_; + my ($text, $display) = @_; my @split = (); - + if (!defined $display) { + $display = $text; + } # If there are context lines in the middle of a hunk, # it can be split, but we would need to take care of # overlaps later. @@ -383,16 +577,19 @@ sub split_hunk { my $i = $hunk_start - 1; my $this = +{ TEXT => [], + DISPLAY => [], OLD => $o_ofs, NEW => $n_ofs, OCNT => 0, NCNT => 0, ADDDEL => 0, POSTCTX => 0, + USE => undef, }; while (++$i < @$text) { my $line = $text->[$i]; + my $display = $display->[$i]; if ($line =~ /^ /) { if ($this->{ADDDEL} && !defined $next_hunk_start) { @@ -404,6 +601,7 @@ sub split_hunk { $next_hunk_start = $i; } push @{$this->{TEXT}}, $line; + push @{$this->{DISPLAY}}, $display; $this->{OCNT}++; $this->{NCNT}++; if (defined $next_hunk_start) { @@ -426,6 +624,7 @@ sub split_hunk { redo OUTER; } push @{$this->{TEXT}}, $line; + push @{$this->{DISPLAY}}, $display; $this->{ADDDEL}++; if ($line =~ /^-/) { $this->{OCNT}++; @@ -450,9 +649,14 @@ sub split_hunk { " +$n_ofs" . (($n_cnt != 1) ? ",$n_cnt" : '') . " @@\n"); + my $display_head = $head; unshift @{$hunk->{TEXT}}, $head; + if ($diff_use_color) { + $display_head = colored($fraginfo_color, $head); + } + unshift @{$hunk->{DISPLAY}}, $display_head; } - return map { $_->{TEXT} } @split; + return @split; } sub find_last_o_ctx { @@ -544,35 +748,46 @@ sub coalesce_overlapping_hunks { } sub help_patch_cmd { - print <<\EOF ; + print colored $help_color, <<\EOF ; y - stage this hunk n - do not stage this hunk -a - stage this and all the remaining hunks -d - do not stage this hunk nor any of the remaining hunks +a - stage this and all the remaining hunks in the file +d - do not stage this hunk nor any of the remaining hunks in the file j - leave this hunk undecided, see next undecided hunk J - leave this hunk undecided, see next hunk k - leave this hunk undecided, see previous undecided hunk K - leave this hunk undecided, see previous hunk s - split the current hunk into smaller hunks +? - print help EOF } sub patch_update_cmd { - my @mods = list_modified('file-only'); - @mods = grep { !($_->{BINARY}) } @mods; - return if (!@mods); + my @mods = grep { !($_->{BINARY}) } list_modified('file-only'); + my @them; - my ($it) = list_and_choose({ PROMPT => 'Patch update', - SINGLETON => 1, - IMMEDIATE => 1, - HEADER => $status_head, }, - @mods); - return if (!$it); + if (!@mods) { + print STDERR "No changes.\n"; + return 0; + } + if ($patch_mode) { + @them = @mods; + } + else { + @them = list_and_choose({ PROMPT => 'Patch update', + HEADER => $status_head, }, + @mods); + } + for (@them) { + patch_update_file($_->{VALUE}); + } +} +sub patch_update_file { my ($ix, $num); - my $path = $it->{VALUE}; + my $path = shift; my ($head, @hunk) = parse_diff($path); - for (@{$head->{TEXT}}) { + for (@{$head->{DISPLAY}}) { print; } $num = scalar @hunk; @@ -616,10 +831,10 @@ sub patch_update_cmd { if (hunk_splittable($hunk[$ix]{TEXT})) { $other .= '/s'; } - for (@{$hunk[$ix]{TEXT}}) { + for (@{$hunk[$ix]{DISPLAY}}) { print; } - print "Stage this hunk [y/n/a/d$other/?]? "; + print colored $prompt_color, "Stage this hunk [y/n/a/d$other/?]? "; my $line = <STDIN>; if ($line) { if ($line =~ /^y/i) { @@ -671,14 +886,12 @@ sub patch_update_cmd { next; } elsif ($other =~ /s/ && $line =~ /^s/) { - my @split = split_hunk($hunk[$ix]{TEXT}); + my @split = split_hunk($hunk[$ix]{TEXT}, $hunk[$ix]{DISPLAY}); if (1 < @split) { - print "Split into ", + print colored $header_color, "Split into ", scalar(@split), " hunks.\n"; } - splice(@hunk, $ix, 1, - map { +{ TEXT => $_, USE => undef } } - @split); + splice (@hunk, $ix, 1, @split); $num = scalar @hunk; next; } @@ -756,8 +969,7 @@ sub diff_cmd { HEADER => $status_head, }, @mods); return if (!@them); - system(qw(git diff-index -p --cached HEAD --), - map { $_->{VALUE} } @them); + system(qw(git diff -p --cached HEAD --), map { $_->{VALUE} } @them); } sub quit_cmd { @@ -766,7 +978,7 @@ sub quit_cmd { } sub help_cmd { - print <<\EOF ; + print colored $help_color, <<\EOF ; status - show paths with changes update - add working tree state to the staged set of changes revert - revert staged set of changes back to the HEAD version @@ -776,6 +988,20 @@ add untracked - add contents of untracked files to the staged set of changes EOF } +sub process_args { + return unless @ARGV; + my $arg = shift @ARGV; + if ($arg eq "--patch") { + $patch_mode = 1; + $arg = shift @ARGV or die "missing --"; + die "invalid argument $arg, expecting --" + unless $arg eq "--"; + } + elsif ($arg ne "--") { + die "invalid argument $arg, expecting --"; + } +} + sub main_loop { my @cmd = ([ 'status', \&status_cmd, ], [ 'update', \&update_cmd, ], @@ -804,6 +1030,12 @@ sub main_loop { } } +process_args(); refresh(); -status_cmd(); -main_loop(); +if ($patch_mode) { + patch_update_cmd(); +} +else { + status_cmd(); + main_loop(); +} @@ -2,11 +2,26 @@ # # Copyright (c) 2005, 2006 Junio C Hamano -USAGE='[--signoff] [--dotest=<dir>] [--keep] [--utf8 | --no-utf8] - [--3way] [--interactive] [--binary] - [--whitespace=<option>] [-C<n>] [-p<n>] - <mbox>|<Maildir>... - or, when resuming [--skip | --resolved]' +OPTIONS_KEEPDASHDASH= +OPTIONS_SPEC="\ +git-am [options] <mbox>|<Maildir>... +git-am [options] --resolved +git-am [options] --skip +-- +d,dotest= use <dir> and not .dotest +i,interactive run interactively +b,binary pass --allo-binary-replacement to git-apply +3,3way allow fall back on 3way merging if needed +s,signoff add a Signed-off-by line to the commit message +u,utf8 recode into utf8 (default) +k,keep pass -k flagg to git-mailinfo +whitespace= pass it through git-apply +C= pass it through git-apply +p= pass it through git-apply +resolvemsg= override error message when patch failure occurs +r,resolved to be used after a patch failure +skip skip the current patch" + . git-sh-setup set_reflog_action am require_work_tree @@ -102,6 +117,10 @@ It does not apply to blobs recorded in its index." unset GITHEAD_$his_tree } +reread_subject () { + git stripspace <"$1" | sed -e 1q +} + prec=4 dotest=.dotest sign= utf8=t keep= skip= interactive= resolved= binary= resolvemsg= resume= @@ -110,49 +129,38 @@ git_apply_opt= while test $# != 0 do case "$1" in - -d=*|--d=*|--do=*|--dot=*|--dote=*|--dotes=*|--dotest=*) - dotest=`expr "z$1" : 'z-[^=]*=\(.*\)'`; shift ;; - -d|--d|--do|--dot|--dote|--dotes|--dotest) - case "$#" in 1) usage ;; esac; shift - dotest="$1"; shift;; - - -i|--i|--in|--int|--inte|--inter|--intera|--interac|--interact|\ - --interacti|--interactiv|--interactive) - interactive=t; shift ;; - - -b|--b|--bi|--bin|--bina|--binar|--binary) - binary=t; shift ;; - - -3|--3|--3w|--3wa|--3way) - threeway=t; shift ;; - -s|--s|--si|--sig|--sign|--signo|--signof|--signoff) - sign=t; shift ;; - -u|--u|--ut|--utf|--utf8) - utf8=t; shift ;; # this is now default - --no-u|--no-ut|--no-utf|--no-utf8) - utf8=; shift ;; - -k|--k|--ke|--kee|--keep) - keep=t; shift ;; - - -r|--r|--re|--res|--reso|--resol|--resolv|--resolve|--resolved) - resolved=t; shift ;; - - --sk|--ski|--skip) - skip=t; shift ;; - - --whitespace=*|-C*|-p*) - git_apply_opt="$git_apply_opt $1"; shift ;; - - --resolvemsg=*) - resolvemsg=${1#--resolvemsg=}; shift ;; - + -i|--interactive) + interactive=t ;; + -b|--binary) + binary=t ;; + -3|--3way) + threeway=t ;; + -s|--signoff) + sign=t ;; + -u|--utf8) + utf8=t ;; # this is now default + --no-utf8) + utf8= ;; + -k|--keep) + keep=t ;; + -r|--resolved) + resolved=t ;; + --skip) + skip=t ;; + -d|--dotest) + shift; dotest=$1;; + --resolvemsg) + shift; resolvemsg=$1 ;; + --whitespace) + git_apply_opt="$git_apply_opt $1=$2"; shift ;; + -C|-p) + git_apply_opt="$git_apply_opt $1$2"; shift ;; --) - shift; break ;; - -*) - usage ;; + shift; break ;; *) - break ;; + usage ;; esac + shift done # If the dotest directory exists, but we have finished applying all the @@ -214,7 +222,7 @@ fi case "$resolved" in '') - files=$(git diff-index --cached --name-only HEAD) || exit + files=$(git diff-index --cached --name-only HEAD --) || exit if [ "$files" ]; then echo "Dirty index: cannot apply patches (dirty: $files)" >&2 exit 1 @@ -348,7 +356,7 @@ do case "$resolved$interactive" in tt) # This is used only for interactive view option. - git diff-index -p --cached HEAD >"$dotest/patch" + git diff-index -p --cached HEAD -- >"$dotest/patch" ;; esac esac @@ -372,6 +380,7 @@ do [aA]*) action=yes interactive= ;; [nN]*) action=skip ;; [eE]*) git_editor "$dotest/final-commit" + SUBJECT=$(reread_subject "$dotest/final-commit") action=again ;; [vV]*) action=again LESS=-S ${PAGER:-less} "$dotest/patch" ;; @@ -407,7 +416,7 @@ do # trust what the user has in the index file and the # working tree. resolved= - git diff-index --quiet --cached HEAD && { + git diff-index --quiet --cached HEAD -- && { echo "No changes - did you forget to use 'git add'?" stop_here_user_resolve $this } @@ -429,7 +438,7 @@ do then # Applying the patch to an earlier tree and merging the # result may have produced the same tree as ours. - git diff-index --quiet --cached HEAD && { + git diff-index --quiet --cached HEAD -- && { echo No changes -- Patch already applied. go_next continue diff --git a/git-bisect.sh b/git-bisect.sh index b74f44df60..5385249890 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -22,6 +22,7 @@ git bisect log git bisect run <cmd>... use <cmd>... to automatically bisect.' +OPTIONS_SPEC= . git-sh-setup require_work_tree @@ -36,7 +37,7 @@ sq() { } bisect_autostart() { - test -d "$GIT_DIR/refs/bisect" || { + test -f "$GIT_DIR/BISECT_NAMES" || { echo >&2 'You need to start by "git bisect start"' if test -t 0 then @@ -71,7 +72,7 @@ bisect_start() { ;; refs/heads/*) [ -s "$GIT_DIR/head-name" ] && die "won't bisect on seeked tree" - echo "$head" | sed 's#^refs/heads/##' >"$GIT_DIR/head-name" + echo "${head#refs/heads/}" >"$GIT_DIR/head-name" ;; *) die "Bad HEAD - strange symbolic ref" @@ -82,7 +83,6 @@ bisect_start() { # Get rid of any old bisect state # bisect_clean_state - mkdir "$GIT_DIR/refs/bisect" # # Check for one bad and then some good revisions. @@ -130,7 +130,7 @@ bisect_write() { good|skip) tag="$state"-"$rev" ;; *) die "Bad bisect_write argument: $state" ;; esac - echo "$rev" >"$GIT_DIR/refs/bisect/$tag" + git update-ref "refs/bisect/$tag" "$rev" echo "# $state: "$(git show-branch $rev) >>"$GIT_DIR/BISECT_LOG" test -z "$nolog" && echo "git-bisect $state $rev" >>"$GIT_DIR/BISECT_LOG" } @@ -191,7 +191,7 @@ bisect_next_check() { ;; *) THEN='' - test -d "$GIT_DIR/refs/bisect" || { + test -f "$GIT_DIR/BISECT_NAMES" || { echo >&2 'You need to start by "git bisect start".' THEN='then ' } @@ -275,7 +275,7 @@ exit_if_skipped_commits () { if expr "$_tried" : ".*[|].*" > /dev/null ; then echo "There are only 'skip'ped commit left to test." echo "The first bad commit could be any of:" - echo "$_tried" | sed -e 's/[|]/\n/g' + echo "$_tried" | tr '[|]' '[\012]' echo "We cannot bisect more!" exit 2 fi @@ -316,20 +316,38 @@ bisect_next() { exit_if_skipped_commits "$bisect_rev" echo "Bisecting: $bisect_nr revisions left to test after this" - echo "$bisect_rev" >"$GIT_DIR/refs/heads/new-bisect" + git branch -f new-bisect "$bisect_rev" git checkout -q new-bisect || exit - mv "$GIT_DIR/refs/heads/new-bisect" "$GIT_DIR/refs/heads/bisect" && - GIT_DIR="$GIT_DIR" git symbolic-ref HEAD refs/heads/bisect + git branch -M new-bisect bisect git show-branch "$bisect_rev" } bisect_visualize() { bisect_next_check fail - not=`cd "$GIT_DIR/refs" && echo bisect/good-*` - eval gitk bisect/bad --not $not -- $(cat "$GIT_DIR/BISECT_NAMES") + + if test $# = 0 + then + case "${DISPLAY+set}" in + '') set git log ;; + set) set gitk ;; + esac + else + case "$1" in + git*|tig) ;; + -*) set git log "$@" ;; + *) set git "$@" ;; + esac + fi + + not=$(git for-each-ref --format='%(refname)' "refs/bisect/good-*") + eval '"$@"' refs/bisect/bad --not $not -- $(cat "$GIT_DIR/BISECT_NAMES") } bisect_reset() { + test -f "$GIT_DIR/BISECT_NAMES" || { + echo "We are not bisecting." + return + } case "$#" in 0) if [ -s "$GIT_DIR/head-name" ]; then branch=`cat "$GIT_DIR/head-name"` @@ -349,8 +367,12 @@ bisect_reset() { } bisect_clean_state() { - rm -fr "$GIT_DIR/refs/bisect" - rm -f "$GIT_DIR/refs/heads/bisect" + # There may be some refs packed during bisection. + git for-each-ref --format='%(refname) %(objectname)' refs/bisect/\* refs/heads/bisect | + while read ref hash + do + git update-ref -d $ref $hash + done rm -f "$GIT_DIR/BISECT_LOG" rm -f "$GIT_DIR/BISECT_NAMES" rm -f "$GIT_DIR/BISECT_RUN" @@ -442,7 +464,7 @@ case "$#" in next) # Not sure we want "next" at the UI level anymore. bisect_next "$@" ;; - visualize) + visualize|view) bisect_visualize "$@" ;; reset) bisect_reset "$@" ;; diff --git a/git-checkout.sh b/git-checkout.sh index 8993920673..f6d58ac044 100755 --- a/git-checkout.sh +++ b/git-checkout.sh @@ -1,6 +1,16 @@ #!/bin/sh -USAGE='[-q] [-f] [-b <new_branch>] [-m] [<branch>] [<paths>...]' +OPTIONS_KEEPDASHDASH=t +OPTIONS_SPEC="\ +git-branch [options] [<branch>] [<paths>...] +-- +b= create a new branch started at <branch> +l create the new branchs reflog +track tells if the new branch should track the remote branch +f proceed even if the index or working tree is not HEAD +m performa three-way merge on local modifications if needed +q,quiet be quiet +" SUBDIRECTORY_OK=Sometimes . git-sh-setup require_work_tree @@ -20,13 +30,12 @@ quiet= v=-v LF=' ' -while [ "$#" != "0" ]; do - arg="$1" - shift - case "$arg" in - "-b") - newbranch="$1" + +while test $# != 0; do + case "$1" in + -b) shift + newbranch="$1" [ -z "$newbranch" ] && die "git checkout: -b needs a branch name" git show-ref --verify --quiet -- "refs/heads/$newbranch" && @@ -34,64 +43,54 @@ while [ "$#" != "0" ]; do git check-ref-format "heads/$newbranch" || die "git checkout: we do not like '$newbranch' as a branch name." ;; - "-l") + -l) newbranch_log=-l ;; - "--track"|"--no-track") - track="$arg" + --track|--no-track) + track="$1" ;; - "-f") + -f) force=1 ;; -m) merge=1 ;; - "-q") + -q|--quiet) quiet=1 v= ;; --) + shift break ;; - -*) - usage - ;; *) - if rev=$(git rev-parse --verify "$arg^0" 2>/dev/null) - then - if [ -z "$rev" ]; then - echo "unknown flag $arg" - exit 1 - fi - new_name="$arg" - if git show-ref --verify --quiet -- "refs/heads/$arg" - then - rev=$(git rev-parse --verify "refs/heads/$arg^0") - branch="$arg" - fi - new="$rev" - elif rev=$(git rev-parse --verify "$arg^{tree}" 2>/dev/null) - then - # checking out selected paths from a tree-ish. - new="$rev" - new_name="$arg^{tree}" - branch= - else - new= - new_name= - branch= - set x "$arg" "$@" - shift - fi - case "$1" in - --) - shift ;; - esac - break + usage ;; - esac + esac + shift done +arg="$1" +if rev=$(git rev-parse --verify "$arg^0" 2>/dev/null) +then + [ -z "$rev" ] && die "unknown flag $arg" + new_name="$arg" + if git show-ref --verify --quiet -- "refs/heads/$arg" + then + rev=$(git rev-parse --verify "refs/heads/$arg^0") + branch="$arg" + fi + new="$rev" + shift +elif rev=$(git rev-parse --verify "$arg^{tree}" 2>/dev/null) +then + # checking out selected paths from a tree-ish. + new="$rev" + new_name="$arg^{tree}" + shift +fi +[ "$1" = "--" ] && shift + case "$newbranch,$track" in ,--*) die "git checkout: --track and --no-track require -b" @@ -134,12 +133,12 @@ Did you intend to checkout '$@' which can not be resolved as commit?" fi # Make sure the request is about existing paths. - git ls-files --error-unmatch -- "$@" >/dev/null || exit - git ls-files -- "$@" | - git checkout-index -f -u --stdin + git ls-files --full-name --error-unmatch -- "$@" >/dev/null || exit + git ls-files --full-name -- "$@" | + (cd_to_toplevel && git checkout-index -f -u --stdin) - # Run a post-checkout hook -- the HEAD does not change so the - # current HEAD is passed in for both args + # Run a post-checkout hook -- the HEAD does not change so the + # current HEAD is passed in for both args if test -x "$GIT_DIR"/hooks/post-checkout; then "$GIT_DIR"/hooks/post-checkout $old $old 0 fi @@ -176,7 +175,7 @@ detach_warn= describe_detached_head () { test -n "$quiet" || { printf >&2 "$1 " - GIT_PAGER= git log >&2 -1 --pretty=oneline --abbrev-commit "$2" + GIT_PAGER= git log >&2 -1 --pretty=oneline --abbrev-commit "$2" -- } } @@ -267,7 +266,7 @@ if [ "$?" -eq 0 ]; then if test -n "$branch" then old_branch_name=`expr "z$oldbranch" : 'zrefs/heads/\(.*\)'` - GIT_DIR="$GIT_DIR" git symbolic-ref -m "checkout: moving from $old_branch_name to $branch" HEAD "refs/heads/$branch" + GIT_DIR="$GIT_DIR" git symbolic-ref -m "checkout: moving from ${old_branch_name:-$old} to $branch" HEAD "refs/heads/$branch" if test -n "$quiet" then true # nothing @@ -279,7 +278,8 @@ if [ "$?" -eq 0 ]; then fi elif test -n "$detached" then - git update-ref --no-deref -m "checkout: moving to $arg" HEAD "$detached" || + old_branch_name=`expr "z$oldbranch" : 'zrefs/heads/\(.*\)'` + git update-ref --no-deref -m "checkout: moving from ${old_branch_name:-$old} to $arg" HEAD "$detached" || die "Cannot detach HEAD" if test -n "$detach_warn" then @@ -294,5 +294,5 @@ fi # Run a post-checkout hook if test -x "$GIT_DIR"/hooks/post-checkout; then - "$GIT_DIR"/hooks/post-checkout $old $new 1 + "$GIT_DIR"/hooks/post-checkout $old $new 1 fi diff --git a/git-clean.sh b/git-clean.sh deleted file mode 100755 index 4491738186..0000000000 --- a/git-clean.sh +++ /dev/null @@ -1,99 +0,0 @@ -#!/bin/sh -# -# Copyright (c) 2005-2006 Pavel Roskin -# - -USAGE="[-d] [-f] [-n] [-q] [-x | -X] [--] <paths>..." -LONG_USAGE='Clean untracked files from the working directory - -d remove directories as well - -f override clean.requireForce and clean anyway - -n don'\''t remove anything, just show what would be done - -q be quiet, only report errors - -x remove ignored files as well - -X remove only ignored files -When optional <paths>... arguments are given, the paths -affected are further limited to those that match them.' -SUBDIRECTORY_OK=Yes -. git-sh-setup -require_work_tree - -ignored= -ignoredonly= -cleandir= -disabled="`git config --bool clean.requireForce`" -rmf="rm -f --" -rmrf="rm -rf --" -rm_refuse="echo Not removing" -echo1="echo" - -while test $# != 0 -do - case "$1" in - -d) - cleandir=1 - ;; - -f) - disabled= - ;; - -n) - disabled= - rmf="echo Would remove" - rmrf="echo Would remove" - rm_refuse="echo Would not remove" - echo1=":" - ;; - -q) - echo1=":" - ;; - -x) - ignored=1 - ;; - -X) - ignoredonly=1 - ;; - --) - shift - break - ;; - -*) - usage - ;; - *) - break - esac - shift -done - -if [ "$disabled" = true ]; then - echo "clean.requireForce set and -n or -f not given; refusing to clean" - exit 1 -fi - -case "$ignored,$ignoredonly" in - 1,1) usage;; -esac - -if [ -z "$ignored" ]; then - excl="--exclude-per-directory=.gitignore" - if [ -f "$GIT_DIR/info/exclude" ]; then - excl_info="--exclude-from=$GIT_DIR/info/exclude" - fi - if [ "$ignoredonly" ]; then - excl="$excl --ignored" - fi -fi - -git ls-files --others --directory $excl ${excl_info:+"$excl_info"} -- "$@" | -while read -r file; do - if [ -d "$file" -a ! -L "$file" ]; then - if [ -z "$cleandir" ]; then - $rm_refuse "$file" - continue - fi - $echo1 "Removing $file" - $rmrf "$file" - else - $echo1 "Removing $file" - $rmf "$file" - fi -done diff --git a/git-clone.sh b/git-clone.sh index 0ea3c24f59..ecf9d89a10 100755 --- a/git-clone.sh +++ b/git-clone.sh @@ -8,15 +8,36 @@ # See git-sh-setup why. unset CDPATH +OPTIONS_SPEC="\ +git-clone [options] [--] <repo> [<dir>] +-- +n,no-checkout don't create a checkout +bare create a bare repository +naked create a bare repository +l,local to clone from a local repository +no-hardlinks don't use local hardlinks, always copy +s,shared setup as a shared repository +template= path to the template directory +q,quiet be quiet +reference= reference repository +o,origin= use <name> instead of 'origin' to track upstream +u,upload-pack= path to git-upload-pack on the remote +depth= create a shallow clone of that depth + +use-separate-remote compatibility, do not use +no-separate-remote compatibility, do not use" + die() { echo >&2 "$@" exit 1 } usage() { - die "Usage: $0 [--template=<template_directory>] [--reference <reference-repo>] [--bare] [-l [-s]] [-q] [-u <upload-pack>] [--origin <name>] [--depth <n>] [-n] <repo> [<dir>]" + exec "$0" -h } +eval "$(echo "$OPTIONS_SPEC" | git rev-parse --parseopt -- "$@" || echo exit $?)" + get_repo_base() { ( cd "`/bin/pwd`" && @@ -106,64 +127,57 @@ depth= no_progress= local_explicitly_asked_for= test -t 1 || no_progress=--no-progress -while - case "$#,$1" in - 0,*) break ;; - *,-n|*,--no|*,--no-|*,--no-c|*,--no-ch|*,--no-che|*,--no-chec|\ - *,--no-check|*,--no-checko|*,--no-checkou|*,--no-checkout) - no_checkout=yes ;; - *,--na|*,--nak|*,--nake|*,--naked|\ - *,-b|*,--b|*,--ba|*,--bar|*,--bare) bare=yes ;; - *,-l|*,--l|*,--lo|*,--loc|*,--loca|*,--local) - local_explicitly_asked_for=yes - use_local_hardlink=yes ;; - *,--no-h|*,--no-ha|*,--no-har|*,--no-hard|*,--no-hardl|\ - *,--no-hardli|*,--no-hardlin|*,--no-hardlink|*,--no-hardlinks) - use_local_hardlink=no ;; - *,-s|*,--s|*,--sh|*,--sha|*,--shar|*,--share|*,--shared) - local_shared=yes; ;; - 1,--template) usage ;; - *,--template) + +while test $# != 0 +do + case "$1" in + -n|--no-checkout) + no_checkout=yes ;; + --naked|--bare) + bare=yes ;; + -l|--local) + local_explicitly_asked_for=yes + use_local_hardlink=yes + ;; + --no-hardlinks) + use_local_hardlink=no ;; + -s|--shared) + local_shared=yes ;; + --template) shift; template="--template=$1" ;; - *,--template=*) - template="$1" ;; - *,-q|*,--quiet) quiet=-q ;; - *,--use-separate-remote) ;; - *,--no-separate-remote) + -q|--quiet) + quiet=-q ;; + --use-separate-remote|--no-separate-remote) die "clones are always made with separate-remote layout" ;; - 1,--reference) usage ;; - *,--reference) + --reference) shift; reference="$1" ;; - *,--reference=*) - reference=`expr "z$1" : 'z--reference=\(.*\)'` ;; - *,-o|*,--or|*,--ori|*,--orig|*,--origi|*,--origin) - case "$2" in + -o,--origin) + shift; + case "$1" in '') usage ;; */*) - die "'$2' is not suitable for an origin name" + die "'$1' is not suitable for an origin name" esac - git check-ref-format "heads/$2" || - die "'$2' is not suitable for a branch name" + git check-ref-format "heads/$1" || + die "'$1' is not suitable for a branch name" test -z "$origin_override" || die "Do not give more than one --origin options." origin_override=yes - origin="$2"; shift + origin="$1" ;; - 1,-u|1,--upload-pack) usage ;; - *,-u|*,--upload-pack) + -u|--upload-pack) shift upload_pack="--upload-pack=$1" ;; - *,--upload-pack=*) - upload_pack=--upload-pack=$(expr "z$1" : 'z-[^=]*=\(.*\)') ;; - 1,--depth) usage;; - *,--depth) + --depth) + shift + depth="--depth=$1" ;; + --) shift - depth="--depth=$1";; - *,-*) usage ;; - *) break ;; + break ;; + *) + usage ;; esac -do shift done @@ -215,7 +229,7 @@ cleanup() { trap cleanup 0 mkdir -p "$dir" && D=$(cd "$dir" && pwd) || usage test -n "$GIT_WORK_TREE" && mkdir -p "$GIT_WORK_TREE" && -W=$(cd "$GIT_WORK_TREE" && pwd) && export GIT_WORK_TREE="$W" +W=$(cd "$GIT_WORK_TREE" && pwd) && GIT_WORK_TREE="$W" && export GIT_WORK_TREE if test yes = "$bare" || test -n "$GIT_WORK_TREE"; then GIT_DIR="$D" else diff --git a/git-compat-util.h b/git-compat-util.h index 474f1d1ffb..79eb10eacb 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -4,10 +4,24 @@ #define _FILE_OFFSET_BITS 64 #ifndef FLEX_ARRAY -#if defined(__GNUC__) && (__GNUC__ < 3) -#define FLEX_ARRAY 0 -#else -#define FLEX_ARRAY /* empty */ +/* + * See if our compiler is known to support flexible array members. + */ +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) +# define FLEX_ARRAY /* empty */ +#elif defined(__GNUC__) +# if (__GNUC__ >= 3) +# define FLEX_ARRAY /* empty */ +# else +# define FLEX_ARRAY 0 /* older GNU extension */ +# endif +#endif + +/* + * Otherwise, default to safer but a bit wasteful traditional style + */ +#ifndef FLEX_ARRAY +# define FLEX_ARRAY 1 #endif #endif @@ -20,6 +34,7 @@ #endif #define MSB(x, bits) ((x) & TYPEOF(x)(~0ULL << (sizeof(x) * 8 - (bits)))) +#define HAS_MULTI_BITS(i) ((i) & ((i) - 1)) /* checks if an integer has more than 1 bit set */ /* Approximation of the length of the decimal representation of this type. */ #define decimal_length(x) ((int)(sizeof(x) * 2.56 + 0.5) + 1) @@ -52,6 +67,8 @@ #include <fnmatch.h> #include <sys/poll.h> #include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/select.h> #include <assert.h> #include <regex.h> #include <netinet/in.h> @@ -183,6 +200,22 @@ void *gitmemmem(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen); #endif +#ifdef __GLIBC_PREREQ +#if __GLIBC_PREREQ(2, 1) +#define HAVE_STRCHRNUL +#endif +#endif + +#ifndef HAVE_STRCHRNUL +#define strchrnul gitstrchrnul +static inline char *gitstrchrnul(const char *s, int c) +{ + while (*s && *s != c) + s++; + return (char *)s; +} +#endif + extern void release_pack_memory(size_t, int); static inline char* xstrdup(const char *str) @@ -381,4 +414,17 @@ static inline int strtoul_ui(char const *s, int base, unsigned int *result) return 0; } +static inline int strtol_i(char const *s, int base, int *result) +{ + long ul; + char *p; + + errno = 0; + ul = strtol(s, &p, base); + if (errno || *p || p == s || (int) ul != ul) + return -1; + *result = ul; + return 0; +} + #endif diff --git a/git-cvsexportcommit.perl b/git-cvsexportcommit.perl index 26844af439..92e41620fd 100755 --- a/git-cvsexportcommit.perl +++ b/git-cvsexportcommit.perl @@ -1,28 +1,42 @@ #!/usr/bin/perl -w -# Known limitations: -# - does not propagate permissions -# - error handling has not been extensively tested -# - use strict; use Getopt::Std; use File::Temp qw(tempdir); use Data::Dumper; use File::Basename qw(basename dirname); -unless ($ENV{GIT_DIR} && -r $ENV{GIT_DIR}){ - die "GIT_DIR is not defined or is unreadable"; -} - -our ($opt_h, $opt_P, $opt_p, $opt_v, $opt_c, $opt_f, $opt_a, $opt_m, $opt_d, $opt_u); +our ($opt_h, $opt_P, $opt_p, $opt_v, $opt_c, $opt_f, $opt_a, $opt_m, $opt_d, $opt_u, $opt_w); -getopts('uhPpvcfam:d:'); +getopts('uhPpvcfam:d:w:'); $opt_h && usage(); die "Need at least one commit identifier!" unless @ARGV; +if ($opt_w) { + unless ($ENV{GIT_DIR}) { + # Remember where our GIT_DIR is before changing to CVS checkout + my $gd =`git-rev-parse --git-dir`; + chomp($gd); + if ($gd eq '.git') { + my $wd = `pwd`; + chomp($wd); + $gd = $wd."/.git" ; + } + $ENV{GIT_DIR} = $gd; + } + + if (! -d $opt_w."/CVS" ) { + die "$opt_w is not a CVS checkout"; + } + chdir $opt_w or die "Cannot change to CVS checkout at $opt_w"; +} +unless ($ENV{GIT_DIR} && -r $ENV{GIT_DIR}){ + die "GIT_DIR is not defined or is unreadable"; +} + + my @cvs; if ($opt_d) { @cvs = ('cvs', '-d', $opt_d); @@ -274,6 +288,7 @@ if ($dirtypatch) { print "You'll need to apply the patch in .cvsexportcommit.diff manually\n"; print "using a patch program. After applying the patch and resolving the\n"; print "problems you may commit using:"; + print "\n cd \"$opt_w\"" if $opt_w; print "\n $cmd\n\n"; exit(1); } @@ -301,7 +316,7 @@ sleep(1); sub usage { print STDERR <<END; -Usage: GIT_DIR=/path/to/.git ${\basename $0} [-h] [-p] [-v] [-c] [-f] [-m msgprefix] [ parent ] commit +Usage: GIT_DIR=/path/to/.git ${\basename $0} [-h] [-p] [-v] [-c] [-f] [-u] [-w cvsworkdir] [-m msgprefix] [ parent ] commit END exit(1); } diff --git a/git-cvsimport.perl b/git-cvsimport.perl index 2954fb846e..92648f40c9 100755 --- a/git-cvsimport.perl +++ b/git-cvsimport.perl @@ -108,10 +108,6 @@ sub read_repo_config { } } } - if (@ARGV == 0) { - chomp(my $module = `git-repo-config --get cvsimport.module`); - push(@ARGV, $module); - } } my $opts = "haivmkuo:d:p:r:C:z:s:M:P:A:S:L:"; @@ -119,6 +115,10 @@ read_repo_config($opts); getopts($opts) or usage(); usage if $opt_h; +if (@ARGV == 0) { + chomp(my $module = `git-repo-config --get cvsimport.module`); + push(@ARGV, $module) if $? == 0; +} @ARGV <= 1 or usage("You can't specify more than one CVS module"); if ($opt_d) { @@ -223,7 +223,8 @@ sub conn { } } - $user="anonymous" unless defined $user; + # if username is not explicit in CVSROOT, then use current user, as cvs would + $user=(getlogin() || $ENV{'LOGNAME'} || $ENV{'USER'} || "anonymous") unless $user; my $rr2 = "-"; unless ($port) { $rr2 = ":pserver:$user\@$serv:$repo"; @@ -526,18 +527,12 @@ sub is_sha1 { return $s =~ /^[a-f0-9]{40}$/; } -sub get_headref ($$) { - my $name = shift; - my $git_dir = shift; - - my $f = "$git_dir/$remote/$name"; - if (open(my $fh, $f)) { - chomp(my $r = <$fh>); - is_sha1($r) or die "Cannot get head id for $name ($r): $!"; - return $r; - } - die "unable to open $f: $!" unless $! == POSIX::ENOENT; - return undef; +sub get_headref ($) { + my $name = shift; + my $r = `git rev-parse --verify '$name' 2>/dev/null`; + return undef unless $? == 0; + chomp $r; + return $r; } -d $git_tree @@ -697,7 +692,8 @@ my (@old,@new,@skipped,%ignorebranch); $ignorebranch{'#CVSPS_NO_BRANCH'} = 1; sub commit { - if ($branch eq $opt_o && !$index{branch} && !get_headref($branch, $git_dir)) { + if ($branch eq $opt_o && !$index{branch} && + !get_headref("$remote/$branch")) { # looks like an initial commit # use the index primed by git-init $ENV{GIT_INDEX_FILE} = "$git_dir/index"; @@ -721,7 +717,7 @@ sub commit { update_index(@old, @new); @old = @new = (); my $tree = write_tree(); - my $parent = get_headref($last_branch, $git_dir); + my $parent = get_headref("$remote/$last_branch"); print "Parent ID " . ($parent ? $parent : "(empty)") . "\n" if $opt_v; my @commit_args; @@ -732,7 +728,7 @@ sub commit { foreach my $rx (@mergerx) { next unless $logmsg =~ $rx && $1; my $mparent = $1 eq 'HEAD' ? $opt_o : $1; - if (my $sha1 = get_headref($mparent, $git_dir)) { + if (my $sha1 = get_headref("$remote/$mparent")) { push @commit_args, '-p', $mparent; print "Merge parent branch: $mparent\n" if $opt_v; } @@ -818,6 +814,7 @@ while (<CVS>) { $state = 4; } elsif ($state == 4 and s/^Branch:\s+//) { s/\s+$//; + tr/_/\./ if ( $opt_u ); s/[\/]/$opt_s/g; $branch = $_; $state = 5; @@ -868,29 +865,27 @@ while (<CVS>) { print STDERR "Branch $branch erroneously stems from itself -- changed ancestor to $opt_o\n"; $ancestor = $opt_o; } - if (-f "$git_dir/$remote/$branch") { + if (defined get_headref("$remote/$branch")) { print STDERR "Branch $branch already exists!\n"; $state=11; next; } - unless (open(H,"$git_dir/$remote/$ancestor")) { + my $id = get_headref("$remote/$ancestor"); + if (!$id) { print STDERR "Branch $ancestor does not exist!\n"; $ignorebranch{$branch} = 1; $state=11; next; } - chomp(my $id = <H>); - close(H); - unless (open(H,"> $git_dir/$remote/$branch")) { - print STDERR "Could not create branch $branch: $!\n"; + + system(qw(git update-ref -m cvsimport), + "$remote/$branch", $id); + if($? != 0) { + print STDERR "Could not create branch $branch\n"; $ignorebranch{$branch} = 1; $state=11; next; } - print H "$id\n" - or die "Could not write branch $branch: $!"; - close(H) - or die "Could not write branch $branch: $!"; } $last_branch = $branch if $branch ne $last_branch; $state = 9; @@ -1002,7 +997,7 @@ if ($orig_branch) { $orig_branch = "master"; print "DONE; creating $orig_branch branch\n" if $opt_v; system("git-update-ref", "refs/heads/master", "$remote/$opt_o") - unless -f "$git_dir/refs/heads/master"; + unless defined get_headref('refs/heads/master'); system("git-symbolic-ref", "$remote/HEAD", "$remote/$opt_o") if ($opt_r && $opt_o ne 'HEAD'); system('git-update-ref', 'HEAD', "$orig_branch"); diff --git a/git-cvsserver.perl b/git-cvsserver.perl index 0d55fec04f..ecded3b9cb 100755 --- a/git-cvsserver.perl +++ b/git-cvsserver.perl @@ -1211,13 +1211,13 @@ sub req_ci chdir $tmpdir; - # populate the temporary index based + # populate the temporary index system("git-read-tree", $parenthash); unless ($? == 0) { die "Error running git-read-tree $state->{module} $file_index $!"; } - $log->info("Created index '$file_index' with for head $state->{module} - exit status $?"); + $log->info("Created index '$file_index' for head $state->{module} - exit status $?"); my @committedfiles = (); my %oldmeta; @@ -1237,7 +1237,7 @@ sub req_ci my ( $filepart, $dirpart ) = filenamesplit($filename); - # do a checkout of the file if it part of this tree + # do a checkout of the file if it is part of this tree if ($wrev) { system('git-checkout-index', '-f', '-u', $filename); unless ($? == 0) { @@ -1324,11 +1324,11 @@ sub req_ci exit; } - # Check that this is allowed, just as we would with a receive-pack - my @cmd = ( $ENV{GIT_DIR}.'hooks/update', "refs/heads/$state->{module}", + ### Emulate git-receive-pack by running hooks/update + my @hook = ( $ENV{GIT_DIR}.'hooks/update', "refs/heads/$state->{module}", $parenthash, $commithash ); - if( -x $cmd[0] ) { - unless( system( @cmd ) == 0 ) + if( -x $hook[0] ) { + unless( system( @hook ) == 0 ) { $log->warn("Commit failed (update hook declined to update ref)"); print "error 1 Commit failed (update hook declined)\n"; @@ -1337,6 +1337,7 @@ sub req_ci } } + ### Update the ref if (system(qw(git update-ref -m), "cvsserver ci", "refs/heads/$state->{module}", $commithash, $parenthash)) { $log->warn("update-ref for $state->{module} failed."); @@ -1344,6 +1345,24 @@ sub req_ci exit; } + ### Emulate git-receive-pack by running hooks/post-receive + my $hook = $ENV{GIT_DIR}.'hooks/post-receive'; + if( -x $hook ) { + open(my $pipe, "| $hook") || die "can't fork $!"; + + local $SIG{PIPE} = sub { die 'pipe broke' }; + + print $pipe "$parenthash $commithash refs/heads/$state->{module}\n"; + + close $pipe || die "bad pipe: $! $?"; + } + + ### Then hooks/post-update + $hook = $ENV{GIT_DIR}.'hooks/post-update'; + if (-x $hook) { + system($hook, "refs/heads/$state->{module}"); + } + $updater->update(); # foreach file specified on the command line ... diff --git a/git-filter-branch.sh b/git-filter-branch.sh index ffcc408ee5..29d35fd27c 100755 --- a/git-filter-branch.sh +++ b/git-filter-branch.sh @@ -8,6 +8,9 @@ # a new branch. You can specify a number of filters to modify the commits, # files and trees. +# The following functions will also be available in the commit filter: + +functions=$(cat << \EOF warn () { echo "$*" >&2 } @@ -46,6 +49,10 @@ die() echo "$*" >&2 exit 1 } +EOF +) + +eval "$functions" # When piped a commit, output a script to set the ident of either # "author" or "committer @@ -59,17 +66,17 @@ set_ident () { h s/^'$lid' \([^<]*\) <[^>]*> .*$/\1/ s/'\''/'\''\'\'\''/g - s/.*/export GIT_'$uid'_NAME='\''&'\''/p + s/.*/GIT_'$uid'_NAME='\''&'\''; export GIT_'$uid'_NAME/p g s/^'$lid' [^<]* <\([^>]*\)> .*$/\1/ s/'\''/'\''\'\'\''/g - s/.*/export GIT_'$uid'_EMAIL='\''&'\''/p + s/.*/GIT_'$uid'_EMAIL='\''&'\''; export GIT_'$uid'_EMAIL/p g s/^'$lid' [^<]* <[^>]*> \(.*\)$/\1/ s/'\''/'\''\'\'\''/g - s/.*/export GIT_'$uid'_DATE='\''&'\''/p + s/.*/GIT_'$uid'_DATE='\''&'\''; export GIT_'$uid'_DATE/p q } @@ -77,14 +84,9 @@ set_ident () { LANG=C LC_ALL=C sed -ne "$pick_id_script" # Ensure non-empty id name. - echo "[ -n \"\$GIT_${uid}_NAME\" ] || export GIT_${uid}_NAME=\"\${GIT_${uid}_EMAIL%%@*}\"" + echo "case \"\$GIT_${uid}_NAME\" in \"\") GIT_${uid}_NAME=\"\${GIT_${uid}_EMAIL%%@*}\" && export GIT_${uid}_NAME;; esac" } -# This script can be sourced by the commit filter to get the functions -test "a$SOURCE_FUNCTIONS" = a1 && return -this_script="$(cd "$(dirname "$0")"; pwd)"/$(basename "$0") -export this_script - USAGE="[--env-filter <command>] [--tree-filter <command>] \ [--index-filter <command>] [--parent-filter <command>] \ [--msg-filter <command>] [--commit-filter <command>] \ @@ -92,10 +94,11 @@ USAGE="[--env-filter <command>] [--tree-filter <command>] \ [--original <namespace>] [-d <directory>] [-f | --force] \ [<rev-list options>...]" +OPTIONS_SPEC= . git-sh-setup git diff-files --quiet && - git diff-index --cached --quiet HEAD || + git diff-index --cached --quiet HEAD -- || die "Cannot rewrite branch(es) with a dirty working directory." tempdir=.git-rewrite @@ -155,7 +158,7 @@ do filter_msg="$OPTARG" ;; --commit-filter) - filter_commit='SOURCE_FUNCTIONS=1 . "$this_script";'" $OPTARG" + filter_commit="$functions; $OPTARG" ;; --tag-name-filter) filter_tag_name="$OPTARG" @@ -203,7 +206,8 @@ done < "$tempdir"/backup-refs ORIG_GIT_DIR="$GIT_DIR" ORIG_GIT_WORK_TREE="$GIT_WORK_TREE" ORIG_GIT_INDEX_FILE="$GIT_INDEX_FILE" -export GIT_DIR GIT_WORK_TREE=. +GIT_WORK_TREE=. +export GIT_DIR GIT_WORK_TREE # These refs should be updated if their heads were rewritten @@ -228,7 +232,8 @@ done > "$tempdir"/heads test -s "$tempdir"/heads || die "Which ref do you want to rewrite?" -export GIT_INDEX_FILE="$(pwd)/../index" +GIT_INDEX_FILE="$(pwd)/../index" +export GIT_INDEX_FILE git read-tree || die "Could not seed the index" ret=0 @@ -264,7 +269,8 @@ while read commit parents; do git read-tree -i -m $commit:"$filter_subdir" esac || die "Could not initialize the index" - export GIT_COMMIT=$commit + GIT_COMMIT=$commit + export GIT_COMMIT git cat-file commit "$commit" >../commit || die "Cannot read commit $commit" @@ -398,7 +404,8 @@ if [ "$filter_tag_name" ]; then [ -f "../map/$sha1" ] || continue new_sha1="$(cat "../map/$sha1")" - export GIT_COMMIT="$sha1" + GIT_COMMIT="$sha1" + export GIT_COMMIT new_ref="$(echo "$ref" | eval "$filter_tag_name")" || die "tag name filter failed: $filter_tag_name" diff --git a/git-gui/.gitignore b/git-gui/.gitignore index 020b86deae..6483b21cbf 100644 --- a/git-gui/.gitignore +++ b/git-gui/.gitignore @@ -1,5 +1,8 @@ +.DS_Store +config.mak +Git Gui.app* +git-gui.tcl GIT-VERSION-FILE GIT-GUI-VARS -git-citool git-gui lib/tclIndex diff --git a/git-gui/GIT-VERSION-GEN b/git-gui/GIT-VERSION-GEN index 9770b0bc27..cfe46a857e 100755 --- a/git-gui/GIT-VERSION-GEN +++ b/git-gui/GIT-VERSION-GEN @@ -1,7 +1,7 @@ #!/bin/sh GVF=GIT-VERSION-FILE -DEF_VER=0.8.GITGUI +DEF_VER=0.9.GITGUI LF=' ' diff --git a/git-gui/Makefile b/git-gui/Makefile index 18e6750137..26ac4b6bb0 100644 --- a/git-gui/Makefile +++ b/git-gui/Makefile @@ -2,18 +2,27 @@ all:: # Define V=1 to have a more verbose compile. # +# Define NO_MSGFMT if you do not have msgfmt from the GNU gettext +# package and want to use our rough pure Tcl po->msg translator. +# TCL_PATH must be vaild for this to work. +# GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE @$(SHELL_PATH) ./GIT-VERSION-GEN -include GIT-VERSION-FILE +uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not') uname_O := $(shell sh -c 'uname -o 2>/dev/null || echo not') SCRIPT_SH = git-gui.sh +GITGUI_MAIN := git-gui GITGUI_BUILT_INS = git-citool -ALL_PROGRAMS = $(GITGUI_BUILT_INS) $(patsubst %.sh,%,$(SCRIPT_SH)) ALL_LIBFILES = $(wildcard lib/*.tcl) PRELOAD_FILES = lib/class.tcl +NONTCL_LIBFILES = \ + lib/git-gui.ico \ + $(wildcard lib/win32_*.js) \ +#end NONTCL_LIBFILES ifndef SHELL_PATH SHELL_PATH = /bin/sh @@ -31,15 +40,17 @@ ifndef INSTALL INSTALL = install endif -RM_F ?= rm -f +RM_RF ?= rm -rf RMDIR ?= rmdir -INSTALL_D0 = $(INSTALL) -d -m755 # space is required here +INSTALL_D0 = $(INSTALL) -d -m 755 # space is required here INSTALL_D1 = -INSTALL_R0 = $(INSTALL) -m644 # space is required here +INSTALL_R0 = $(INSTALL) -m 644 # space is required here INSTALL_R1 = -INSTALL_X0 = $(INSTALL) -m755 # space is required here +INSTALL_X0 = $(INSTALL) -m 755 # space is required here INSTALL_X1 = +INSTALL_A0 = find # space is required here +INSTALL_A1 = | cpio -pud INSTALL_L0 = rm -f # space is required here INSTALL_L1 = && ln # space is required here INSTALL_L2 = @@ -47,23 +58,26 @@ INSTALL_L3 = REMOVE_D0 = $(RMDIR) # space is required here REMOVE_D1 = || true -REMOVE_F0 = $(RM_F) # space is required here +REMOVE_F0 = $(RM_RF) # space is required here REMOVE_F1 = CLEAN_DST = true ifndef V QUIET = @ - QUIET_GEN = $(QUIET)echo ' ' GEN $@ && - QUIET_BUILT_IN = $(QUIET)echo ' ' BUILTIN $@ && + QUIET_GEN = $(QUIET)echo ' ' GEN '$@' && QUIET_INDEX = $(QUIET)echo ' ' INDEX $(dir $@) && + QUIET_MSGFMT0 = $(QUIET)printf ' MSGFMT %12s ' $@ && v=` + QUIET_MSGFMT1 = 2>&1` && echo "$$v" | sed -e 's/fuzzy translations/fuzzy/' | sed -e 's/ messages//g' QUIET_2DEVNULL = 2>/dev/null INSTALL_D0 = dir= - INSTALL_D1 = && echo ' ' DEST $$dir && $(INSTALL) -d -m755 "$$dir" + INSTALL_D1 = && echo ' ' DEST $$dir && $(INSTALL) -d -m 755 "$$dir" INSTALL_R0 = src= - INSTALL_R1 = && echo ' ' INSTALL 644 `basename $$src` && $(INSTALL) -m644 $$src + INSTALL_R1 = && echo ' ' INSTALL 644 `basename $$src` && $(INSTALL) -m 644 $$src INSTALL_X0 = src= - INSTALL_X1 = && echo ' ' INSTALL 755 `basename $$src` && $(INSTALL) -m755 $$src + INSTALL_X1 = && echo ' ' INSTALL 755 `basename $$src` && $(INSTALL) -m 755 $$src + INSTALL_A0 = src= + INSTALL_A1 = && echo ' ' INSTALL ' ' `basename "$$src"` && find "$$src" | cpio -pud INSTALL_L0 = dst= INSTALL_L1 = && src= @@ -74,51 +88,133 @@ ifndef V REMOVE_D0 = dir= REMOVE_D1 = && echo ' ' REMOVE $$dir && test -d "$$dir" && $(RMDIR) "$$dir" || true REMOVE_F0 = dst= - REMOVE_F1 = && echo ' ' REMOVE `basename "$$dst"` && $(RM_F) "$$dst" + REMOVE_F1 = && echo ' ' REMOVE `basename "$$dst"` && $(RM_RF) "$$dst" endif TCL_PATH ?= tclsh TCLTK_PATH ?= wish +TKFRAMEWORK = /Library/Frameworks/Tk.framework/Resources/Wish.app ifeq ($(findstring $(MAKEFLAGS),s),s) QUIET_GEN = -QUIET_BUILT_IN = endif +-include config.mak + DESTDIR_SQ = $(subst ','\'',$(DESTDIR)) gitexecdir_SQ = $(subst ','\'',$(gitexecdir)) SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH)) TCL_PATH_SQ = $(subst ','\'',$(TCL_PATH)) TCLTK_PATH_SQ = $(subst ','\'',$(TCLTK_PATH)) +TCLTK_PATH_SED = $(subst ','\'',$(subst \,\\,$(TCLTK_PATH))) gg_libdir ?= $(sharedir)/git-gui/lib libdir_SQ = $(subst ','\'',$(gg_libdir)) +libdir_SED = $(subst ','\'',$(subst \,\\,$(gg_libdir))) +exedir = $(dir $(gitexecdir))share/git-gui/lib + +GITGUI_SCRIPT := $$0 +GITGUI_RELATIVE := +GITGUI_MACOSXAPP := + +ifeq ($(exedir),$(gg_libdir)) + GITGUI_RELATIVE := 1 +endif + +ifeq ($(uname_O),Cygwin) + GITGUI_SCRIPT := `cygpath --windows --absolute "$(GITGUI_SCRIPT)"` + ifeq ($(GITGUI_RELATIVE),) + gg_libdir := $(shell cygpath --windows --absolute "$(gg_libdir)") + endif +endif +ifeq ($(uname_S),Darwin) + ifeq ($(shell test -d $(TKFRAMEWORK) && echo y),y) + GITGUI_MACOSXAPP := YesPlease + endif +endif +ifneq (,$(findstring MINGW,$(uname_S))) + NO_MSGFMT=1 + GITGUI_WINDOWS_WRAPPER := YesPlease +endif + +ifdef GITGUI_MACOSXAPP +GITGUI_MAIN := git-gui.tcl + +git-gui: GIT-VERSION-FILE GIT-GUI-VARS + $(QUIET_GEN)rm -f $@ $@+ && \ + echo '#!$(SHELL_PATH_SQ)' >$@+ && \ + echo 'if test "z$$*" = zversion ||' >>$@+ && \ + echo ' test "z$$*" = z--version' >>$@+ && \ + echo then >>$@+ && \ + echo ' 'echo \'git-gui version '$(GITGUI_VERSION)'\' >>$@+ && \ + echo else >>$@+ && \ + echo ' 'exec \''$(libdir_SQ)/Git Gui.app/Contents/MacOS/Wish'\' \ + '"$$0" "$$@"' >>$@+ && \ + echo fi >>$@+ && \ + chmod +x $@+ && \ + mv $@+ $@ + +Git\ Gui.app: GIT-VERSION-FILE GIT-GUI-VARS \ + macosx/Info.plist \ + macosx/git-gui.icns \ + macosx/AppMain.tcl \ + $(TKFRAMEWORK)/Contents/MacOS/Wish + $(QUIET_GEN)rm -rf '$@' '$@'+ && \ + mkdir -p '$@'+/Contents/MacOS && \ + mkdir -p '$@'+/Contents/Resources/Scripts && \ + cp '$(subst ','\'',$(TKFRAMEWORK))/Contents/MacOS/Wish' \ + '$@'+/Contents/MacOS && \ + cp macosx/git-gui.icns '$@'+/Contents/Resources && \ + sed -e 's/@@GITGUI_VERSION@@/$(GITGUI_VERSION)/g' \ + macosx/Info.plist \ + >'$@'+/Contents/Info.plist && \ + sed -e 's|@@gitexecdir@@|$(gitexecdir_SQ)|' \ + -e 's|@@GITGUI_LIBDIR@@|$(libdir_SED)|' \ + macosx/AppMain.tcl \ + >'$@'+/Contents/Resources/Scripts/AppMain.tcl && \ + mv '$@'+ '$@' +endif + +ifdef GITGUI_WINDOWS_WRAPPER +GITGUI_MAIN := git-gui.tcl -exedir = $(dir $(gitexecdir))share/git-gui/lib -exedir_SQ = $(subst ','\'',$(exedir)) +git-gui: windows/git-gui.sh + cp $< $@ +endif -$(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh +$(GITGUI_MAIN): git-gui.sh GIT-VERSION-FILE GIT-GUI-VARS $(QUIET_GEN)rm -f $@ $@+ && \ - GITGUI_RELATIVE= && \ - if test '$(exedir_SQ)' = '$(libdir_SQ)'; then \ - if test "$(uname_O)" = Cygwin; \ - then GITGUI_RELATIVE= ; \ - else GITGUI_RELATIVE=1; \ - fi; \ - fi && \ sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \ - -e 's|^ exec wish "$$0"| exec $(subst |,'\|',$(TCLTK_PATH_SQ)) "$$0"|' \ + -e '1,30s|^ argv0=$$0| argv0=$(GITGUI_SCRIPT)|' \ + -e '1,30s|^ exec wish | exec '\''$(TCLTK_PATH_SED)'\'' |' \ -e 's/@@GITGUI_VERSION@@/$(GITGUI_VERSION)/g' \ - -e 's|@@GITGUI_RELATIVE@@|'$$GITGUI_RELATIVE'|' \ - -e $$GITGUI_RELATIVE's|@@GITGUI_LIBDIR@@|$(libdir_SQ)|' \ - $@.sh >$@+ && \ + -e 's|@@GITGUI_RELATIVE@@|$(GITGUI_RELATIVE)|' \ + -e '$(GITGUI_RELATIVE)s|@@GITGUI_LIBDIR@@|$(libdir_SED)|' \ + git-gui.sh >$@+ && \ chmod +x $@+ && \ mv $@+ $@ -$(GITGUI_BUILT_INS): git-gui - $(QUIET_BUILT_IN)rm -f $@ && ln git-gui $@ +XGETTEXT ?= xgettext +ifdef NO_MSGFMT + MSGFMT ?= $(TCL_PATH) po/po2msg.sh +else + MSGFMT ?= msgfmt +endif + +msgsdir = $(gg_libdir)/msgs +msgsdir_SQ = $(subst ','\'',$(msgsdir)) +PO_TEMPLATE = po/git-gui.pot +ALL_POFILES = $(wildcard po/*.po) +ALL_MSGFILES = $(subst .po,.msg,$(ALL_POFILES)) + +$(PO_TEMPLATE): $(SCRIPT_SH) $(ALL_LIBFILES) + $(XGETTEXT) -kmc -LTcl -o $@ $(SCRIPT_SH) $(ALL_LIBFILES) +update-po:: $(PO_TEMPLATE) + $(foreach p, $(ALL_POFILES), echo Updating $p ; msgmerge -U $p $(PO_TEMPLATE) ; ) +$(ALL_MSGFILES): %.msg : %.po + $(QUIET_MSGFMT0)$(MSGFMT) --statistics --tcl -l $(basename $(notdir $<)) -d $(dir $@) $< $(QUIET_MSGFMT1) -lib/tclIndex: $(ALL_LIBFILES) +lib/tclIndex: $(ALL_LIBFILES) GIT-GUI-VARS $(QUIET_INDEX)if echo \ $(foreach p,$(PRELOAD_FILES),source $p\;) \ auto_mkindex lib '*.tcl' \ @@ -132,16 +228,13 @@ lib/tclIndex: $(ALL_LIBFILES) echo >>$@ ; \ fi -# These can record GITGUI_VERSION -$(patsubst %.sh,%,$(SCRIPT_SH)): GIT-VERSION-FILE GIT-GUI-VARS -lib/tclIndex: GIT-GUI-VARS - TRACK_VARS = \ $(subst ','\'',SHELL_PATH='$(SHELL_PATH_SQ)') \ $(subst ','\'',TCL_PATH='$(TCL_PATH_SQ)') \ $(subst ','\'',TCLTK_PATH='$(TCLTK_PATH_SQ)') \ $(subst ','\'',gitexecdir='$(gitexecdir_SQ)') \ $(subst ','\'',gg_libdir='$(libdir_SQ)') \ + GITGUI_MACOSXAPP=$(GITGUI_MACOSXAPP) \ #end TRACK_VARS GIT-GUI-VARS: .FORCE-GIT-GUI-VARS @@ -151,24 +244,49 @@ GIT-GUI-VARS: .FORCE-GIT-GUI-VARS echo 1>$@ "$$VARS"; \ fi -all:: $(ALL_PROGRAMS) lib/tclIndex +ifdef GITGUI_MACOSXAPP +all:: git-gui Git\ Gui.app +endif +ifdef GITGUI_WINDOWS_WRAPPER +all:: git-gui +endif +all:: $(GITGUI_MAIN) lib/tclIndex $(ALL_MSGFILES) install: all $(QUIET)$(INSTALL_D0)'$(DESTDIR_SQ)$(gitexecdir_SQ)' $(INSTALL_D1) $(QUIET)$(INSTALL_X0)git-gui $(INSTALL_X1) '$(DESTDIR_SQ)$(gitexecdir_SQ)' $(QUIET)$(foreach p,$(GITGUI_BUILT_INS), $(INSTALL_L0)'$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' $(INSTALL_L1)'$(DESTDIR_SQ)$(gitexecdir_SQ)/git-gui' $(INSTALL_L2)'$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' $(INSTALL_L3) &&) true +ifdef GITGUI_WINDOWS_WRAPPER + $(QUIET)$(INSTALL_R0)git-gui.tcl $(INSTALL_R1) '$(DESTDIR_SQ)$(gitexecdir_SQ)' +endif $(QUIET)$(INSTALL_D0)'$(DESTDIR_SQ)$(libdir_SQ)' $(INSTALL_D1) $(QUIET)$(INSTALL_R0)lib/tclIndex $(INSTALL_R1) '$(DESTDIR_SQ)$(libdir_SQ)' - $(QUIET)$(foreach p,$(ALL_LIBFILES), $(INSTALL_R0)$p $(INSTALL_R1) '$(DESTDIR_SQ)$(libdir_SQ)' &&) true +ifdef GITGUI_MACOSXAPP + $(QUIET)$(INSTALL_A0)'Git Gui.app' $(INSTALL_A1) '$(DESTDIR_SQ)$(libdir_SQ)' + $(QUIET)$(INSTALL_X0)git-gui.tcl $(INSTALL_X1) '$(DESTDIR_SQ)$(libdir_SQ)' +endif + $(QUIET)$(foreach p,$(ALL_LIBFILES) $(NONTCL_LIBFILES), $(INSTALL_R0)$p $(INSTALL_R1) '$(DESTDIR_SQ)$(libdir_SQ)' &&) true + $(QUIET)$(INSTALL_D0)'$(DESTDIR_SQ)$(msgsdir_SQ)' $(INSTALL_D1) + $(QUIET)$(foreach p,$(ALL_MSGFILES), $(INSTALL_R0)$p $(INSTALL_R1) '$(DESTDIR_SQ)$(msgsdir_SQ)' &&) true uninstall: $(QUIET)$(CLEAN_DST) '$(DESTDIR_SQ)$(gitexecdir_SQ)' $(QUIET)$(REMOVE_F0)'$(DESTDIR_SQ)$(gitexecdir_SQ)'/git-gui $(REMOVE_F1) $(QUIET)$(foreach p,$(GITGUI_BUILT_INS), $(REMOVE_F0)'$(DESTDIR_SQ)$(gitexecdir_SQ)'/$p $(REMOVE_F1) &&) true +ifdef GITGUI_WINDOWS_WRAPPER + $(QUIET)$(REMOVE_F0)'$(DESTDIR_SQ)$(gitexecdir_SQ)'/git-gui.tcl $(REMOVE_F1) +endif $(QUIET)$(CLEAN_DST) '$(DESTDIR_SQ)$(libdir_SQ)' $(QUIET)$(REMOVE_F0)'$(DESTDIR_SQ)$(libdir_SQ)'/tclIndex $(REMOVE_F1) - $(QUIET)$(foreach p,$(ALL_LIBFILES), $(REMOVE_F0)'$(DESTDIR_SQ)$(libdir_SQ)'/$(notdir $p) $(REMOVE_F1) &&) true +ifdef GITGUI_MACOSXAPP + $(QUIET)$(REMOVE_F0)'$(DESTDIR_SQ)$(libdir_SQ)/Git Gui.app' $(REMOVE_F1) + $(QUIET)$(REMOVE_F0)'$(DESTDIR_SQ)$(libdir_SQ)'/git-gui.tcl $(REMOVE_F1) +endif + $(QUIET)$(foreach p,$(ALL_LIBFILES) $(NONTCL_LIBFILES), $(REMOVE_F0)'$(DESTDIR_SQ)$(libdir_SQ)'/$(notdir $p) $(REMOVE_F1) &&) true + $(QUIET)$(CLEAN_DST) '$(DESTDIR_SQ)$(msgsdir_SQ)' + $(QUIET)$(foreach p,$(ALL_MSGFILES), $(REMOVE_F0)'$(DESTDIR_SQ)$(msgsdir_SQ)'/$(notdir $p) $(REMOVE_F1) &&) true $(QUIET)$(REMOVE_D0)'$(DESTDIR_SQ)$(gitexecdir_SQ)' $(REMOVE_D1) + $(QUIET)$(REMOVE_D0)'$(DESTDIR_SQ)$(msgsdir_SQ)' $(REMOVE_D1) $(QUIET)$(REMOVE_D0)'$(DESTDIR_SQ)$(libdir_SQ)' $(REMOVE_D1) $(QUIET)$(REMOVE_D0)`dirname '$(DESTDIR_SQ)$(libdir_SQ)'` $(REMOVE_D1) @@ -177,8 +295,14 @@ dist-version: @echo $(GITGUI_VERSION) > $(TARDIR)/version clean:: - rm -f $(ALL_PROGRAMS) lib/tclIndex - rm -f GIT-VERSION-FILE GIT-GUI-VARS + $(RM_RF) $(GITGUI_MAIN) lib/tclIndex po/*.msg + $(RM_RF) GIT-VERSION-FILE GIT-GUI-VARS +ifdef GITGUI_MACOSXAPP + $(RM_RF) 'Git Gui.app'* git-gui +endif +ifdef GITGUI_WINDOWS_WRAPPER + $(RM_RF) git-gui +endif .PHONY: all install uninstall dist-version clean .PHONY: .FORCE-GIT-VERSION-FILE diff --git a/git-gui/git-gui.sh b/git-gui/git-gui.sh index 9335a9761b..1fca11f278 100755 --- a/git-gui/git-gui.sh +++ b/git-gui/git-gui.sh @@ -6,11 +6,12 @@ echo 'git-gui version @@GITGUI_VERSION@@'; \ exit; \ fi; \ - exec wish "$0" -- "$@" + argv0=$0; \ + exec wish "$argv0" -- "$@" set appvers {@@GITGUI_VERSION@@} -set copyright { -Copyright © 2006, 2007 Shawn Pearce, et. al. +set copyright [encoding convertfrom utf-8 { +Copyright © 2006, 2007 Shawn Pearce, et. al. 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 @@ -24,7 +25,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA} +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA}] ###################################################################### ## @@ -37,7 +38,7 @@ if {[catch {package require Tcl 8.4} err] tk_messageBox \ -icon error \ -type ok \ - -title "git-gui: fatal error" \ + -title [mc "git-gui: fatal error"] \ -message $err exit 1 } @@ -46,6 +47,24 @@ catch {rename send {}} ; # What an evil concept... ###################################################################### ## +## locate our library + +set oguilib {@@GITGUI_LIBDIR@@} +set oguirel {@@GITGUI_RELATIVE@@} +if {$oguirel eq {1}} { + set oguilib [file dirname [file dirname [file normalize $argv0]]] + set oguilib [file join $oguilib share git-gui lib] + set oguimsg [file join $oguilib msgs] +} elseif {[string match @@* $oguirel]} { + set oguilib [file join [file dirname [file normalize $argv0]] lib] + set oguimsg [file join [file dirname [file normalize $argv0]] po] +} else { + set oguimsg [file join $oguilib msgs] +} +unset oguirel + +###################################################################### +## ## enable verbose loading? if {![catch {set _verbose $env(GITGUI_VERBOSE)}]} { @@ -64,21 +83,39 @@ if {![catch {set _verbose $env(GITGUI_VERBOSE)}]} { ###################################################################### ## -## Fake internationalization to ease backporting of changes. +## Internationalization (i18n) through msgcat and gettext. See +## http://www.gnu.org/software/gettext/manual/html_node/Tcl.html + +package require msgcat -proc mc {fmt args} { +proc _mc_trim {fmt} { set cmk [string first @@ $fmt] if {$cmk > 0} { - set fmt [string range $fmt 0 [expr {$cmk - 1}]] + return [string range $fmt 0 [expr {$cmk - 1}]] } - return [eval [list format $fmt] $args] + return $fmt +} + +proc mc {en_fmt args} { + set fmt [_mc_trim [::msgcat::mc $en_fmt]] + if {[catch {set msg [eval [list format $fmt] $args]} err]} { + set msg [eval [list format [_mc_trim $en_fmt]] $args] + } + return $msg +} + +proc strcat {args} { + return [join $args {}] } +::msgcat::mcload $oguimsg +unset oguimsg + ###################################################################### ## ## read only globals -set _appname [lindex [file split $argv0] end] +set _appname {Git Gui} set _gitdir {} set _gitexec {} set _reponame {} @@ -175,6 +212,7 @@ proc disable_option {option} { proc is_many_config {name} { switch -glob -- $name { + gui.recentrepo - remote.*.fetch - remote.*.push {return 1} @@ -203,51 +241,6 @@ proc get_config {name} { } } -proc load_config {include_global} { - global repo_config global_config default_config - - array unset global_config - if {$include_global} { - catch { - set fd_rc [git_read config --global --list] - while {[gets $fd_rc line] >= 0} { - if {[regexp {^([^=]+)=(.*)$} $line line name value]} { - if {[is_many_config $name]} { - lappend global_config($name) $value - } else { - set global_config($name) $value - } - } - } - close $fd_rc - } - } - - array unset repo_config - catch { - set fd_rc [git_read config --list] - while {[gets $fd_rc line] >= 0} { - if {[regexp {^([^=]+)=(.*)$} $line line name value]} { - if {[is_many_config $name]} { - lappend repo_config($name) $value - } else { - set repo_config($name) $value - } - } - } - close $fd_rc - } - - foreach name [array names default_config] { - if {[catch {set v $global_config($name)}]} { - set global_config($name) $default_config($name) - } - if {[catch {set v $repo_config($name)}]} { - set repo_config($name) $default_config($name) - } - } -} - ###################################################################### ## ## handy utils @@ -313,6 +306,9 @@ proc _which {what} { $env(PATH)] {;}] set _search_exe .exe } elseif {[is_Windows]} { + set gitguidir [file dirname [info script]] + regsub -all ";" $gitguidir "\\;" gitguidir + set env(PATH) "$gitguidir;$env(PATH)" set _search_path [split $env(PATH) {;}] set _search_exe .exe } else { @@ -491,6 +487,110 @@ proc rmsel_tag {text} { return $text } +set root_exists 0 +bind . <Visibility> { + bind . <Visibility> {} + set root_exists 1 +} + +if {[is_Windows]} { + wm iconbitmap . -default $oguilib/git-gui.ico +} + +###################################################################### +## +## config defaults + +set cursor_ptr arrow +font create font_diff -family Courier -size 10 +font create font_ui +catch { + label .dummy + eval font configure font_ui [font actual [.dummy cget -font]] + destroy .dummy +} + +font create font_uiitalic +font create font_uibold +font create font_diffbold +font create font_diffitalic + +foreach class {Button Checkbutton Entry Label + Labelframe Listbox Menu Message + Radiobutton Spinbox Text} { + option add *$class.font font_ui +} +unset class + +if {[is_Windows] || [is_MacOSX]} { + option add *Menu.tearOff 0 +} + +if {[is_MacOSX]} { + set M1B M1 + set M1T Cmd +} else { + set M1B Control + set M1T Ctrl +} + +proc bind_button3 {w cmd} { + bind $w <Any-Button-3> $cmd + if {[is_MacOSX]} { + # Mac OS X sends Button-2 on right click through three-button mouse, + # or through trackpad right-clicking (two-finger touch + click). + bind $w <Any-Button-2> $cmd + bind $w <Control-Button-1> $cmd + } +} + +proc apply_config {} { + global repo_config font_descs + + foreach option $font_descs { + set name [lindex $option 0] + set font [lindex $option 1] + if {[catch { + set need_weight 1 + foreach {cn cv} $repo_config(gui.$name) { + if {$cn eq {-weight}} { + set need_weight 0 + } + font configure $font $cn $cv + } + if {$need_weight} { + font configure $font -weight normal + } + } err]} { + error_popup [strcat [mc "Invalid font specified in %s:" "gui.$name"] "\n\n$err"] + } + foreach {cn cv} [font configure $font] { + font configure ${font}bold $cn $cv + font configure ${font}italic $cn $cv + } + font configure ${font}bold -weight bold + font configure ${font}italic -slant italic + } +} + +set default_config(merge.diffstat) true +set default_config(merge.summary) false +set default_config(merge.verbosity) 2 +set default_config(user.name) {} +set default_config(user.email) {} + +set default_config(gui.matchtrackingbranch) false +set default_config(gui.pruneduringfetch) false +set default_config(gui.trustmtime) false +set default_config(gui.diffcontext) 5 +set default_config(gui.newbranchtemplate) {} +set default_config(gui.fontui) [font configure font_ui] +set default_config(gui.fontdiff) [font configure font_diff] +set font_descs { + {fontui font_ui {mc "Main Font"}} + {fontdiff font_diff {mc "Diff/Console Font"}} +} + ###################################################################### ## ## find git @@ -515,7 +615,7 @@ if {[catch {set _git_version [git --version]} err]} { tk_messageBox \ -icon error \ -type ok \ - -title "git-gui: fatal error" \ + -title [mc "git-gui: fatal error"] \ -message "Cannot determine Git version: $err @@ -528,8 +628,8 @@ if {![regsub {^git version } $_git_version {} _git_version]} { tk_messageBox \ -icon error \ -type ok \ - -title "git-gui: fatal error" \ - -message "Cannot parse Git version string:\n\n$_git_version" + -title [mc "git-gui: fatal error"] \ + -message [strcat [mc "Cannot parse Git version string:"] "\n\n$_git_version"] exit 1 } @@ -547,14 +647,14 @@ if {![regexp {^[1-9]+(\.[0-9]+)+$} $_git_version]} { -type yesno \ -default no \ -title "[appname]: warning" \ - -message "Git version cannot be determined. + -message [mc "Git version cannot be determined. -$_git claims it is version '$_real_git_version'. +%s claims it is version '%s'. -[appname] requires at least Git 1.5.0 or later. +%s requires at least Git 1.5.0 or later. -Assume '$_real_git_version' is version 1.5.0? -"] eq {yes}} { +Assume '%s' is version 1.5.0? +" $_git $_real_git_version [appname] $_real_git_version]] eq {yes}} { set _git_version 1.5.0 } else { exit 1 @@ -611,7 +711,7 @@ if {[git-version < 1.5]} { tk_messageBox \ -icon error \ -type ok \ - -title "git-gui: fatal error" \ + -title [mc "git-gui: fatal error"] \ -message "[appname] requires Git 1.5.0 or later. You are using [git-version]: @@ -624,22 +724,13 @@ You are using [git-version]: ## ## configure our library -set oguilib {@@GITGUI_LIBDIR@@} -set oguirel {@@GITGUI_RELATIVE@@} -if {$oguirel eq {1}} { - set oguilib [file dirname [file dirname [file normalize $argv0]]] - set oguilib [file join $oguilib share git-gui lib] -} elseif {[string match @@* $oguirel]} { - set oguilib [file join [file dirname [file normalize $argv0]] lib] -} - set idx [file join $oguilib tclIndex] if {[catch {set fd [open $idx r]} err]} { catch {wm withdraw .} tk_messageBox \ -icon error \ -type ok \ - -title "git-gui: fatal error" \ + -title [mc "git-gui: fatal error"] \ -message $err exit 1 } @@ -666,13 +757,78 @@ if {$idx ne {}} { } else { set auto_path [concat [list $oguilib] $auto_path] } -unset -nocomplain oguirel idx fd +unset -nocomplain idx fd + +###################################################################### +## +## config file parsing + +git-version proc _parse_config {arr_name args} { + >= 1.5.3 { + upvar $arr_name arr + array unset arr + set buf {} + catch { + set fd_rc [eval \ + [list git_read config] \ + $args \ + [list --null --list]] + fconfigure $fd_rc -translation binary + set buf [read $fd_rc] + close $fd_rc + } + foreach line [split $buf "\0"] { + if {[regexp {^([^\n]+)\n(.*)$} $line line name value]} { + if {[is_many_config $name]} { + lappend arr($name) $value + } else { + set arr($name) $value + } + } + } + } + default { + upvar $arr_name arr + array unset arr + catch { + set fd_rc [eval [list git_read config --list] $args] + while {[gets $fd_rc line] >= 0} { + if {[regexp {^([^=]+)=(.*)$} $line line name value]} { + if {[is_many_config $name]} { + lappend arr($name) $value + } else { + set arr($name) $value + } + } + } + close $fd_rc + } + } +} + +proc load_config {include_global} { + global repo_config global_config default_config + + if {$include_global} { + _parse_config global_config --global + } + _parse_config repo_config + + foreach name [array names default_config] { + if {[catch {set v $global_config($name)}]} { + set global_config($name) $default_config($name) + } + if {[catch {set v $repo_config($name)}]} { + set repo_config($name) $default_config($name) + } + } +} ###################################################################### ## ## feature option selection -if {[regexp {^git-(.+)$} [appname] _junk subcommand]} { +if {[regexp {^git-(.+)$} [file tail $argv0] _junk subcommand]} { unset _junk } else { set subcommand gui @@ -720,35 +876,35 @@ if {[catch { set _gitdir [git rev-parse --git-dir] set _prefix [git rev-parse --show-prefix] } err]} { - catch {wm withdraw .} - error_popup "Cannot find the git directory:\n\n$err" - exit 1 + load_config 1 + apply_config + choose_repository::pick } if {![file isdirectory $_gitdir] && [is_Cygwin]} { - catch {set _gitdir [exec cygpath --unix $_gitdir]} + catch {set _gitdir [exec cygpath --windows $_gitdir]} } if {![file isdirectory $_gitdir]} { catch {wm withdraw .} - error_popup "Git directory not found:\n\n$_gitdir" + error_popup [strcat [mc "Git directory not found:"] "\n\n$_gitdir"] exit 1 } if {$_prefix ne {}} { regsub -all {[^/]+/} $_prefix ../ cdup if {[catch {cd $cdup} err]} { catch {wm withdraw .} - error_popup "Cannot move to top of working directory:\n\n$err" + error_popup [strcat [mc "Cannot move to top of working directory:"] "\n\n$err"] exit 1 } unset cdup } elseif {![is_enabled bare]} { if {[lindex [file split $_gitdir] end] ne {.git}} { catch {wm withdraw .} - error_popup "Cannot use funny .git directory:\n\n$_gitdir" + error_popup [strcat [mc "Cannot use funny .git directory:"] "\n\n$_gitdir"] exit 1 } if {[catch {cd [file dirname $_gitdir]} err]} { catch {wm withdraw .} - error_popup "No working directory [file dirname $_gitdir]:\n\n$err" + error_popup [strcat [mc "No working directory"] " [file dirname $_gitdir]:\n\n$err"] exit 1 } } @@ -895,7 +1051,7 @@ proc rescan {after {honor_trustmtime 1}} { rescan_stage2 {} $after } else { set rescan_active 1 - ui_status {Refreshing file status...} + ui_status [mc "Refreshing file status..."] set fd_rf [git_read update-index \ -q \ --unmerged \ @@ -960,7 +1116,7 @@ proc rescan_stage2 {fd after} { set buf_rlo {} set rescan_active 3 - ui_status {Scanning for modified files ...} + ui_status [mc "Scanning for modified files ..."] set fd_di [git_read diff-index --cached -z [PARENT]] set fd_df [git_read diff-files -z] set fd_lo [eval git_read ls-files --others -z $ls_others] @@ -1401,31 +1557,32 @@ set all_icons(O$ui_workdir) file_plain set max_status_desc 0 foreach i { - {__ "Unmodified"} - - {_M "Modified, not staged"} - {M_ "Staged for commit"} - {MM "Portions staged for commit"} - {MD "Staged for commit, missing"} - - {_O "Untracked, not staged"} - {A_ "Staged for commit"} - {AM "Portions staged for commit"} - {AD "Staged for commit, missing"} - - {_D "Missing"} - {D_ "Staged for removal"} - {DO "Staged for removal, still present"} - - {U_ "Requires merge resolution"} - {UU "Requires merge resolution"} - {UM "Requires merge resolution"} - {UD "Requires merge resolution"} + {__ {mc "Unmodified"}} + + {_M {mc "Modified, not staged"}} + {M_ {mc "Staged for commit"}} + {MM {mc "Portions staged for commit"}} + {MD {mc "Staged for commit, missing"}} + + {_O {mc "Untracked, not staged"}} + {A_ {mc "Staged for commit"}} + {AM {mc "Portions staged for commit"}} + {AD {mc "Staged for commit, missing"}} + + {_D {mc "Missing"}} + {D_ {mc "Staged for removal"}} + {DO {mc "Staged for removal, still present"}} + + {U_ {mc "Requires merge resolution"}} + {UU {mc "Requires merge resolution"}} + {UM {mc "Requires merge resolution"}} + {UD {mc "Requires merge resolution"}} } { - if {$max_status_desc < [string length [lindex $i 1]]} { - set max_status_desc [string length [lindex $i 1]] + set text [eval [lindex $i 1]] + if {$max_status_desc < [string length $text]} { + set max_status_desc [string length $text] } - set all_descs([lindex $i 0]) [lindex $i 1] + set all_descs([lindex $i 0]) $text } unset i @@ -1433,16 +1590,6 @@ unset i ## ## util -proc bind_button3 {w cmd} { - bind $w <Any-Button-3> $cmd - if {[is_MacOSX]} { - # Mac OS X sends Button-2 on right click through three-button mouse, - # or through trackpad right-clicking (two-finger touch + click). - bind $w <Any-Button-2> $cmd - bind $w <Control-Button-1> $cmd - } -} - proc scrollbar2many {list mode args} { foreach w $list {eval $w $mode $args} } @@ -1464,7 +1611,7 @@ proc incr_font_size {font {amt 1}} { ## ## ui commands -set starting_gitk_msg {Starting gitk... please wait...} +set starting_gitk_msg [mc "Starting gitk... please wait..."] proc do_gitk {revs} { # -- Always start gitk through whatever we were loaded with. This @@ -1473,7 +1620,7 @@ proc do_gitk {revs} { set exe [file join [file dirname $::_git] gitk] set cmd [list [info nameofexecutable] $exe] if {! [file exists $exe]} { - error_popup "Unable to start gitk:\n\n$exe does not exist" + error_popup [mc "Unable to start gitk:\n\n%s does not exist" $exe] } else { global env @@ -1546,8 +1693,8 @@ proc do_quit {} { # set cfg_geometry [list] lappend cfg_geometry [wm geometry .] - lappend cfg_geometry [lindex [.vpane sash coord 0] 1] - lappend cfg_geometry [lindex [.vpane.files sash coord 0] 0] + lappend cfg_geometry [lindex [.vpane sash coord 0] 0] + lappend cfg_geometry [lindex [.vpane.files sash coord 0] 1] if {[catch {set rc_geometry $repo_config(gui.geometry)}]} { set rc_geometry {} } @@ -1664,104 +1811,26 @@ proc add_range_to_selection {w x y} { ###################################################################### ## -## config defaults - -set cursor_ptr arrow -font create font_diff -family Courier -size 10 -font create font_ui -catch { - label .dummy - eval font configure font_ui [font actual [.dummy cget -font]] - destroy .dummy -} - -font create font_uiitalic -font create font_uibold -font create font_diffbold -font create font_diffitalic - -foreach class {Button Checkbutton Entry Label - Labelframe Listbox Menu Message - Radiobutton Spinbox Text} { - option add *$class.font font_ui -} -unset class - -if {[is_Windows] || [is_MacOSX]} { - option add *Menu.tearOff 0 -} - -if {[is_MacOSX]} { - set M1B M1 - set M1T Cmd -} else { - set M1B Control - set M1T Ctrl -} - -proc apply_config {} { - global repo_config font_descs - - foreach option $font_descs { - set name [lindex $option 0] - set font [lindex $option 1] - if {[catch { - foreach {cn cv} $repo_config(gui.$name) { - font configure $font $cn $cv -weight normal - } - } err]} { - error_popup "Invalid font specified in gui.$name:\n\n$err" - } - foreach {cn cv} [font configure $font] { - font configure ${font}bold $cn $cv - font configure ${font}italic $cn $cv - } - font configure ${font}bold -weight bold - font configure ${font}italic -slant italic - } -} - -set default_config(merge.diffstat) true -set default_config(merge.summary) false -set default_config(merge.verbosity) 2 -set default_config(user.name) {} -set default_config(user.email) {} +## ui construction -set default_config(gui.matchtrackingbranch) false -set default_config(gui.pruneduringfetch) false -set default_config(gui.trustmtime) false -set default_config(gui.diffcontext) 5 -set default_config(gui.newbranchtemplate) {} -set default_config(gui.fontui) [font configure font_ui] -set default_config(gui.fontdiff) [font configure font_diff] -set font_descs { - {fontui font_ui {Main Font}} - {fontdiff font_diff {Diff/Console Font}} -} load_config 0 apply_config - -###################################################################### -## -## ui construction - set ui_comm {} # -- Menu Bar # menu .mbar -tearoff 0 -.mbar add cascade -label Repository -menu .mbar.repository -.mbar add cascade -label Edit -menu .mbar.edit +.mbar add cascade -label [mc Repository] -menu .mbar.repository +.mbar add cascade -label [mc Edit] -menu .mbar.edit if {[is_enabled branch]} { - .mbar add cascade -label Branch -menu .mbar.branch + .mbar add cascade -label [mc Branch] -menu .mbar.branch } if {[is_enabled multicommit] || [is_enabled singlecommit]} { - .mbar add cascade -label Commit -menu .mbar.commit + .mbar add cascade -label [mc Commit@@noun] -menu .mbar.commit } if {[is_enabled transport]} { - .mbar add cascade -label Merge -menu .mbar.merge - .mbar add cascade -label Fetch -menu .mbar.fetch - .mbar add cascade -label Push -menu .mbar.push + .mbar add cascade -label [mc Merge] -menu .mbar.merge + .mbar add cascade -label [mc Remote] -menu .mbar.remote } . configure -menu .mbar @@ -1770,87 +1839,87 @@ if {[is_enabled transport]} { menu .mbar.repository .mbar.repository add command \ - -label {Browse Current Branch's Files} \ + -label [mc "Browse Current Branch's Files"] \ -command {browser::new $current_branch} set ui_browse_current [.mbar.repository index last] .mbar.repository add command \ - -label {Browse Branch Files...} \ + -label [mc "Browse Branch Files..."] \ -command browser_open::dialog .mbar.repository add separator .mbar.repository add command \ - -label {Visualize Current Branch's History} \ + -label [mc "Visualize Current Branch's History"] \ -command {do_gitk $current_branch} set ui_visualize_current [.mbar.repository index last] .mbar.repository add command \ - -label {Visualize All Branch History} \ + -label [mc "Visualize All Branch History"] \ -command {do_gitk --all} .mbar.repository add separator proc current_branch_write {args} { global current_branch .mbar.repository entryconf $::ui_browse_current \ - -label "Browse $current_branch's Files" + -label [mc "Browse %s's Files" $current_branch] .mbar.repository entryconf $::ui_visualize_current \ - -label "Visualize $current_branch's History" + -label [mc "Visualize %s's History" $current_branch] } trace add variable current_branch write current_branch_write if {[is_enabled multicommit]} { - .mbar.repository add command -label {Database Statistics} \ + .mbar.repository add command -label [mc "Database Statistics"] \ -command do_stats - .mbar.repository add command -label {Compress Database} \ + .mbar.repository add command -label [mc "Compress Database"] \ -command do_gc - .mbar.repository add command -label {Verify Database} \ + .mbar.repository add command -label [mc "Verify Database"] \ -command do_fsck_objects .mbar.repository add separator if {[is_Cygwin]} { .mbar.repository add command \ - -label {Create Desktop Icon} \ + -label [mc "Create Desktop Icon"] \ -command do_cygwin_shortcut } elseif {[is_Windows]} { .mbar.repository add command \ - -label {Create Desktop Icon} \ + -label [mc "Create Desktop Icon"] \ -command do_windows_shortcut } elseif {[is_MacOSX]} { .mbar.repository add command \ - -label {Create Desktop Icon} \ + -label [mc "Create Desktop Icon"] \ -command do_macosx_app } } -.mbar.repository add command -label Quit \ +.mbar.repository add command -label [mc Quit] \ -command do_quit \ -accelerator $M1T-Q # -- Edit Menu # menu .mbar.edit -.mbar.edit add command -label Undo \ +.mbar.edit add command -label [mc Undo] \ -command {catch {[focus] edit undo}} \ -accelerator $M1T-Z -.mbar.edit add command -label Redo \ +.mbar.edit add command -label [mc Redo] \ -command {catch {[focus] edit redo}} \ -accelerator $M1T-Y .mbar.edit add separator -.mbar.edit add command -label Cut \ +.mbar.edit add command -label [mc Cut] \ -command {catch {tk_textCut [focus]}} \ -accelerator $M1T-X -.mbar.edit add command -label Copy \ +.mbar.edit add command -label [mc Copy] \ -command {catch {tk_textCopy [focus]}} \ -accelerator $M1T-C -.mbar.edit add command -label Paste \ +.mbar.edit add command -label [mc Paste] \ -command {catch {tk_textPaste [focus]; [focus] see insert}} \ -accelerator $M1T-V -.mbar.edit add command -label Delete \ +.mbar.edit add command -label [mc Delete] \ -command {catch {[focus] delete sel.first sel.last}} \ -accelerator Del .mbar.edit add separator -.mbar.edit add command -label {Select All} \ +.mbar.edit add command -label [mc "Select All"] \ -command {catch {[focus] tag add sel 0.0 end}} \ -accelerator $M1T-A @@ -1859,29 +1928,29 @@ menu .mbar.edit if {[is_enabled branch]} { menu .mbar.branch - .mbar.branch add command -label {Create...} \ + .mbar.branch add command -label [mc "Create..."] \ -command branch_create::dialog \ -accelerator $M1T-N lappend disable_on_lock [list .mbar.branch entryconf \ [.mbar.branch index last] -state] - .mbar.branch add command -label {Checkout...} \ + .mbar.branch add command -label [mc "Checkout..."] \ -command branch_checkout::dialog \ -accelerator $M1T-O lappend disable_on_lock [list .mbar.branch entryconf \ [.mbar.branch index last] -state] - .mbar.branch add command -label {Rename...} \ + .mbar.branch add command -label [mc "Rename..."] \ -command branch_rename::dialog lappend disable_on_lock [list .mbar.branch entryconf \ [.mbar.branch index last] -state] - .mbar.branch add command -label {Delete...} \ + .mbar.branch add command -label [mc "Delete..."] \ -command branch_delete::dialog lappend disable_on_lock [list .mbar.branch entryconf \ [.mbar.branch index last] -state] - .mbar.branch add command -label {Reset...} \ + .mbar.branch add command -label [mc "Reset..."] \ -command merge::reset_hard lappend disable_on_lock [list .mbar.branch entryconf \ [.mbar.branch index last] -state] @@ -1893,7 +1962,7 @@ if {[is_enabled multicommit] || [is_enabled singlecommit]} { menu .mbar.commit .mbar.commit add radiobutton \ - -label {New Commit} \ + -label [mc "New Commit"] \ -command do_select_commit_type \ -variable selected_commit_type \ -value new @@ -1901,7 +1970,7 @@ if {[is_enabled multicommit] || [is_enabled singlecommit]} { [list .mbar.commit entryconf [.mbar.commit index last] -state] .mbar.commit add radiobutton \ - -label {Amend Last Commit} \ + -label [mc "Amend Last Commit"] \ -command do_select_commit_type \ -variable selected_commit_type \ -value amend @@ -1910,40 +1979,41 @@ if {[is_enabled multicommit] || [is_enabled singlecommit]} { .mbar.commit add separator - .mbar.commit add command -label Rescan \ + .mbar.commit add command -label [mc Rescan] \ -command do_rescan \ -accelerator F5 lappend disable_on_lock \ [list .mbar.commit entryconf [.mbar.commit index last] -state] - .mbar.commit add command -label {Stage To Commit} \ - -command do_add_selection + .mbar.commit add command -label [mc "Stage To Commit"] \ + -command do_add_selection \ + -accelerator $M1T-T lappend disable_on_lock \ [list .mbar.commit entryconf [.mbar.commit index last] -state] - .mbar.commit add command -label {Stage Changed Files To Commit} \ + .mbar.commit add command -label [mc "Stage Changed Files To Commit"] \ -command do_add_all \ -accelerator $M1T-I lappend disable_on_lock \ [list .mbar.commit entryconf [.mbar.commit index last] -state] - .mbar.commit add command -label {Unstage From Commit} \ + .mbar.commit add command -label [mc "Unstage From Commit"] \ -command do_unstage_selection lappend disable_on_lock \ [list .mbar.commit entryconf [.mbar.commit index last] -state] - .mbar.commit add command -label {Revert Changes} \ + .mbar.commit add command -label [mc "Revert Changes"] \ -command do_revert_selection lappend disable_on_lock \ [list .mbar.commit entryconf [.mbar.commit index last] -state] .mbar.commit add separator - .mbar.commit add command -label {Sign Off} \ + .mbar.commit add command -label [mc "Sign Off"] \ -command do_signoff \ -accelerator $M1T-S - .mbar.commit add command -label Commit \ + .mbar.commit add command -label [mc Commit@@verb] \ -command do_commit \ -accelerator $M1T-Return lappend disable_on_lock \ @@ -1954,12 +2024,12 @@ if {[is_enabled multicommit] || [is_enabled singlecommit]} { # if {[is_enabled branch]} { menu .mbar.merge - .mbar.merge add command -label {Local Merge...} \ + .mbar.merge add command -label [mc "Local Merge..."] \ -command merge::dialog \ -accelerator $M1T-M lappend disable_on_lock \ [list .mbar.merge entryconf [.mbar.merge index last] -state] - .mbar.merge add command -label {Abort Merge...} \ + .mbar.merge add command -label [mc "Abort Merge..."] \ -command merge::reset_hard lappend disable_on_lock \ [list .mbar.merge entryconf [.mbar.merge index last] -state] @@ -1968,41 +2038,46 @@ if {[is_enabled branch]} { # -- Transport Menu # if {[is_enabled transport]} { - menu .mbar.fetch + menu .mbar.remote - menu .mbar.push - .mbar.push add command -label {Push...} \ + .mbar.remote add command \ + -label [mc "Push..."] \ -command do_push_anywhere \ -accelerator $M1T-P - .mbar.push add command -label {Delete...} \ + .mbar.remote add command \ + -label [mc "Delete..."] \ -command remote_branch_delete::dialog } if {[is_MacOSX]} { # -- Apple Menu (Mac OS X only) # - .mbar add cascade -label Apple -menu .mbar.apple + .mbar add cascade -label [mc Apple] -menu .mbar.apple menu .mbar.apple - .mbar.apple add command -label "About [appname]" \ + .mbar.apple add command -label [mc "About %s" [appname]] \ -command do_about - .mbar.apple add command -label "Options..." \ - -command do_options + .mbar.apple add separator + .mbar.apple add command \ + -label [mc "Preferences..."] \ + -command do_options \ + -accelerator $M1T-, + bind . <$M1B-,> do_options } else { # -- Edit Menu # .mbar.edit add separator - .mbar.edit add command -label {Options...} \ + .mbar.edit add command -label [mc "Options..."] \ -command do_options } # -- Help Menu # -.mbar add cascade -label Help -menu .mbar.help +.mbar add cascade -label [mc Help] -menu .mbar.help menu .mbar.help if {![is_MacOSX]} { - .mbar.help add command -label "About [appname]" \ + .mbar.help add command -label [mc "About %s" [appname]] \ -command do_about } @@ -2039,17 +2114,11 @@ if {[file isfile $doc_path]} { } if {$browser ne {}} { - .mbar.help add command -label {Online Documentation} \ + .mbar.help add command -label [mc "Online Documentation"] \ -command [list exec $browser $doc_url &] } unset browser doc_path doc_url -set root_exists 0 -bind . <Visibility> { - bind . <Visibility> {} - set root_exists 1 -} - # -- Standard bindings # wm protocol . WM_DELETE_WINDOW do_quit @@ -2129,7 +2198,7 @@ blame { } blame { if {$head eq {} && ![file exists $path]} { - puts stderr "fatal: cannot stat path $path: No such file or directory" + puts stderr [mc "fatal: cannot stat path %s: No such file or directory" $path] exit 1 } blame::new $head $path @@ -2141,7 +2210,8 @@ citool - gui { if {[llength $argv] != 0} { puts -nonewline stderr "usage: $argv0" - if {$subcommand ne {gui} && [appname] ne "git-$subcommand"} { + if {$subcommand ne {gui} + && [file tail $argv0] ne "git-$subcommand"} { puts -nonewline stderr " $subcommand" } puts stderr {} @@ -2161,7 +2231,7 @@ frame .branch \ -borderwidth 1 \ -relief sunken label .branch.l1 \ - -text {Current Branch:} \ + -text [mc "Current Branch:"] \ -anchor w \ -justify left label .branch.cb \ @@ -2174,15 +2244,15 @@ pack .branch -side top -fill x # -- Main Window Layout # -panedwindow .vpane -orient vertical -panedwindow .vpane.files -orient horizontal +panedwindow .vpane -orient horizontal +panedwindow .vpane.files -orient vertical .vpane add .vpane.files -sticky nsew -height 100 -width 200 pack .vpane -anchor n -side top -fill both -expand 1 # -- Index File List # frame .vpane.files.index -height 100 -width 200 -label .vpane.files.index.title -text {Staged Changes (Will Be Committed)} \ +label .vpane.files.index.title -text [mc "Staged Changes (Will Commit)"] \ -background lightgreen text $ui_index -background white -borderwidth 0 \ -width 20 -height 10 \ @@ -2197,12 +2267,11 @@ pack .vpane.files.index.title -side top -fill x pack .vpane.files.index.sx -side bottom -fill x pack .vpane.files.index.sy -side right -fill y pack $ui_index -side left -fill both -expand 1 -.vpane.files add .vpane.files.index -sticky nsew # -- Working Directory File List # frame .vpane.files.workdir -height 100 -width 200 -label .vpane.files.workdir.title -text {Unstaged Changes (Will Not Be Committed)} \ +label .vpane.files.workdir.title -text [mc "Unstaged Changes"] \ -background lightsalmon text $ui_workdir -background white -borderwidth 0 \ -width 20 -height 10 \ @@ -2217,7 +2286,9 @@ pack .vpane.files.workdir.title -side top -fill x pack .vpane.files.workdir.sx -side bottom -fill x pack .vpane.files.workdir.sy -side right -fill y pack $ui_workdir -side left -fill both -expand 1 + .vpane.files add .vpane.files.workdir -sticky nsew +.vpane.files add .vpane.files.index -sticky nsew foreach i [list $ui_index $ui_workdir] { rmsel_tag $i @@ -2230,8 +2301,8 @@ unset i frame .vpane.lower -height 300 -width 400 frame .vpane.lower.commarea frame .vpane.lower.diff -relief sunken -borderwidth 1 -pack .vpane.lower.commarea -side top -fill x -pack .vpane.lower.diff -side bottom -fill both -expand 1 +pack .vpane.lower.diff -fill both -expand 1 +pack .vpane.lower.commarea -side bottom -fill x .vpane add .vpane.lower -sticky nsew # -- Commit Area Buttons @@ -2243,29 +2314,29 @@ label .vpane.lower.commarea.buttons.l -text {} \ pack .vpane.lower.commarea.buttons.l -side top -fill x pack .vpane.lower.commarea.buttons -side left -fill y -button .vpane.lower.commarea.buttons.rescan -text {Rescan} \ +button .vpane.lower.commarea.buttons.rescan -text [mc Rescan] \ -command do_rescan pack .vpane.lower.commarea.buttons.rescan -side top -fill x lappend disable_on_lock \ {.vpane.lower.commarea.buttons.rescan conf -state} -button .vpane.lower.commarea.buttons.incall -text {Stage Changed} \ +button .vpane.lower.commarea.buttons.incall -text [mc "Stage Changed"] \ -command do_add_all pack .vpane.lower.commarea.buttons.incall -side top -fill x lappend disable_on_lock \ {.vpane.lower.commarea.buttons.incall conf -state} -button .vpane.lower.commarea.buttons.signoff -text {Sign Off} \ +button .vpane.lower.commarea.buttons.signoff -text [mc "Sign Off"] \ -command do_signoff pack .vpane.lower.commarea.buttons.signoff -side top -fill x -button .vpane.lower.commarea.buttons.commit -text {Commit} \ +button .vpane.lower.commarea.buttons.commit -text [mc Commit@@verb] \ -command do_commit pack .vpane.lower.commarea.buttons.commit -side top -fill x lappend disable_on_lock \ {.vpane.lower.commarea.buttons.commit conf -state} -button .vpane.lower.commarea.buttons.push -text {Push} \ +button .vpane.lower.commarea.buttons.push -text [mc Push] \ -command do_push_anywhere pack .vpane.lower.commarea.buttons.push -side top -fill x @@ -2276,14 +2347,14 @@ frame .vpane.lower.commarea.buffer.header set ui_comm .vpane.lower.commarea.buffer.t set ui_coml .vpane.lower.commarea.buffer.header.l radiobutton .vpane.lower.commarea.buffer.header.new \ - -text {New Commit} \ + -text [mc "New Commit"] \ -command do_select_commit_type \ -variable selected_commit_type \ -value new lappend disable_on_lock \ [list .vpane.lower.commarea.buffer.header.new conf -state] radiobutton .vpane.lower.commarea.buffer.header.amend \ - -text {Amend Last Commit} \ + -text [mc "Amend Last Commit"] \ -command do_select_commit_type \ -variable selected_commit_type \ -value amend @@ -2295,12 +2366,12 @@ label $ui_coml \ proc trace_commit_type {varname args} { global ui_coml commit_type switch -glob -- $commit_type { - initial {set txt {Initial Commit Message:}} - amend {set txt {Amended Commit Message:}} - amend-initial {set txt {Amended Initial Commit Message:}} - amend-merge {set txt {Amended Merge Commit Message:}} - merge {set txt {Merge Commit Message:}} - * {set txt {Commit Message:}} + initial {set txt [mc "Initial Commit Message:"]} + amend {set txt [mc "Amended Commit Message:"]} + amend-initial {set txt [mc "Amended Initial Commit Message:"]} + amend-merge {set txt [mc "Amended Merge Commit Message:"]} + merge {set txt [mc "Merge Commit Message:"]} + * {set txt [mc "Commit Message:"]} } $ui_coml conf -text $txt } @@ -2329,23 +2400,23 @@ pack .vpane.lower.commarea.buffer -side left -fill y set ctxm .vpane.lower.commarea.buffer.ctxm menu $ctxm -tearoff 0 $ctxm add command \ - -label {Cut} \ + -label [mc Cut] \ -command {tk_textCut $ui_comm} $ctxm add command \ - -label {Copy} \ + -label [mc Copy] \ -command {tk_textCopy $ui_comm} $ctxm add command \ - -label {Paste} \ + -label [mc Paste] \ -command {tk_textPaste $ui_comm} $ctxm add command \ - -label {Delete} \ + -label [mc Delete] \ -command {$ui_comm delete sel.first sel.last} $ctxm add separator $ctxm add command \ - -label {Select All} \ + -label [mc "Select All"] \ -command {focus $ui_comm;$ui_comm tag add sel 0.0 end} $ctxm add command \ - -label {Copy All} \ + -label [mc "Copy All"] \ -command { $ui_comm tag add sel 0.0 end tk_textCopy $ui_comm @@ -2353,7 +2424,7 @@ $ctxm add command \ } $ctxm add separator $ctxm add command \ - -label {Sign Off} \ + -label [mc "Sign Off"] \ -command do_signoff bind_button3 $ui_comm "tk_popup $ctxm %X %Y" @@ -2369,7 +2440,7 @@ proc trace_current_diff_path {varname args} { } else { set p $current_diff_path set s [mapdesc [lindex $file_states($p) 0] $p] - set f {File:} + set f [mc "File:"] set p [escape_path $p] set o normal } @@ -2403,7 +2474,7 @@ pack .vpane.lower.diff.header.path -fill x set ctxm .vpane.lower.diff.header.ctxm menu $ctxm -tearoff 0 $ctxm add command \ - -label {Copy} \ + -label [mc Copy] \ -command { clipboard clear clipboard append \ @@ -2471,19 +2542,19 @@ $ui_diff tag raise sel set ctxm .vpane.lower.diff.body.ctxm menu $ctxm -tearoff 0 $ctxm add command \ - -label {Refresh} \ + -label [mc Refresh] \ -command reshow_diff lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state] $ctxm add command \ - -label {Copy} \ + -label [mc Copy] \ -command {tk_textCopy $ui_diff} lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state] $ctxm add command \ - -label {Select All} \ + -label [mc "Select All"] \ -command {focus $ui_diff;$ui_diff tag add sel 0.0 end} lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state] $ctxm add command \ - -label {Copy All} \ + -label [mc "Copy All"] \ -command { $ui_diff tag add sel 0.0 end tk_textCopy $ui_diff @@ -2492,45 +2563,45 @@ $ctxm add command \ lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state] $ctxm add separator $ctxm add command \ - -label {Apply/Reverse Hunk} \ + -label [mc "Apply/Reverse Hunk"] \ -command {apply_hunk $cursorX $cursorY} set ui_diff_applyhunk [$ctxm index last] lappend diff_actions [list $ctxm entryconf $ui_diff_applyhunk -state] $ctxm add separator $ctxm add command \ - -label {Decrease Font Size} \ + -label [mc "Decrease Font Size"] \ -command {incr_font_size font_diff -1} lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state] $ctxm add command \ - -label {Increase Font Size} \ + -label [mc "Increase Font Size"] \ -command {incr_font_size font_diff 1} lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state] $ctxm add separator $ctxm add command \ - -label {Show Less Context} \ + -label [mc "Show Less Context"] \ -command {if {$repo_config(gui.diffcontext) >= 1} { incr repo_config(gui.diffcontext) -1 reshow_diff }} lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state] $ctxm add command \ - -label {Show More Context} \ + -label [mc "Show More Context"] \ -command {if {$repo_config(gui.diffcontext) < 99} { incr repo_config(gui.diffcontext) reshow_diff }} lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state] $ctxm add separator -$ctxm add command -label {Options...} \ +$ctxm add command -label [mc "Options..."] \ -command do_options proc popup_diff_menu {ctxm x y X Y} { global current_diff_path file_states set ::cursorX $x set ::cursorY $y if {$::ui_index eq $::current_diff_side} { - set l "Unstage Hunk From Commit" + set l [mc "Unstage Hunk From Commit"] } else { - set l "Stage Hunk For Commit" + set l [mc "Stage Hunk For Commit"] } if {$::is_3way_diff || $current_diff_path eq {} @@ -2549,7 +2620,7 @@ bind_button3 $ui_diff [list popup_diff_menu $ctxm %x %y %X %Y] # set main_status [::status_bar::new .status] pack .status -anchor w -side bottom -fill x -$main_status show {Initializing...} +$main_status show [mc "Initializing..."] # -- Load geometry # @@ -2557,17 +2628,19 @@ catch { set gm $repo_config(gui.geometry) wm geometry . [lindex $gm 0] .vpane sash place 0 \ - [lindex [.vpane sash coord 0] 0] \ - [lindex $gm 1] + [lindex $gm 1] \ + [lindex [.vpane sash coord 0] 1] .vpane.files sash place 0 \ - [lindex $gm 2] \ - [lindex [.vpane.files sash coord 0] 1] + [lindex [.vpane.files sash coord 0] 0] \ + [lindex $gm 2] unset gm } # -- Key Bindings # bind $ui_comm <$M1B-Key-Return> {do_commit;break} +bind $ui_comm <$M1B-Key-t> {do_add_selection;break} +bind $ui_comm <$M1B-Key-T> {do_add_selection;break} bind $ui_comm <$M1B-Key-i> {do_add_all;break} bind $ui_comm <$M1B-Key-I> {do_add_all;break} bind $ui_comm <$M1B-Key-x> {tk_textCut %W;break} @@ -2617,6 +2690,8 @@ bind . <$M1B-Key-r> do_rescan bind . <$M1B-Key-R> do_rescan bind . <$M1B-Key-s> do_signoff bind . <$M1B-Key-S> do_signoff +bind . <$M1B-Key-t> do_add_selection +bind . <$M1B-Key-T> do_add_selection bind . <$M1B-Key-i> do_add_all bind . <$M1B-Key-I> do_add_all bind . <$M1B-Key-Return> do_commit @@ -2640,13 +2715,13 @@ focus -force $ui_comm if {[is_Cygwin]} { set ignored_env 0 set suggest_user {} - set msg "Possible environment issues exist. + set msg [mc "Possible environment issues exist. The following environment variables are probably going to be ignored by any Git subprocess run -by [appname]: +by %s: -" +" [appname]] foreach name [array names env] { switch -regexp -- $name { {^GIT_INDEX_FILE$} - @@ -2670,18 +2745,18 @@ by [appname]: } } if {$ignored_env > 0} { - append msg " + append msg [mc " This is due to a known issue with the -Tcl binary distributed by Cygwin." +Tcl binary distributed by Cygwin."] if {$suggest_user ne {}} { - append msg " + append msg [mc " -A good replacement for $suggest_user +A good replacement for %s is placing values for the user.name and user.email settings into your personal ~/.gitconfig file. -" +" $suggest_user] } warn_popup $msg } @@ -2693,8 +2768,14 @@ user.email settings into your personal if {[is_enabled transport]} { load_all_remotes - populate_fetch_menu + set n [.mbar.remote index end] populate_push_menu + populate_fetch_menu + set n [expr {[.mbar.remote index end] - $n}] + if {$n > 0} { + .mbar.remote insert $n separator + } + unset n } if {[winfo exists $ui_comm]} { diff --git a/git-gui/lib/about.tcl b/git-gui/lib/about.tcl new file mode 100644 index 0000000000..719fc547b3 --- /dev/null +++ b/git-gui/lib/about.tcl @@ -0,0 +1,81 @@ +# git-gui about git-gui dialog +# Copyright (C) 2006, 2007 Shawn Pearce + +proc do_about {} { + global appvers copyright oguilib + global tcl_patchLevel tk_patchLevel + + set w .about_dialog + toplevel $w + wm geometry $w "+[winfo rootx .]+[winfo rooty .]" + + pack [git_logo $w.git_logo] -side left -fill y -padx 10 -pady 10 + label $w.header -text [mc "About %s" [appname]] \ + -font font_uibold + pack $w.header -side top -fill x + + frame $w.buttons + button $w.buttons.close -text {Close} \ + -default active \ + -command [list destroy $w] + pack $w.buttons.close -side right + pack $w.buttons -side bottom -fill x -pady 10 -padx 10 + + label $w.desc \ + -text "[mc "git-gui - a graphical user interface for Git."]\n$copyright" \ + -padx 5 -pady 5 \ + -justify left \ + -anchor w \ + -borderwidth 1 \ + -relief solid + pack $w.desc -side top -fill x -padx 5 -pady 5 + + set v {} + append v "git-gui version $appvers\n" + append v "[git version]\n" + append v "\n" + if {$tcl_patchLevel eq $tk_patchLevel} { + append v "Tcl/Tk version $tcl_patchLevel" + } else { + append v "Tcl version $tcl_patchLevel" + append v ", Tk version $tk_patchLevel" + } + + set d {} + append d "git wrapper: $::_git\n" + append d "git exec dir: [gitexec]\n" + append d "git-gui lib: $oguilib" + + label $w.vers \ + -text $v \ + -padx 5 -pady 5 \ + -justify left \ + -anchor w \ + -borderwidth 1 \ + -relief solid + pack $w.vers -side top -fill x -padx 5 -pady 5 + + label $w.dirs \ + -text $d \ + -padx 5 -pady 5 \ + -justify left \ + -anchor w \ + -borderwidth 1 \ + -relief solid + pack $w.dirs -side top -fill x -padx 5 -pady 5 + + menu $w.ctxm -tearoff 0 + $w.ctxm add command \ + -label {Copy} \ + -command " + clipboard clear + clipboard append -format STRING -type STRING -- \[$w.vers cget -text\] + " + + bind $w <Visibility> "grab $w; focus $w.buttons.close" + bind $w <Key-Escape> "destroy $w" + bind $w <Key-Return> "destroy $w" + bind_button3 $w.vers "tk_popup $w.ctxm %X %Y; grab $w; focus $w" + wm title $w "About [appname]" + tkwait window $w +} diff --git a/git-gui/lib/blame.tcl b/git-gui/lib/blame.tcl index 96072847a2..00ecf21333 100644 --- a/git-gui/lib/blame.tcl +++ b/git-gui/lib/blame.tcl @@ -74,11 +74,11 @@ constructor new {i_commit i_path} { set path $i_path make_toplevel top w - wm title $top "[appname] ([reponame]): File Viewer" + wm title $top [append "[appname] ([reponame]): " [mc "File Viewer"]] frame $w.header -background gold label $w.header.commit_l \ - -text {Commit:} \ + -text [mc "Commit:"] \ -background gold \ -anchor w \ -justify left @@ -101,7 +101,7 @@ constructor new {i_commit i_path} { -anchor w \ -justify left label $w.header.path_l \ - -text {File:} \ + -text [mc "File:"] \ -background gold \ -anchor w \ -justify left @@ -246,7 +246,7 @@ constructor new {i_commit i_path} { menu $w.ctxm -tearoff 0 $w.ctxm add command \ - -label "Copy Commit" \ + -label [mc "Copy Commit"] \ -command [cb _copycommit] foreach i $w_columns { @@ -366,7 +366,7 @@ method _load {jump} { set amov_data [list [list]] set asim_data [list [list]] - $status show "Reading $commit:[escape_path $path]..." + $status show [mc "Reading %s..." "$commit:[escape_path $path]"] $w_path conf -text [escape_path $path] if {$commit eq {}} { set fd [open $path r] @@ -470,7 +470,7 @@ method _read_file {fd jump} { _exec_blame $this $w_asim @asim_data \ [list] \ - { copy/move tracking} + [mc "Loading copy/move tracking annotations..."] } } ifdeleted { catch {close $fd} } @@ -489,8 +489,8 @@ method _exec_blame {cur_w cur_d options cur_s} { set blame_lines 0 $status start \ - "Loading$cur_s annotations..." \ - {lines annotated} + $cur_s \ + [mc "lines annotated"] } method _read_blame {fd cur_w cur_d} { @@ -671,10 +671,10 @@ method _read_blame {fd cur_w cur_d} { if {$cur_w eq $w_asim} { _exec_blame $this $w_amov @amov_data \ $original_options \ - { original location} + [mc "Loading original location annotations..."] } else { set current_fd {} - $status stop {Annotation complete.} + $status stop [mc "Annotation complete."] } } else { $status update $blame_lines $total_lines @@ -728,7 +728,7 @@ method _showcommit {cur_w lno} { if {$dat eq {}} { set cmit {} - $w_cviewer insert end "Loading annotation..." still_loading + $w_cviewer insert end [mc "Loading annotation..."] still_loading } else { set cmit [lindex $dat 0] set file [lindex $dat 1] @@ -743,20 +743,14 @@ method _showcommit {cur_w lno} { set author_time {} catch {set author_name $header($cmit,author)} catch {set author_email $header($cmit,author-mail)} - catch {set author_time [clock format \ - $header($cmit,author-time) \ - -format {%Y-%m-%d %H:%M:%S} - ]} + catch {set author_time [format_date $header($cmit,author-time)]} set committer_name {} set committer_email {} set committer_time {} catch {set committer_name $header($cmit,committer)} catch {set committer_email $header($cmit,committer-mail)} - catch {set committer_time [clock format \ - $header($cmit,committer-time) \ - -format {%Y-%m-%d %H:%M:%S} - ]} + catch {set committer_time [format_date $header($cmit,committer-time)]} if {[catch {set msg $header($cmit,message)}]} { set msg {} @@ -790,16 +784,16 @@ method _showcommit {cur_w lno} { } $w_cviewer insert end "commit $cmit\n" header_key - $w_cviewer insert end "Author:\t" header_key + $w_cviewer insert end [strcat [mc "Author:"] "\t"] header_key $w_cviewer insert end "$author_name $author_email" header_val $w_cviewer insert end " $author_time\n" header_val - $w_cviewer insert end "Committer:\t" header_key + $w_cviewer insert end [strcat [mc "Committer:"] "\t"] header_key $w_cviewer insert end "$committer_name $committer_email" header_val $w_cviewer insert end " $committer_time\n" header_val if {$file ne $path} { - $w_cviewer insert end "Original File:\t" header_key + $w_cviewer insert end [strcat [mc "Original File:"] "\t"] header_key $w_cviewer insert end "[escape_path $file]\n" header_val } @@ -892,10 +886,7 @@ method _open_tooltip {cur_w} { set author_time {} catch {set author_name $header($cmit,author)} catch {set summary $header($cmit,summary)} - catch {set author_time [clock format \ - $header($cmit,author-time) \ - -format {%Y-%m-%d %H:%M:%S} - ]} + catch {set author_time [format_date $header($cmit,author-time)]} $tooltip_t insert end "commit $cmit\n" $tooltip_t insert end "$author_name $author_time\n" @@ -914,23 +905,20 @@ method _open_tooltip {cur_w} { set author_time {} catch {set author_name $header($cmit,author)} catch {set summary $header($cmit,summary)} - catch {set author_time [clock format \ - $header($cmit,author-time) \ - -format {%Y-%m-%d %H:%M:%S} - ]} + catch {set author_time [format_date $header($cmit,author-time)]} - $tooltip_t insert end "Originally By:\n" section_header + $tooltip_t insert end [strcat [mc "Originally By:"] "\n"] section_header $tooltip_t insert end "commit $cmit\n" $tooltip_t insert end "$author_name $author_time\n" $tooltip_t insert end "$summary\n" if {$file ne $path} { - $tooltip_t insert end "In File: " section_header + $tooltip_t insert end [strcat [mc "In File:"] " "] section_header $tooltip_t insert end "$file\n" } $tooltip_t insert end "\n" - $tooltip_t insert end "Copied Or Moved Here By:\n" section_header + $tooltip_t insert end [strcat [mc "Copied Or Moved Here By:"] "\n"] section_header $tooltip_t insert end $save } diff --git a/git-gui/lib/branch_checkout.tcl b/git-gui/lib/branch_checkout.tcl index 72c45b4554..6603703ea1 100644 --- a/git-gui/lib/branch_checkout.tcl +++ b/git-gui/lib/branch_checkout.tcl @@ -11,37 +11,37 @@ field opt_detach 0; # force a detached head case? constructor dialog {} { make_toplevel top w - wm title $top "[appname] ([reponame]): Checkout Branch" + wm title $top [append "[appname] ([reponame]): " [mc "Checkout Branch"]] if {$top ne {.}} { wm geometry $top "+[winfo rootx .]+[winfo rooty .]" } - label $w.header -text {Checkout Branch} -font font_uibold + label $w.header -text [mc "Checkout Branch"] -font font_uibold pack $w.header -side top -fill x frame $w.buttons - button $w.buttons.create -text Checkout \ + button $w.buttons.create -text [mc Checkout] \ -default active \ -command [cb _checkout] pack $w.buttons.create -side right - button $w.buttons.cancel -text {Cancel} \ + button $w.buttons.cancel -text [mc Cancel] \ -command [list destroy $w] pack $w.buttons.cancel -side right -padx 5 pack $w.buttons -side bottom -fill x -pady 10 -padx 10 - set w_rev [::choose_rev::new $w.rev {Revision}] + set w_rev [::choose_rev::new $w.rev [mc Revision]] $w_rev bind_listbox <Double-Button-1> [cb _checkout] pack $w.rev -anchor nw -fill both -expand 1 -pady 5 -padx 5 - labelframe $w.options -text {Options} + labelframe $w.options -text [mc Options] checkbutton $w.options.fetch \ - -text {Fetch Tracking Branch} \ + -text [mc "Fetch Tracking Branch"] \ -variable @opt_fetch pack $w.options.fetch -anchor nw checkbutton $w.options.detach \ - -text {Detach From Local Branch} \ + -text [mc "Detach From Local Branch"] \ -variable @opt_detach pack $w.options.detach -anchor nw diff --git a/git-gui/lib/branch_create.tcl b/git-gui/lib/branch_create.tcl index def615d19d..53dfb4ce6b 100644 --- a/git-gui/lib/branch_create.tcl +++ b/git-gui/lib/branch_create.tcl @@ -19,28 +19,28 @@ constructor dialog {} { global repo_config make_toplevel top w - wm title $top "[appname] ([reponame]): Create Branch" + wm title $top [append "[appname] ([reponame]): " [mc "Create Branch"]] if {$top ne {.}} { wm geometry $top "+[winfo rootx .]+[winfo rooty .]" } - label $w.header -text {Create New Branch} -font font_uibold + label $w.header -text [mc "Create New Branch"] -font font_uibold pack $w.header -side top -fill x frame $w.buttons - button $w.buttons.create -text Create \ + button $w.buttons.create -text [mc Create] \ -default active \ -command [cb _create] pack $w.buttons.create -side right - button $w.buttons.cancel -text {Cancel} \ + button $w.buttons.cancel -text [mc Cancel] \ -command [list destroy $w] pack $w.buttons.cancel -side right -padx 5 pack $w.buttons -side bottom -fill x -pady 10 -padx 10 - labelframe $w.desc -text {Branch Name} + labelframe $w.desc -text [mc "Branch Name"] radiobutton $w.desc.name_r \ -anchor w \ - -text {Name:} \ + -text [mc "Name:"] \ -value user \ -variable @name_type set w_name $w.desc.name_t @@ -55,7 +55,7 @@ constructor dialog {} { radiobutton $w.desc.match_r \ -anchor w \ - -text {Match Tracking Branch Name} \ + -text [mc "Match Tracking Branch Name"] \ -value match \ -variable @name_type grid $w.desc.match_r -sticky we -padx {0 5} -columnspan 2 @@ -63,38 +63,38 @@ constructor dialog {} { grid columnconfigure $w.desc 1 -weight 1 pack $w.desc -anchor nw -fill x -pady 5 -padx 5 - set w_rev [::choose_rev::new $w.rev {Starting Revision}] + set w_rev [::choose_rev::new $w.rev [mc "Starting Revision"]] pack $w.rev -anchor nw -fill both -expand 1 -pady 5 -padx 5 - labelframe $w.options -text {Options} + labelframe $w.options -text [mc Options] frame $w.options.merge - label $w.options.merge.l -text {Update Existing Branch:} + label $w.options.merge.l -text [mc "Update Existing Branch:"] pack $w.options.merge.l -side left radiobutton $w.options.merge.no \ - -text No \ + -text [mc No] \ -value none \ -variable @opt_merge pack $w.options.merge.no -side left radiobutton $w.options.merge.ff \ - -text {Fast Forward Only} \ + -text [mc "Fast Forward Only"] \ -value ff \ -variable @opt_merge pack $w.options.merge.ff -side left radiobutton $w.options.merge.reset \ - -text {Reset} \ + -text [mc Reset] \ -value reset \ -variable @opt_merge pack $w.options.merge.reset -side left pack $w.options.merge -anchor nw checkbutton $w.options.fetch \ - -text {Fetch Tracking Branch} \ + -text [mc "Fetch Tracking Branch"] \ -variable @opt_fetch pack $w.options.fetch -anchor nw checkbutton $w.options.checkout \ - -text {Checkout After Creation} \ + -text [mc "Checkout After Creation"] \ -variable @opt_checkout pack $w.options.checkout -anchor nw pack $w.options -anchor nw -fill x -pady 5 -padx 5 @@ -128,7 +128,7 @@ method _create {} { -type ok \ -title [wm title $w] \ -parent $w \ - -message "Please select a tracking branch." + -message [mc "Please select a tracking branch."] return } if {![regsub ^refs/heads/ [lindex $spec 2] {} newbranch]} { @@ -137,7 +137,7 @@ method _create {} { -type ok \ -title [wm title $w] \ -parent $w \ - -message "Tracking branch [$w get] is not a branch in the remote repository." + -message [mc "Tracking branch %s is not a branch in the remote repository." [$w get]] return } } @@ -150,7 +150,7 @@ method _create {} { -type ok \ -title [wm title $w] \ -parent $w \ - -message "Please supply a branch name." + -message [mc "Please supply a branch name."] focus $w_name return } @@ -161,7 +161,7 @@ method _create {} { -type ok \ -title [wm title $w] \ -parent $w \ - -message "'$newbranch' is not an acceptable branch name." + -message [mc "'%s' is not an acceptable branch name." $newbranch] focus $w_name return } diff --git a/git-gui/lib/branch_delete.tcl b/git-gui/lib/branch_delete.tcl index c7573c6c72..86c4f73370 100644 --- a/git-gui/lib/branch_delete.tcl +++ b/git-gui/lib/branch_delete.tcl @@ -12,29 +12,29 @@ constructor dialog {} { global current_branch make_toplevel top w - wm title $top "[appname] ([reponame]): Delete Branch" + wm title $top [append "[appname] ([reponame]): " [mc "Delete Branch"]] if {$top ne {.}} { wm geometry $top "+[winfo rootx .]+[winfo rooty .]" } - label $w.header -text {Delete Local Branch} -font font_uibold + label $w.header -text [mc "Delete Local Branch"] -font font_uibold pack $w.header -side top -fill x frame $w.buttons set w_delete $w.buttons.delete button $w_delete \ - -text Delete \ + -text [mc Delete] \ -default active \ -state disabled \ -command [cb _delete] pack $w_delete -side right button $w.buttons.cancel \ - -text {Cancel} \ + -text [mc Cancel] \ -command [list destroy $w] pack $w.buttons.cancel -side right -padx 5 pack $w.buttons -side bottom -fill x -pady 10 -padx 10 - labelframe $w.list -text {Local Branches} + labelframe $w.list -text [mc "Local Branches"] set w_heads $w.list.l listbox $w_heads \ -height 10 \ @@ -49,9 +49,9 @@ constructor dialog {} { set w_check [choose_rev::new \ $w.check \ - {Delete Only If Merged Into} \ + [mc "Delete Only If Merged Into"] \ ] - $w_check none {Always (Do not perform merge test.)} + $w_check none [mc "Always (Do not perform merge test.)"] pack $w.check -anchor nw -fill x -pady 5 -padx 5 foreach h [load_all_heads] { @@ -100,7 +100,7 @@ method _delete {} { lappend to_delete [list $b $o] } if {$not_merged ne {}} { - set msg "The following branches are not completely merged into [$w_check get]: + set msg "[mc "The following branches are not completely merged into %s:" [$w_check get]] - [join $not_merged "\n - "]" tk_messageBox \ @@ -112,9 +112,7 @@ method _delete {} { } if {$to_delete eq {}} return if {$check_cmt eq {}} { - set msg {Recovering deleted branches is difficult. - -Delete the selected branches?} + set msg [mc "Recovering deleted branches is difficult. \n\n Delete the selected branches?"] if {[tk_messageBox \ -icon warning \ -type yesno \ @@ -140,7 +138,7 @@ Delete the selected branches?} -type ok \ -title [wm title $w] \ -parent $w \ - -message "Failed to delete branches:\n$failed" + -message [mc "Failed to delete branches:\n%s" $failed] } destroy $w diff --git a/git-gui/lib/branch_rename.tcl b/git-gui/lib/branch_rename.tcl index 1cadc31d20..166538808f 100644 --- a/git-gui/lib/branch_rename.tcl +++ b/git-gui/lib/branch_rename.tcl @@ -11,7 +11,7 @@ constructor dialog {} { global current_branch make_toplevel top w - wm title $top "[appname] ([reponame]): Rename Branch" + wm title $top [append "[appname] ([reponame]): " [mc "Rename Branch"]] if {$top ne {.}} { wm geometry $top "+[winfo rootx .]+[winfo rooty .]" } @@ -19,24 +19,24 @@ constructor dialog {} { set oldname $current_branch set newname [get_config gui.newbranchtemplate] - label $w.header -text {Rename Branch} -font font_uibold + label $w.header -text [mc "Rename Branch"] -font font_uibold pack $w.header -side top -fill x frame $w.buttons - button $w.buttons.rename -text Rename \ + button $w.buttons.rename -text [mc Rename] \ -default active \ -command [cb _rename] pack $w.buttons.rename -side right - button $w.buttons.cancel -text {Cancel} \ + button $w.buttons.cancel -text [mc Cancel] \ -command [list destroy $w] pack $w.buttons.cancel -side right -padx 5 pack $w.buttons -side bottom -fill x -pady 10 -padx 10 frame $w.rename - label $w.rename.oldname_l -text {Branch:} + label $w.rename.oldname_l -text [mc "Branch:"] eval tk_optionMenu $w.rename.oldname_m @oldname [load_all_heads] - label $w.rename.newname_l -text {New Name:} + label $w.rename.newname_l -text [mc "New Name:"] entry $w.rename.newname_t \ -borderwidth 1 \ -relief sunken \ @@ -72,7 +72,7 @@ method _rename {} { -type ok \ -title [wm title $w] \ -parent $w \ - -message "Please select a branch to rename." + -message [mc "Please select a branch to rename."] focus $w.rename.oldname_m return } @@ -83,7 +83,7 @@ method _rename {} { -type ok \ -title [wm title $w] \ -parent $w \ - -message "Please supply a branch name." + -message [mc "Please supply a branch name."] focus $w.rename.newname_t return } @@ -93,7 +93,7 @@ method _rename {} { -type ok \ -title [wm title $w] \ -parent $w \ - -message "Branch '$newname' already exists." + -message [mc "Branch '%s' already exists." $newname] focus $w.rename.newname_t return } @@ -103,7 +103,7 @@ method _rename {} { -type ok \ -title [wm title $w] \ -parent $w \ - -message "We do not like '$newname' as a branch name." + -message [mc "'%s' is not an acceptable branch name." $newname] focus $w.rename.newname_t return } @@ -114,7 +114,7 @@ method _rename {} { -type ok \ -title [wm title $w] \ -parent $w \ - -message "Failed to rename '$oldname'.\n\n$err" + -message [strcat [mc "Failed to rename '%s'." $oldname] "\n\n$err"] return } diff --git a/git-gui/lib/browser.tcl b/git-gui/lib/browser.tcl index 31349009ae..53d5a62816 100644 --- a/git-gui/lib/browser.tcl +++ b/git-gui/lib/browser.tcl @@ -14,7 +14,7 @@ field w field browser_commit field browser_path field browser_files {} -field browser_status {Starting...} +field browser_status [mc "Starting..."] field browser_stack {} field browser_busy 1 @@ -23,7 +23,7 @@ field ls_buf {}; # Buffered record output from ls-tree constructor new {commit {path {}}} { global cursor_ptr M1B make_toplevel top w - wm title $top "[appname] ([reponame]): File Browser" + wm title $top [append "[appname] ([reponame]): " [mc "File Browser"]] set browser_commit $commit set browser_path $browser_commit:$path @@ -122,7 +122,7 @@ method _parent {} { } else { regsub {/[^/]+$} $browser_path {} browser_path } - set browser_status "Loading $browser_path..." + set browser_status [mc "Loading %s..." $browser_path] _ls $this [lindex $parent 0] [lindex $parent 1] } } @@ -139,7 +139,7 @@ method _enter {} { tree { set name [lindex $info 2] set escn [escape_path $name] - set browser_status "Loading $escn..." + set browser_status [mc "Loading %s..." $escn] append browser_path $escn _ls $this [lindex $info 1] $name } @@ -183,7 +183,7 @@ method _ls {tree_id {name {}}} { -align center -padx 5 -pady 1 \ -name icon0 \ -image ::browser::img_parent - $w insert end {[Up To Parent]} + $w insert end [mc "\[Up To Parent\]"] lappend browser_files parent } lappend browser_stack [list $tree_id $name] @@ -242,7 +242,7 @@ method _read {fd} { if {[eof $fd]} { close $fd - set browser_status Ready. + set browser_status [mc "Ready."] set browser_busy 0 set ls_buf {} if {$n > 0} { @@ -263,27 +263,27 @@ field w_rev ; # mega-widget to pick the initial revision constructor dialog {} { make_toplevel top w - wm title $top "[appname] ([reponame]): Browse Branch Files" + wm title $top [append "[appname] ([reponame]): " [mc "Browse Branch Files"]] if {$top ne {.}} { wm geometry $top "+[winfo rootx .]+[winfo rooty .]" } label $w.header \ - -text {Browse Branch Files} \ + -text [mc "Browse Branch Files"] \ -font font_uibold pack $w.header -side top -fill x frame $w.buttons - button $w.buttons.browse -text Browse \ + button $w.buttons.browse -text [mc Browse] \ -default active \ -command [cb _open] pack $w.buttons.browse -side right - button $w.buttons.cancel -text {Cancel} \ + button $w.buttons.cancel -text [mc Cancel] \ -command [list destroy $w] pack $w.buttons.cancel -side right -padx 5 pack $w.buttons -side bottom -fill x -pady 10 -padx 10 - set w_rev [::choose_rev::new $w.rev {Revision}] + set w_rev [::choose_rev::new $w.rev [mc Revision]] $w_rev bind_listbox <Double-Button-1> [cb _open] pack $w.rev -anchor nw -fill both -expand 1 -pady 5 -padx 5 diff --git a/git-gui/lib/checkout_op.tcl b/git-gui/lib/checkout_op.tcl index 76f04f2854..f243966924 100644 --- a/git-gui/lib/checkout_op.tcl +++ b/git-gui/lib/checkout_op.tcl @@ -76,7 +76,7 @@ method run {} { _toplevel $this {Refreshing Tracking Branch} set w_cons [::console::embed \ $w.console \ - "Fetching $r_name from $remote"] + [mc "Fetching %s from %s" $r_name $remote]] pack $w.console -fill both -expand 1 $w_cons exec $cmd [cb _finish_fetch] @@ -124,7 +124,7 @@ method _finish_fetch {ok} { } if {[catch {set new_hash [git rev-parse --verify "$l_trck^0"]} err]} { set ok 0 - $w_cons insert "fatal: Cannot resolve $l_trck" + $w_cons insert [mc "fatal: Cannot resolve %s" $l_trck] $w_cons insert $err } } @@ -137,7 +137,7 @@ method _finish_fetch {ok} { destroy $w set w {} } else { - button $w.close -text Close -command [list destroy $w] + button $w.close -text [mc Close] -command [list destroy $w] pack $w.close -side bottom -anchor e -padx 10 -pady 10 } @@ -166,7 +166,7 @@ method _update_ref {} { # Assume it does not exist, and that is what the error was. # if {!$create} { - _error $this "Branch '$newbranch' does not exist." + _error $this [mc "Branch '%s' does not exist." $newbranch] return 0 } @@ -176,7 +176,7 @@ method _update_ref {} { # We were told to create it, but not do a merge. # Bad. Name shouldn't have existed. # - _error $this "Branch '$newbranch' already exists." + _error $this [mc "Branch '%s' already exists." $newbranch] return 0 } elseif {!$create && $merge_type eq {none}} { # We aren't creating, it exists and we don't merge. @@ -203,7 +203,7 @@ method _update_ref {} { set new $cur set new_hash $cur } else { - _error $this "Branch '$newbranch' already exists.\n\nIt cannot fast-forward to $new_expr.\nA merge is required." + _error $this [mc "Branch '%s' already exists.\n\nIt cannot fast-forward to %s.\nA merge is required." $newbranch $new_expr] return 0 } } @@ -217,7 +217,7 @@ method _update_ref {} { } } default { - _error $this "Merge strategy '$merge_type' not supported." + _error $this [mc "Merge strategy '%s' not supported." $merge_type] return 0 } } @@ -236,7 +236,7 @@ method _update_ref {} { if {[catch { git update-ref -m $reflog_msg $ref $new $cur } err]} { - _error $this "Failed to update '$newbranch'.\n\n$err" + _error $this [strcat [mc "Failed to update '%s'." $newbranch] "\n\n$err"] return 0 } } @@ -248,7 +248,7 @@ method _checkout {} { if {[lock_index checkout_op]} { after idle [cb _start_checkout] } else { - _error $this "Staging area (index) is already locked." + _error $this [mc "Staging area (index) is already locked."] delete_this } } @@ -263,12 +263,12 @@ method _start_checkout {} { && $curType eq {normal} && $curHEAD eq $HEAD} { } elseif {$commit_type ne $curType || $HEAD ne $curHEAD} { - info_popup {Last scanned state does not match repository state. + info_popup [mc "Last scanned state does not match repository state. Another Git program has modified this repository since the last scan. A rescan must be performed before the current branch can be changed. The rescan will be automatically started now. -} +"] unlock_index rescan ui_ready delete_this @@ -319,7 +319,7 @@ method _readtree {} { set readtree_d {} $::main_status start \ - "Updating working directory to '[_name $this]'..." \ + [mc "Updating working directory to '%s'..." [_name $this]] \ {files checked out} set fd [git_read --stderr read-tree \ @@ -350,12 +350,12 @@ method _readtree_wait {fd} { if {[catch {close $fd}]} { set err $readtree_d regsub {^fatal: } $err {} err - $::main_status stop "Aborted checkout of '[_name $this]' (file level merging is required)." - warn_popup "File level merge required. + $::main_status stop [mc "Aborted checkout of '%s' (file level merging is required)." [_name $this]] + warn_popup [strcat [mc "File level merge required."] " $err -Staying on branch '$current_branch'." +" [mc "Staying on branch '%s'." $current_branch]] unlock_index delete_this return @@ -426,9 +426,9 @@ method _after_readtree {} { } if {$is_detached} { - info_popup "You are no longer on a local branch. + info_popup [mc "You are no longer on a local branch. -If you wanted to be on a branch, create one now starting from 'This Detached Checkout'." +If you wanted to be on a branch, create one now starting from 'This Detached Checkout'."] } # -- Update our repository state. If we were previously in @@ -443,7 +443,7 @@ If you wanted to be on a branch, create one now starting from 'This Detached Che $ui_comm delete 0.0 end $ui_comm edit reset $ui_comm edit modified false - rescan [list ui_status "Checked out '$name'."] + rescan [list ui_status [mc "Checked out '%s'." $name]] } else { repository_state commit_type HEAD MERGE_HEAD set PARENT $HEAD @@ -475,7 +475,7 @@ method _confirm_reset {cur} { pack [label $w.msg1 \ -anchor w \ -justify left \ - -text "Resetting '$name' to $new_expr will lose the following commits:" \ + -text [mc "Resetting '%s' to '%s' will lose the following commits:" $name $new_expr]\ ] -anchor w set list $w.list.l @@ -497,21 +497,21 @@ method _confirm_reset {cur} { pack [label $w.msg2 \ -anchor w \ -justify left \ - -text {Recovering lost commits may not be easy.} \ + -text [mc "Recovering lost commits may not be easy."] \ ] pack [label $w.msg3 \ -anchor w \ -justify left \ - -text "Reset '$name'?" \ + -text [mc "Reset '%s'?" $name] \ ] frame $w.buttons button $w.buttons.visualize \ - -text Visualize \ + -text [mc Visualize] \ -command $gitk pack $w.buttons.visualize -side left button $w.buttons.reset \ - -text Reset \ + -text [mc Reset] \ -command " set @reset_ok 1 destroy $w @@ -519,7 +519,7 @@ method _confirm_reset {cur} { pack $w.buttons.reset -side right button $w.buttons.cancel \ -default active \ - -text Cancel \ + -text [mc Cancel] \ -command [list destroy $w] pack $w.buttons.cancel -side right -padx 5 pack $w.buttons -side bottom -fill x -pady 10 -padx 10 @@ -575,13 +575,13 @@ method _toplevel {title} { } method _fatal {err} { - error_popup "Failed to set current branch. + error_popup [strcat [mc "Failed to set current branch. This working directory is only partially switched. We successfully updated your files, but failed to update an internal Git file. -This should not have occurred. [appname] will now close and give up. +This should not have occurred. %s will now close and give up." [appname]] " -$err" +$err"] exit 1 } diff --git a/git-gui/lib/choose_repository.tcl b/git-gui/lib/choose_repository.tcl new file mode 100644 index 0000000000..2bac50e149 --- /dev/null +++ b/git-gui/lib/choose_repository.tcl @@ -0,0 +1,1044 @@ +# git-gui Git repository chooser +# Copyright (C) 2007 Shawn Pearce + +class choose_repository { + +field top +field w +field w_body ; # Widget holding the center content +field w_next ; # Next button +field w_quit ; # Quit button +field o_cons ; # Console object (if active) +field w_types ; # List of type buttons in clone +field w_recentlist ; # Listbox containing recent repositories + +field done 0 ; # Finished picking the repository? +field local_path {} ; # Where this repository is locally +field origin_url {} ; # Where we are cloning from +field origin_name origin ; # What we shall call 'origin' +field clone_type hardlink ; # Type of clone to construct +field readtree_err ; # Error output from read-tree (if any) +field sorted_recent ; # recent repositories (sorted) + +constructor pick {} { + global M1T M1B + + make_toplevel top w + wm title $top [mc "Git Gui"] + + if {$top eq {.}} { + menu $w.mbar -tearoff 0 + $top configure -menu $w.mbar + + set m_repo $w.mbar.repository + $w.mbar add cascade \ + -label [mc Repository] \ + -menu $m_repo + menu $m_repo + + if {[is_MacOSX]} { + $w.mbar add cascade -label [mc Apple] -menu .mbar.apple + menu $w.mbar.apple + $w.mbar.apple add command \ + -label [mc "About %s" [appname]] \ + -command do_about + } else { + $w.mbar add cascade -label [mc Help] -menu $w.mbar.help + menu $w.mbar.help + $w.mbar.help add command \ + -label [mc "About %s" [appname]] \ + -command do_about + } + + wm protocol $top WM_DELETE_WINDOW exit + bind $top <$M1B-q> exit + bind $top <$M1B-Q> exit + bind $top <Key-Escape> exit + } else { + wm geometry $top "+[winfo rootx .]+[winfo rooty .]" + bind $top <Key-Escape> [list destroy $top] + set m_repo {} + } + + pack [git_logo $w.git_logo] -side left -fill y -padx 10 -pady 10 + + set w_body $w.body + set opts $w_body.options + frame $w_body + text $opts \ + -cursor $::cursor_ptr \ + -relief flat \ + -background [$w_body cget -background] \ + -wrap none \ + -spacing1 5 \ + -width 50 \ + -height 3 + pack $opts -anchor w -fill x + + $opts tag conf link_new -foreground blue -underline 1 + $opts tag bind link_new <1> [cb _next new] + $opts insert end [mc "Create New Repository"] link_new + $opts insert end "\n" + if {$m_repo ne {}} { + $m_repo add command \ + -command [cb _next new] \ + -accelerator $M1T-N \ + -label [mc "New..."] + bind $top <$M1B-n> [cb _next new] + bind $top <$M1B-N> [cb _next new] + } + + $opts tag conf link_clone -foreground blue -underline 1 + $opts tag bind link_clone <1> [cb _next clone] + $opts insert end [mc "Clone Existing Repository"] link_clone + $opts insert end "\n" + if {$m_repo ne {}} { + $m_repo add command \ + -command [cb _next clone] \ + -accelerator $M1T-C \ + -label [mc "Clone..."] + bind $top <$M1B-c> [cb _next clone] + bind $top <$M1B-C> [cb _next clone] + } + + $opts tag conf link_open -foreground blue -underline 1 + $opts tag bind link_open <1> [cb _next open] + $opts insert end [mc "Open Existing Repository"] link_open + $opts insert end "\n" + if {$m_repo ne {}} { + $m_repo add command \ + -command [cb _next open] \ + -accelerator $M1T-O \ + -label [mc "Open..."] + bind $top <$M1B-o> [cb _next open] + bind $top <$M1B-O> [cb _next open] + } + + $opts conf -state disabled + + set sorted_recent [_get_recentrepos] + if {[llength $sorted_recent] > 0} { + if {$m_repo ne {}} { + $m_repo add separator + $m_repo add command \ + -state disabled \ + -label [mc "Recent Repositories"] + } + + label $w_body.space + label $w_body.recentlabel \ + -anchor w \ + -text [mc "Open Recent Repository:"] + set w_recentlist $w_body.recentlist + text $w_recentlist \ + -cursor $::cursor_ptr \ + -relief flat \ + -background [$w_body.recentlabel cget -background] \ + -wrap none \ + -width 50 \ + -height 10 + $w_recentlist tag conf link \ + -foreground blue \ + -underline 1 + set home $::env(HOME) + if {[is_Cygwin]} { + set home [exec cygpath --windows --absolute $home] + } + set home "[file normalize $home]/" + set hlen [string length $home] + foreach p $sorted_recent { + set path $p + if {[string equal -length $hlen $home $p]} { + set p "~/[string range $p $hlen end]" + } + regsub -all "\n" $p "\\n" p + $w_recentlist insert end $p link + $w_recentlist insert end "\n" + + if {$m_repo ne {}} { + $m_repo add command \ + -command [cb _open_recent_path $path] \ + -label " $p" + } + } + $w_recentlist conf -state disabled + $w_recentlist tag bind link <1> [cb _open_recent %x,%y] + pack $w_body.space -anchor w -fill x + pack $w_body.recentlabel -anchor w -fill x + pack $w_recentlist -anchor w -fill x + } + pack $w_body -fill x -padx 10 -pady 10 + + frame $w.buttons + set w_next $w.buttons.next + set w_quit $w.buttons.quit + button $w_quit \ + -text [mc "Quit"] \ + -command exit + pack $w_quit -side right -padx 5 + pack $w.buttons -side bottom -fill x -padx 10 -pady 10 + + if {$m_repo ne {}} { + $m_repo add separator + $m_repo add command \ + -label [mc Quit] \ + -command exit \ + -accelerator $M1T-Q + } + + bind $top <Return> [cb _invoke_next] + bind $top <Visibility> " + [cb _center] + grab $top + focus $top + bind $top <Visibility> {} + " + wm deiconify $top + tkwait variable @done + + if {$top eq {.}} { + eval destroy [winfo children $top] + } +} + +proc _home {} { + if {[catch {set h $::env(HOME)}] + || ![file isdirectory $h]} { + set h . + } + return $h +} + +method _center {} { + set nx [winfo reqwidth $top] + set ny [winfo reqheight $top] + set rx [expr {([winfo screenwidth $top] - $nx) / 3}] + set ry [expr {([winfo screenheight $top] - $ny) / 3}] + wm geometry $top [format {+%d+%d} $rx $ry] +} + +method _invoke_next {} { + if {[winfo exists $w_next]} { + uplevel #0 [$w_next cget -command] + } +} + +proc _get_recentrepos {} { + set recent [list] + foreach p [get_config gui.recentrepo] { + if {[_is_git [file join $p .git]]} { + lappend recent $p + } + } + return [lsort $recent] +} + +proc _unset_recentrepo {p} { + regsub -all -- {([()\[\]{}\.^$+*?\\])} $p {\\\1} p + git config --global --unset gui.recentrepo "^$p\$" +} + +proc _append_recentrepos {path} { + set path [file normalize $path] + set recent [get_config gui.recentrepo] + + if {[lindex $recent end] eq $path} { + return + } + + set i [lsearch $recent $path] + if {$i >= 0} { + _unset_recentrepo $path + set recent [lreplace $recent $i $i] + } + + lappend recent $path + git config --global --add gui.recentrepo $path + + while {[llength $recent] > 10} { + _unset_recentrepo [lindex $recent 0] + set recent [lrange $recent 1 end] + } +} + +method _open_recent {xy} { + set id [lindex [split [$w_recentlist index @$xy] .] 0] + set local_path [lindex $sorted_recent [expr {$id - 1}]] + _do_open2 $this +} + +method _open_recent_path {p} { + set local_path $p + _do_open2 $this +} + +method _next {action} { + destroy $w_body + if {![winfo exists $w_next]} { + button $w_next -default active + pack $w_next -side right -padx 5 -before $w_quit + } + _do_$action $this +} + +method _write_local_path {args} { + if {$local_path eq {}} { + $w_next conf -state disabled + } else { + $w_next conf -state normal + } +} + +method _git_init {} { + if {[file exists $local_path]} { + error_popup [mc "Location %s already exists." $local_path] + return 0 + } + + if {[catch {file mkdir $local_path} err]} { + error_popup [strcat \ + [mc "Failed to create repository %s:" $local_path] \ + "\n\n$err"] + return 0 + } + + if {[catch {cd $local_path} err]} { + error_popup [strcat \ + [mc "Failed to create repository %s:" $local_path] \ + "\n\n$err"] + return 0 + } + + if {[catch {git init} err]} { + error_popup [strcat \ + [mc "Failed to create repository %s:" $local_path] \ + "\n\n$err"] + return 0 + } + + _append_recentrepos [pwd] + set ::_gitdir .git + set ::_prefix {} + return 1 +} + +proc _is_git {path} { + if {[file exists [file join $path HEAD]] + && [file exists [file join $path objects]] + && [file exists [file join $path config]]} { + return 1 + } + if {[is_Cygwin]} { + if {[file exists [file join $path HEAD]] + && [file exists [file join $path objects.lnk]] + && [file exists [file join $path config.lnk]]} { + return 1 + } + } + return 0 +} + +proc _objdir {path} { + set objdir [file join $path .git objects] + if {[file isdirectory $objdir]} { + return $objdir + } + + set objdir [file join $path objects] + if {[file isdirectory $objdir]} { + return $objdir + } + + if {[is_Cygwin]} { + set objdir [file join $path .git objects.lnk] + if {[file isfile $objdir]} { + return [win32_read_lnk $objdir] + } + + set objdir [file join $path objects.lnk] + if {[file isfile $objdir]} { + return [win32_read_lnk $objdir] + } + } + + return {} +} + +###################################################################### +## +## Create New Repository + +method _do_new {} { + $w_next conf \ + -state disabled \ + -command [cb _do_new2] \ + -text [mc "Create"] + + frame $w_body + label $w_body.h \ + -font font_uibold \ + -text [mc "Create New Repository"] + pack $w_body.h -side top -fill x -pady 10 + pack $w_body -fill x -padx 10 + + frame $w_body.where + label $w_body.where.l -text [mc "Directory:"] + entry $w_body.where.t \ + -textvariable @local_path \ + -font font_diff \ + -width 50 + button $w_body.where.b \ + -text [mc "Browse"] \ + -command [cb _new_local_path] + + pack $w_body.where.b -side right + pack $w_body.where.l -side left + pack $w_body.where.t -fill x + pack $w_body.where -fill x + + trace add variable @local_path write [cb _write_local_path] + bind $w_body.h <Destroy> [list trace remove variable @local_path write [cb _write_local_path]] + update + focus $w_body.where.t +} + +method _new_local_path {} { + if {$local_path ne {}} { + set p [file dirname $local_path] + } else { + set p [_home] + } + + set p [tk_chooseDirectory \ + -initialdir $p \ + -parent $top \ + -title [mc "Git Repository"] \ + -mustexist false] + if {$p eq {}} return + + set p [file normalize $p] + if {[file isdirectory $p]} { + foreach i [glob \ + -directory $p \ + -tails \ + -nocomplain \ + * .*] { + switch -- $i { + . continue + .. continue + default { + error_popup [mc "Directory %s already exists." $p] + return + } + } + } + if {[catch {file delete $p} err]} { + error_popup [strcat \ + [mc "Directory %s already exists." $p] \ + "\n\n$err"] + return + } + } elseif {[file exists $p]} { + error_popup [mc "File %s already exists." $p] + return + } + set local_path $p +} + +method _do_new2 {} { + if {![_git_init $this]} { + return + } + set done 1 +} + +###################################################################### +## +## Clone Existing Repository + +method _do_clone {} { + $w_next conf \ + -state disabled \ + -command [cb _do_clone2] \ + -text [mc "Clone"] + + frame $w_body + label $w_body.h \ + -font font_uibold \ + -text [mc "Clone Existing Repository"] + pack $w_body.h -side top -fill x -pady 10 + pack $w_body -fill x -padx 10 + + set args $w_body.args + frame $w_body.args + pack $args -fill both + + label $args.origin_l -text [mc "URL:"] + entry $args.origin_t \ + -textvariable @origin_url \ + -font font_diff \ + -width 50 + button $args.origin_b \ + -text [mc "Browse"] \ + -command [cb _open_origin] + grid $args.origin_l $args.origin_t $args.origin_b -sticky ew + + label $args.where_l -text [mc "Directory:"] + entry $args.where_t \ + -textvariable @local_path \ + -font font_diff \ + -width 50 + button $args.where_b \ + -text [mc "Browse"] \ + -command [cb _new_local_path] + grid $args.where_l $args.where_t $args.where_b -sticky ew + + label $args.type_l -text [mc "Clone Type:"] + frame $args.type_f + set w_types [list] + lappend w_types [radiobutton $args.type_f.hardlink \ + -state disabled \ + -anchor w \ + -text [mc "Standard (Fast, Semi-Redundant, Hardlinks)"] \ + -variable @clone_type \ + -value hardlink] + lappend w_types [radiobutton $args.type_f.full \ + -state disabled \ + -anchor w \ + -text [mc "Full Copy (Slower, Redundant Backup)"] \ + -variable @clone_type \ + -value full] + lappend w_types [radiobutton $args.type_f.shared \ + -state disabled \ + -anchor w \ + -text [mc "Shared (Fastest, Not Recommended, No Backup)"] \ + -variable @clone_type \ + -value shared] + foreach r $w_types { + pack $r -anchor w + } + grid $args.type_l $args.type_f -sticky new + + grid columnconfigure $args 1 -weight 1 + + trace add variable @local_path write [cb _update_clone] + trace add variable @origin_url write [cb _update_clone] + bind $w_body.h <Destroy> " + [list trace remove variable @local_path write [cb _update_clone]] + [list trace remove variable @origin_url write [cb _update_clone]] + " + update + focus $args.origin_t +} + +method _open_origin {} { + if {$origin_url ne {} && [file isdirectory $origin_url]} { + set p $origin_url + } else { + set p [_home] + } + + set p [tk_chooseDirectory \ + -initialdir $p \ + -parent $top \ + -title [mc "Git Repository"] \ + -mustexist true] + if {$p eq {}} return + + set p [file normalize $p] + if {![_is_git [file join $p .git]] && ![_is_git $p]} { + error_popup [mc "Not a Git repository: %s" [file tail $p]] + return + } + set origin_url $p +} + +method _update_clone {args} { + if {$local_path ne {} && $origin_url ne {}} { + $w_next conf -state normal + } else { + $w_next conf -state disabled + } + + if {$origin_url ne {} && + ( [_is_git [file join $origin_url .git]] + || [_is_git $origin_url])} { + set e normal + if {[[lindex $w_types 0] cget -state] eq {disabled}} { + set clone_type hardlink + } + } else { + set e disabled + set clone_type full + } + + foreach r $w_types { + $r conf -state $e + } +} + +method _do_clone2 {} { + if {[file isdirectory $origin_url]} { + set origin_url [file normalize $origin_url] + } + + if {$clone_type eq {hardlink} && ![file isdirectory $origin_url]} { + error_popup [mc "Standard only available for local repository."] + return + } + if {$clone_type eq {shared} && ![file isdirectory $origin_url]} { + error_popup [mc "Shared only available for local repository."] + return + } + + if {$clone_type eq {hardlink} || $clone_type eq {shared}} { + set objdir [_objdir $origin_url] + if {$objdir eq {}} { + error_popup [mc "Not a Git repository: %s" [file tail $origin_url]] + return + } + } + + set giturl $origin_url + if {[is_Cygwin] && [file isdirectory $giturl]} { + set giturl [exec cygpath --unix --absolute $giturl] + if {$clone_type eq {shared}} { + set objdir [exec cygpath --unix --absolute $objdir] + } + } + + if {![_git_init $this]} return + set local_path [pwd] + + if {[catch { + git config remote.$origin_name.url $giturl + git config remote.$origin_name.fetch +refs/heads/*:refs/remotes/$origin_name/* + } err]} { + error_popup [strcat [mc "Failed to configure origin"] "\n\n$err"] + return + } + + destroy $w_body $w_next + + switch -exact -- $clone_type { + hardlink { + set o_cons [status_bar::two_line $w_body] + pack $w_body -fill x -padx 10 -pady 10 + + $o_cons start \ + [mc "Counting objects"] \ + [mc "buckets"] + update + + if {[file exists [file join $objdir info alternates]]} { + set pwd [pwd] + if {[catch { + file mkdir [gitdir objects info] + set f_in [open [file join $objdir info alternates] r] + set f_cp [open [gitdir objects info alternates] w] + fconfigure $f_in -translation binary -encoding binary + fconfigure $f_cp -translation binary -encoding binary + cd $objdir + while {[gets $f_in line] >= 0} { + if {[is_Cygwin]} { + puts $f_cp [exec cygpath --unix --absolute $line] + } else { + puts $f_cp [file normalize $line] + } + } + close $f_in + close $f_cp + cd $pwd + } err]} { + catch {cd $pwd} + _clone_failed $this [mc "Unable to copy objects/info/alternates: %s" $err] + return + } + } + + set tolink [list] + set buckets [glob \ + -tails \ + -nocomplain \ + -directory [file join $objdir] ??] + set bcnt [expr {[llength $buckets] + 2}] + set bcur 1 + $o_cons update $bcur $bcnt + update + + file mkdir [file join .git objects pack] + foreach i [glob -tails -nocomplain \ + -directory [file join $objdir pack] *] { + lappend tolink [file join pack $i] + } + $o_cons update [incr bcur] $bcnt + update + + foreach i $buckets { + file mkdir [file join .git objects $i] + foreach j [glob -tails -nocomplain \ + -directory [file join $objdir $i] *] { + lappend tolink [file join $i $j] + } + $o_cons update [incr bcur] $bcnt + update + } + $o_cons stop + + if {$tolink eq {}} { + info_popup [strcat \ + [mc "Nothing to clone from %s." $origin_url] \ + "\n" \ + [mc "The 'master' branch has not been initialized."] \ + ] + destroy $w_body + set done 1 + return + } + + set i [lindex $tolink 0] + if {[catch { + file link -hard \ + [file join .git objects $i] \ + [file join $objdir $i] + } err]} { + info_popup [mc "Hardlinks are unavailable. Falling back to copying."] + set i [_copy_files $this $objdir $tolink] + } else { + set i [_link_files $this $objdir [lrange $tolink 1 end]] + } + if {!$i} return + + destroy $w_body + } + full { + set o_cons [console::embed \ + $w_body \ + [mc "Cloning from %s" $origin_url]] + pack $w_body -fill both -expand 1 -padx 10 + $o_cons exec \ + [list git fetch --no-tags -k $origin_name] \ + [cb _do_clone_tags] + } + shared { + set fd [open [gitdir objects info alternates] w] + fconfigure $fd -translation binary + puts $fd $objdir + close $fd + } + } + + if {$clone_type eq {hardlink} || $clone_type eq {shared}} { + if {![_clone_refs $this]} return + set pwd [pwd] + if {[catch { + cd $origin_url + set HEAD [git rev-parse --verify HEAD^0] + } err]} { + _clone_failed $this [mc "Not a Git repository: %s" [file tail $origin_url]] + return 0 + } + cd $pwd + _do_clone_checkout $this $HEAD + } +} + +method _copy_files {objdir tocopy} { + $o_cons start \ + [mc "Copying objects"] \ + [mc "KiB"] + set tot 0 + set cmp 0 + foreach p $tocopy { + incr tot [file size [file join $objdir $p]] + } + foreach p $tocopy { + if {[catch { + set f_in [open [file join $objdir $p] r] + set f_cp [open [file join .git objects $p] w] + fconfigure $f_in -translation binary -encoding binary + fconfigure $f_cp -translation binary -encoding binary + + while {![eof $f_in]} { + incr cmp [fcopy $f_in $f_cp -size 16384] + $o_cons update \ + [expr {$cmp / 1024}] \ + [expr {$tot / 1024}] + update + } + + close $f_in + close $f_cp + } err]} { + _clone_failed $this [mc "Unable to copy object: %s" $err] + return 0 + } + } + return 1 +} + +method _link_files {objdir tolink} { + set total [llength $tolink] + $o_cons start \ + [mc "Linking objects"] \ + [mc "objects"] + for {set i 0} {$i < $total} {} { + set p [lindex $tolink $i] + if {[catch { + file link -hard \ + [file join .git objects $p] \ + [file join $objdir $p] + } err]} { + _clone_failed $this [mc "Unable to hardlink object: %s" $err] + return 0 + } + + incr i + if {$i % 5 == 0} { + $o_cons update $i $total + update + } + } + return 1 +} + +method _clone_refs {} { + set pwd [pwd] + if {[catch {cd $origin_url} err]} { + error_popup [mc "Not a Git repository: %s" [file tail $origin_url]] + return 0 + } + set fd_in [git_read for-each-ref \ + --tcl \ + {--format=list %(refname) %(objectname) %(*objectname)}] + cd $pwd + + set fd [open [gitdir packed-refs] w] + fconfigure $fd -translation binary + puts $fd "# pack-refs with: peeled" + while {[gets $fd_in line] >= 0} { + set line [eval $line] + set refn [lindex $line 0] + set robj [lindex $line 1] + set tobj [lindex $line 2] + + if {[regsub ^refs/heads/ $refn \ + "refs/remotes/$origin_name/" refn]} { + puts $fd "$robj $refn" + } elseif {[string match refs/tags/* $refn]} { + puts $fd "$robj $refn" + if {$tobj ne {}} { + puts $fd "^$tobj" + } + } + } + close $fd_in + close $fd + return 1 +} + +method _do_clone_tags {ok} { + if {$ok} { + $o_cons exec \ + [list git fetch --tags -k $origin_name] \ + [cb _do_clone_HEAD] + } else { + $o_cons done $ok + _clone_failed $this [mc "Cannot fetch branches and objects. See console output for details."] + } +} + +method _do_clone_HEAD {ok} { + if {$ok} { + $o_cons exec \ + [list git fetch $origin_name HEAD] \ + [cb _do_clone_full_end] + } else { + $o_cons done $ok + _clone_failed $this [mc "Cannot fetch tags. See console output for details."] + } +} + +method _do_clone_full_end {ok} { + $o_cons done $ok + + if {$ok} { + destroy $w_body + + set HEAD {} + if {[file exists [gitdir FETCH_HEAD]]} { + set fd [open [gitdir FETCH_HEAD] r] + while {[gets $fd line] >= 0} { + if {[regexp "^(.{40})\t\t" $line line HEAD]} { + break + } + } + close $fd + } + + catch {git pack-refs} + _do_clone_checkout $this $HEAD + } else { + _clone_failed $this [mc "Cannot determine HEAD. See console output for details."] + } +} + +method _clone_failed {{why {}}} { + if {[catch {file delete -force $local_path} err]} { + set why [strcat \ + $why \ + "\n\n" \ + [mc "Unable to cleanup %s" $local_path] \ + "\n\n" \ + $err] + } + if {$why ne {}} { + update + error_popup [strcat [mc "Clone failed."] "\n" $why] + } +} + +method _do_clone_checkout {HEAD} { + if {$HEAD eq {}} { + info_popup [strcat \ + [mc "No default branch obtained."] \ + "\n" \ + [mc "The 'master' branch has not been initialized."] \ + ] + set done 1 + return + } + if {[catch { + git update-ref HEAD $HEAD^0 + } err]} { + info_popup [strcat \ + [mc "Cannot resolve %s as a commit." $HEAD^0] \ + "\n $err" \ + "\n" \ + [mc "The 'master' branch has not been initialized."] \ + ] + set done 1 + return + } + + set o_cons [status_bar::two_line $w_body] + pack $w_body -fill x -padx 10 -pady 10 + $o_cons start \ + [mc "Creating working directory"] \ + [mc "files"] + + set readtree_err {} + set fd [git_read --stderr read-tree \ + -m \ + -u \ + -v \ + HEAD \ + HEAD \ + ] + fconfigure $fd -blocking 0 -translation binary + fileevent $fd readable [cb _readtree_wait $fd] +} + +method _readtree_wait {fd} { + set buf [read $fd] + $o_cons update_meter $buf + append readtree_err $buf + + fconfigure $fd -blocking 1 + if {![eof $fd]} { + fconfigure $fd -blocking 0 + return + } + + if {[catch {close $fd}]} { + set err $readtree_err + regsub {^fatal: } $err {} err + error_popup [strcat \ + [mc "Initial file checkout failed."] \ + "\n\n$err"] + return + } + + set done 1 +} + +###################################################################### +## +## Open Existing Repository + +method _do_open {} { + $w_next conf \ + -state disabled \ + -command [cb _do_open2] \ + -text [mc "Open"] + + frame $w_body + label $w_body.h \ + -font font_uibold \ + -text [mc "Open Existing Repository"] + pack $w_body.h -side top -fill x -pady 10 + pack $w_body -fill x -padx 10 + + frame $w_body.where + label $w_body.where.l -text [mc "Repository:"] + entry $w_body.where.t \ + -textvariable @local_path \ + -font font_diff \ + -width 50 + button $w_body.where.b \ + -text [mc "Browse"] \ + -command [cb _open_local_path] + + pack $w_body.where.b -side right + pack $w_body.where.l -side left + pack $w_body.where.t -fill x + pack $w_body.where -fill x + + trace add variable @local_path write [cb _write_local_path] + bind $w_body.h <Destroy> [list trace remove variable @local_path write [cb _write_local_path]] + update + focus $w_body.where.t +} + +method _open_local_path {} { + if {$local_path ne {}} { + set p $local_path + } else { + set p [_home] + } + + set p [tk_chooseDirectory \ + -initialdir $p \ + -parent $top \ + -title [mc "Git Repository"] \ + -mustexist true] + if {$p eq {}} return + + set p [file normalize $p] + if {![_is_git [file join $p .git]]} { + error_popup [mc "Not a Git repository: %s" [file tail $p]] + return + } + set local_path $p +} + +method _do_open2 {} { + if {![_is_git [file join $local_path .git]]} { + error_popup [mc "Not a Git repository: %s" [file tail $local_path]] + return + } + + if {[catch {cd $local_path} err]} { + error_popup [strcat \ + [mc "Failed to open repository %s:" $local_path] \ + "\n\n$err"] + return + } + + _append_recentrepos [pwd] + set ::_gitdir .git + set ::_prefix {} + set done 1 +} + +} diff --git a/git-gui/lib/choose_rev.tcl b/git-gui/lib/choose_rev.tcl index ec064b3e13..a063c5bc49 100644 --- a/git-gui/lib/choose_rev.tcl +++ b/git-gui/lib/choose_rev.tcl @@ -50,14 +50,14 @@ constructor _new {path unmerged_only title} { if {$is_detached} { radiobutton $w.detachedhead_r \ -anchor w \ - -text {This Detached Checkout} \ + -text [mc "This Detached Checkout"] \ -value HEAD \ -variable @revtype grid $w.detachedhead_r -sticky we -padx {0 5} -columnspan 2 } radiobutton $w.expr_r \ - -text {Revision Expression:} \ + -text [mc "Revision Expression:"] \ -value expr \ -variable @revtype entry $w.expr_t \ @@ -71,17 +71,17 @@ constructor _new {path unmerged_only title} { frame $w.types radiobutton $w.types.head_r \ - -text {Local Branch} \ + -text [mc "Local Branch"] \ -value head \ -variable @revtype pack $w.types.head_r -side left radiobutton $w.types.trck_r \ - -text {Tracking Branch} \ + -text [mc "Tracking Branch"] \ -value trck \ -variable @revtype pack $w.types.trck_r -side left radiobutton $w.types.tag_r \ - -text {Tag} \ + -text [mc "Tag"] \ -value tag \ -variable @revtype pack $w.types.tag_r -side left @@ -133,13 +133,13 @@ constructor _new {path unmerged_only title} { append fmt { %(objecttype)} append fmt { %(objectname)} append fmt { [concat %(taggername) %(authorname)]} - append fmt { [concat %(taggerdate) %(authordate)]} + append fmt { [reformat_date [concat %(taggerdate) %(authordate)]]} append fmt { %(subject)} append fmt {] [list} append fmt { %(*objecttype)} append fmt { %(*objectname)} append fmt { %(*authorname)} - append fmt { %(*authordate)} + append fmt { [reformat_date %(*authordate)]} append fmt { %(*subject)} append fmt {]} set all_refn [list] @@ -314,7 +314,7 @@ method commit_or_die {} { } set top [winfo toplevel $w] - set msg "Invalid revision: [get $this]\n\n$err" + set msg [strcat [mc "Invalid revision: %s" [get $this]] "\n\n$err"] tk_messageBox \ -icon error \ -type ok \ @@ -335,7 +335,7 @@ method _expr {} { if {$i ne {}} { return [lindex $cur_specs $i 1] } else { - error "No revision selected." + error [mc "No revision selected."] } } @@ -343,7 +343,7 @@ method _expr {} { if {$c_expr ne {}} { return $c_expr } else { - error "Revision expression is empty." + error [mc "Revision expression is empty."] } } HEAD { return HEAD } @@ -527,14 +527,14 @@ method _open_tooltip {} { set last [_reflog_last $this [lindex $spec 1]] if {$last ne {}} { $tooltip_t insert end "\n" - $tooltip_t insert end "updated" + $tooltip_t insert end [mc "Updated"] $tooltip_t insert end " $last" } $tooltip_t insert end "\n" if {$tag ne {}} { $tooltip_t insert end "\n" - $tooltip_t insert end "tag" section_header + $tooltip_t insert end [mc "Tag"] section_header $tooltip_t insert end " [lindex $tag 1]\n" $tooltip_t insert end [lindex $tag 2] $tooltip_t insert end " ([lindex $tag 3])\n" @@ -544,7 +544,7 @@ method _open_tooltip {} { if {$cmit ne {}} { $tooltip_t insert end "\n" - $tooltip_t insert end "commit" section_header + $tooltip_t insert end [mc "Commit@@noun"] section_header $tooltip_t insert end " [lindex $cmit 1]\n" $tooltip_t insert end [lindex $cmit 2] $tooltip_t insert end " ([lindex $cmit 3])\n" @@ -553,11 +553,11 @@ method _open_tooltip {} { if {[llength $spec] > 2} { $tooltip_t insert end "\n" - $tooltip_t insert end "remote" section_header + $tooltip_t insert end [mc "Remote"] section_header $tooltip_t insert end " [lindex $spec 2]\n" - $tooltip_t insert end "url" + $tooltip_t insert end [mc "URL"] $tooltip_t insert end " $remote_url([lindex $spec 2])\n" - $tooltip_t insert end "branch" + $tooltip_t insert end [mc "Branch"] $tooltip_t insert end " [lindex $spec 3]" } @@ -583,7 +583,7 @@ method _reflog_last {name} { } if {$last ne {}} { - set last [clock format $last -format {%a %b %e %H:%M:%S %Y}] + set last [format_date $last] } set reflog_last($name) $last return $last diff --git a/git-gui/lib/commit.tcl b/git-gui/lib/commit.tcl index 57238129e4..b2d2d53086 100644 --- a/git-gui/lib/commit.tcl +++ b/git-gui/lib/commit.tcl @@ -6,19 +6,19 @@ proc load_last_commit {} { global repo_config if {[llength $PARENT] == 0} { - error_popup {There is nothing to amend. + error_popup [mc "There is nothing to amend. You are about to create the initial commit. There is no commit before this to amend. -} +"] return } repository_state curType curHEAD curMERGE_HEAD if {$curType eq {merge}} { - error_popup {Cannot amend while merging. + error_popup [mc "Cannot amend while merging. You are currently in the middle of a merge that has not been fully completed. You cannot amend the prior commit unless you first abort the current merge activity. -} +"] return } @@ -46,7 +46,7 @@ You are currently in the middle of a merge that has not been fully completed. Y } set msg [string trim $msg] } err]} { - error_popup "Error loading commit data for amend:\n\n$err" + error_popup [strcat [mc "Error loading commit data for amend:"] "\n\n$err"] return } @@ -73,12 +73,12 @@ proc committer_ident {} { if {$GIT_COMMITTER_IDENT eq {}} { if {[catch {set me [git var GIT_COMMITTER_IDENT]} err]} { - error_popup "Unable to obtain your identity:\n\n$err" + error_popup [strcat [mc "Unable to obtain your identity:"] "\n\n$err"] return {} } if {![regexp {^(.*) [0-9]+ [-+0-9]+$} \ $me me GIT_COMMITTER_IDENT]} { - error_popup "Invalid GIT_COMMITTER_IDENT:\n\n$me" + error_popup [strcat [mc "Invalid GIT_COMMITTER_IDENT:"] "\n\n$me"] return {} } } @@ -130,12 +130,12 @@ proc commit_tree {} { && $curType eq {normal} && $curHEAD eq $HEAD} { } elseif {$commit_type ne $curType || $HEAD ne $curHEAD} { - info_popup {Last scanned state does not match repository state. + info_popup [mc "Last scanned state does not match repository state. Another Git program has modified this repository since the last scan. A rescan must be performed before another commit can be created. The rescan will be automatically started now. -} +"] unlock_index rescan ui_ready return @@ -151,26 +151,26 @@ The rescan will be automatically started now. D? - M? {set files_ready 1} U? { - error_popup "Unmerged files cannot be committed. + error_popup [mc "Unmerged files cannot be committed. -File [short_path $path] has merge conflicts. You must resolve them and stage the file before committing. -" +File %s has merge conflicts. You must resolve them and stage the file before committing. +" [short_path $path]] unlock_index return } default { - error_popup "Unknown file state [lindex $s 0] detected. + error_popup [mc "Unknown file state %s detected. -File [short_path $path] cannot be committed by this program. -" +File %s cannot be committed by this program. +" [lindex $s 0] [short_path $path]] } } } if {!$files_ready && ![string match *merge $curType]} { - info_popup {No changes to commit. + info_popup [mc "No changes to commit. You must stage at least 1 file before you can commit. -} +"] unlock_index return } @@ -180,14 +180,14 @@ You must stage at least 1 file before you can commit. set msg [string trim [$ui_comm get 1.0 end]] regsub -all -line {[ \t\r]+$} $msg {} msg if {$msg eq {}} { - error_popup {Please supply a commit message. + error_popup [mc "Please supply a commit message. A good commit message has the following format: -- First line: Describe in one sentance what you did. +- First line: Describe in one sentence what you did. - Second line: Blank - Remaining lines: Describe why this change is good. -} +"] unlock_index return } @@ -254,7 +254,7 @@ proc commit_committree {fd_wt curHEAD msg} { gets $fd_wt tree_id if {[catch {close $fd_wt} err]} { - error_popup "write-tree failed:\n\n$err" + error_popup [strcat [mc "write-tree failed:"] "\n\n$err"] ui_status {Commit failed.} unlock_index return @@ -272,18 +272,18 @@ proc commit_committree {fd_wt curHEAD msg} { && [string length $old_tree] == 45} { set old_tree [string range $old_tree 5 end] } else { - error "Commit $PARENT appears to be corrupt" + error [mc "Commit %s appears to be corrupt" $PARENT] } if {$tree_id eq $old_tree} { - info_popup {No changes to commit. + info_popup [mc "No changes to commit. No files were modified by this commit and it was not a merge commit. A rescan will be automatically started now. -} +"] unlock_index - rescan {ui_status {No changes to commit.}} + rescan {ui_status [mc "No changes to commit."]} return } } @@ -300,7 +300,7 @@ A rescan will be automatically started now. if {$use_enc ne {}} { fconfigure $msg_wt -encoding $use_enc } else { - puts stderr "warning: Tcl does not support encoding '$enc'." + puts stderr [mc "warning: Tcl does not support encoding '%s'." $enc] fconfigure $msg_wt -encoding utf-8 } puts -nonewline $msg_wt $msg @@ -314,7 +314,7 @@ A rescan will be automatically started now. } lappend cmd <$msg_p if {[catch {set cmt_id [eval git $cmd]} err]} { - error_popup "commit-tree failed:\n\n$err" + error_popup [strcat [mc "commit-tree failed:"] "\n\n$err"] ui_status {Commit failed.} unlock_index return @@ -336,7 +336,7 @@ A rescan will be automatically started now. if {[catch { git update-ref -m $reflogm HEAD $cmt_id $curHEAD } err]} { - error_popup "update-ref failed:\n\n$err" + error_popup [strcat [mc "update-ref failed:"] "\n\n$err"] ui_status {Commit failed.} unlock_index return @@ -427,5 +427,5 @@ A rescan will be automatically started now. display_all_files unlock_index reshow_diff - ui_status "Created commit [string range $cmt_id 0 7]: $subject" + ui_status [mc "Created commit %s: %s" [string range $cmt_id 0 7] $subject] } diff --git a/git-gui/lib/console.tcl b/git-gui/lib/console.tcl index b038a78358..5597188d80 100644 --- a/git-gui/lib/console.tcl +++ b/git-gui/lib/console.tcl @@ -6,6 +6,7 @@ class console { field t_short field t_long field w +field w_t field console_cr field is_toplevel 1; # are we our own window? @@ -36,6 +37,7 @@ method _init {} { } set console_cr 1.0 + set w_t $w.m.t frame $w.m label $w.m.l1 \ @@ -43,51 +45,47 @@ method _init {} { -anchor w \ -justify left \ -font font_uibold - text $w.m.t \ + text $w_t \ -background white -borderwidth 1 \ -relief sunken \ -width 80 -height 10 \ -wrap none \ -font font_diff \ -state disabled \ - -xscrollcommand [list $w.m.sbx set] \ - -yscrollcommand [list $w.m.sby set] - label $w.m.s -text {Working... please wait...} \ + -xscrollcommand [cb _sb_set $w.m.sbx h] \ + -yscrollcommand [cb _sb_set $w.m.sby v] + label $w.m.s -text [mc "Working... please wait..."] \ -anchor w \ -justify left \ -font font_uibold - scrollbar $w.m.sbx -command [list $w.m.t xview] -orient h - scrollbar $w.m.sby -command [list $w.m.t yview] pack $w.m.l1 -side top -fill x pack $w.m.s -side bottom -fill x - pack $w.m.sbx -side bottom -fill x - pack $w.m.sby -side right -fill y - pack $w.m.t -side left -fill both -expand 1 + pack $w_t -side left -fill both -expand 1 pack $w.m -side top -fill both -expand 1 -padx 5 -pady 10 menu $w.ctxm -tearoff 0 - $w.ctxm add command -label "Copy" \ - -command "tk_textCopy $w.m.t" - $w.ctxm add command -label "Select All" \ - -command "focus $w.m.t;$w.m.t tag add sel 0.0 end" - $w.ctxm add command -label "Copy All" \ + $w.ctxm add command -label [mc "Copy"] \ + -command "tk_textCopy $w_t" + $w.ctxm add command -label [mc "Select All"] \ + -command "focus $w_t;$w_t tag add sel 0.0 end" + $w.ctxm add command -label [mc "Copy All"] \ -command " - $w.m.t tag add sel 0.0 end - tk_textCopy $w.m.t - $w.m.t tag remove sel 0.0 end + $w_t tag add sel 0.0 end + tk_textCopy $w_t + $w_t tag remove sel 0.0 end " if {$is_toplevel} { - button $w.ok -text {Close} \ + button $w.ok -text [mc "Close"] \ -state disabled \ -command [list destroy $w] pack $w.ok -side bottom -anchor e -pady 10 -padx 10 bind $w <Visibility> [list focus $w] } - bind_button3 $w.m.t "tk_popup $w.ctxm %X %Y" - bind $w.m.t <$M1B-Key-a> "$w.m.t tag add sel 0.0 end;break" - bind $w.m.t <$M1B-Key-A> "$w.m.t tag add sel 0.0 end;break" + bind_button3 $w_t "tk_popup $w.ctxm %X %Y" + bind $w_t <$M1B-Key-a> "$w_t tag add sel 0.0 end;break" + bind $w_t <$M1B-Key-A> "$w_t tag add sel 0.0 end;break" } method exec {cmd {after {}}} { @@ -104,8 +102,8 @@ method exec {cmd {after {}}} { method _read {fd after} { set buf [read $fd] if {$buf ne {}} { - if {![winfo exists $w.m.t]} {_init $this} - $w.m.t conf -state normal + if {![winfo exists $w_t]} {_init $this} + $w_t conf -state normal set c 0 set n [string length $buf] while {$c < $n} { @@ -115,20 +113,20 @@ method _read {fd after} { if {$lf < 0} {set lf [expr {$n + 1}]} if {$lf < $cr} { - $w.m.t insert end [string range $buf $c $lf] - set console_cr [$w.m.t index {end -1c}] + $w_t insert end [string range $buf $c $lf] + set console_cr [$w_t index {end -1c}] set c $lf incr c } else { - $w.m.t delete $console_cr end - $w.m.t insert end "\n" - $w.m.t insert end [string range $buf $c [expr {$cr - 1}]] + $w_t delete $console_cr end + $w_t insert end "\n" + $w_t insert end [string range $buf $c [expr {$cr - 1}]] set c $cr incr c } } - $w.m.t conf -state disabled - $w.m.t see end + $w_t conf -state disabled + $w_t see end } fconfigure $fd -blocking 1 @@ -171,33 +169,50 @@ method chain {cmdlist {ok 1}} { } method insert {txt} { - if {![winfo exists $w.m.t]} {_init $this} - $w.m.t conf -state normal - $w.m.t insert end "$txt\n" - set console_cr [$w.m.t index {end -1c}] - $w.m.t conf -state disabled + if {![winfo exists $w_t]} {_init $this} + $w_t conf -state normal + $w_t insert end "$txt\n" + set console_cr [$w_t index {end -1c}] + $w_t conf -state disabled } method done {ok} { if {$ok} { if {[winfo exists $w.m.s]} { - $w.m.s conf -background green -text {Success} + bind $w.m.s <Destroy> [list delete_this $this] + $w.m.s conf -background green -text [mc "Success"] if {$is_toplevel} { $w.ok conf -state normal focus $w.ok } + } else { + delete_this } } else { if {![winfo exists $w.m.s]} { _init $this } - $w.m.s conf -background red -text {Error: Command Failed} + bind $w.m.s <Destroy> [list delete_this $this] + $w.m.s conf -background red -text [mc "Error: Command Failed"] if {$is_toplevel} { $w.ok conf -state normal focus $w.ok } } - delete_this +} + +method _sb_set {sb orient first last} { + if {![winfo exists $sb]} { + if {$first == $last || ($first == 0 && $last == 1)} return + if {$orient eq {h}} { + scrollbar $sb -orient h -command [list $w_t xview] + pack $sb -fill x -side bottom -before $w_t + } else { + scrollbar $sb -orient v -command [list $w_t yview] + pack $sb -fill y -side right -before $w_t + } + } + $sb set $first $last } } diff --git a/git-gui/lib/database.tcl b/git-gui/lib/database.tcl index 0657cc2245..d66aa3fe33 100644 --- a/git-gui/lib/database.tcl +++ b/git-gui/lib/database.tcl @@ -24,14 +24,14 @@ proc do_stats {} { toplevel $w wm geometry $w "+[winfo rootx .]+[winfo rooty .]" - label $w.header -text {Database Statistics} + label $w.header -text [mc "Database Statistics"] pack $w.header -side top -fill x frame $w.buttons -border 1 - button $w.buttons.close -text Close \ + button $w.buttons.close -text [mc Close] \ -default active \ -command [list destroy $w] - button $w.buttons.gc -text {Compress Database} \ + button $w.buttons.gc -text [mc "Compress Database"] \ -default normal \ -command "destroy $w;do_gc" pack $w.buttons.close -side right @@ -40,16 +40,16 @@ proc do_stats {} { frame $w.stat -borderwidth 1 -relief solid foreach s { - {count {Number of loose objects}} - {size {Disk space used by loose objects} { KiB}} - {in-pack {Number of packed objects}} - {packs {Number of packs}} - {size-pack {Disk space used by packed objects} { KiB}} - {prune-packable {Packed objects waiting for pruning}} - {garbage {Garbage files}} + {count {mc "Number of loose objects"}} + {size {mc "Disk space used by loose objects"} { KiB}} + {in-pack {mc "Number of packed objects"}} + {packs {mc "Number of packs"}} + {size-pack {mc "Disk space used by packed objects"} { KiB}} + {prune-packable {mc "Packed objects waiting for pruning"}} + {garbage {mc "Garbage files"}} } { set name [lindex $s 0] - set label [lindex $s 1] + set label [eval [lindex $s 1]] if {[catch {set value $stats($name)}]} continue if {[llength $s] > 2} { set value "$value[lindex $s 2]" @@ -64,12 +64,12 @@ proc do_stats {} { bind $w <Visibility> "grab $w; focus $w.buttons.close" bind $w <Key-Escape> [list destroy $w] bind $w <Key-Return> [list destroy $w] - wm title $w "[appname] ([reponame]): Database Statistics" + wm title $w [append "[appname] ([reponame]): " [mc "Database Statistics"]] tkwait window $w } proc do_gc {} { - set w [console::new {gc} {Compressing the object database}] + set w [console::new {gc} [mc "Compressing the object database"]] console::chain $w { {exec git pack-refs --prune} {exec git reflog expire --all} @@ -80,7 +80,7 @@ proc do_gc {} { proc do_fsck_objects {} { set w [console::new {fsck-objects} \ - {Verifying the object database with fsck-objects}] + [mc "Verifying the object database with fsck-objects"]] set cmd [list git fsck-objects] lappend cmd --full lappend cmd --cache @@ -105,11 +105,11 @@ proc hint_gc {} { set objects_current [expr {$objects_current * 256}] set object_limit [expr {$object_limit * 256}] if {[ask_popup \ - "This repository currently has approximately $objects_current loose objects. + [mc "This repository currently has approximately %i loose objects. -To maintain optimal performance it is strongly recommended that you compress the database when more than $object_limit loose objects exist. +To maintain optimal performance it is strongly recommended that you compress the database when more than %i loose objects exist. -Compress the database now?"] eq yes} { +Compress the database now?" $objects_current $object_limit]] eq yes} { do_gc } } diff --git a/git-gui/lib/date.tcl b/git-gui/lib/date.tcl new file mode 100644 index 0000000000..abe82992b6 --- /dev/null +++ b/git-gui/lib/date.tcl @@ -0,0 +1,53 @@ +# git-gui date processing support +# Copyright (C) 2007 Shawn Pearce + +set git_month(Jan) 1 +set git_month(Feb) 2 +set git_month(Mar) 3 +set git_month(Apr) 4 +set git_month(May) 5 +set git_month(Jun) 6 +set git_month(Jul) 7 +set git_month(Aug) 8 +set git_month(Sep) 9 +set git_month(Oct) 10 +set git_month(Nov) 11 +set git_month(Dec) 12 + +proc parse_git_date {s} { + if {$s eq {}} { + return {} + } + + if {![regexp \ + {^... (...) (\d{1,2}) (\d\d):(\d\d):(\d\d) (\d{4}) ([+-]?)(\d\d)(\d\d)$} $s s \ + month day hr mm ss yr ew tz_h tz_m]} { + error [mc "Invalid date from Git: %s" $s] + } + + set s [clock scan [format {%4.4i%2.2i%2.2iT%2s%2s%2s} \ + $yr $::git_month($month) $day \ + $hr $mm $ss] \ + -gmt 1] + + regsub ^0 $tz_h {} tz_h + regsub ^0 $tz_m {} tz_m + switch -- $ew { + - {set ew +} + + {set ew -} + {} {set ew -} + } + + return [expr "$s $ew ($tz_h * 3600 + $tz_m * 60)"] +} + +proc format_date {s} { + if {$s eq {}} { + return {} + } + return [clock format $s -format {%a %b %e %H:%M:%S %Y}] +} + +proc reformat_date {s} { + return [format_date [parse_git_date $s]] +} diff --git a/git-gui/lib/diff.tcl b/git-gui/lib/diff.tcl index 694834ab7a..43565e412f 100644 --- a/git-gui/lib/diff.tcl +++ b/git-gui/lib/diff.tcl @@ -39,13 +39,13 @@ proc handle_empty_diff {} { set s $file_states($path) if {[lindex $s 0] ne {_M}} return - info_popup "No differences detected. + info_popup [mc "No differences detected. -[short_path $path] has no changes. +%s has no changes. The modification date of this file was updated by another application, but the content within the file was not changed. -A rescan will be automatically started to find other files which may have the same state." +A rescan will be automatically started to find other files which may have the same state." [short_path $path]] clear_diff display_file $path __ @@ -78,7 +78,7 @@ proc show_diff {path w {lno {}}} { set current_diff_path $path set current_diff_side $w set current_diff_header {} - ui_status "Loading diff of [escape_path $path]..." + ui_status [mc "Loading diff of %s..." [escape_path $path]] # - Git won't give us the diff, there's nothing to compare to! # @@ -111,13 +111,16 @@ proc show_diff {path w {lno {}}} { } err ]} { set diff_active 0 unlock_index - ui_status "Unable to display [escape_path $path]" - error_popup "Error loading file:\n\n$err" + ui_status [mc "Unable to display %s" [escape_path $path]] + error_popup [strcat [mc "Error loading file:"] "\n\n$err"] return } $ui_diff conf -state normal if {$type eq {submodule}} { - $ui_diff insert end "* Git Repository (subproject)\n" d_@ + $ui_diff insert end [append \ + "* " \ + [mc "Git Repository (subproject)"] \ + "\n"] d_@ } elseif {![catch {set type [exec file $path]}]} { set n [string length $path] if {[string equal -length $n $path $type]} { @@ -128,7 +131,7 @@ proc show_diff {path w {lno {}}} { } if {[string first "\0" $content] != -1} { $ui_diff insert end \ - "* Binary file (not showing content)." \ + [mc "* Binary file (not showing content)."] \ d_@ } else { if {$sz > $max_sz} { @@ -178,8 +181,8 @@ proc show_diff {path w {lno {}}} { if {[catch {set fd [eval git_read --nice $cmd]} err]} { set diff_active 0 unlock_index - ui_status "Unable to display [escape_path $path]" - error_popup "Error loading diff:\n\n$err" + ui_status [mc "Unable to display %s" [escape_path $path]] + error_popup [strcat [mc "Error loading diff:"] "\n\n$err"] return } @@ -296,14 +299,14 @@ proc apply_hunk {x y} { set apply_cmd {apply --cached --whitespace=nowarn} set mi [lindex $file_states($current_diff_path) 0] if {$current_diff_side eq $ui_index} { - set mode unstage + set failed_msg [mc "Failed to unstage selected hunk."] lappend apply_cmd --reverse if {[string index $mi 0] ne {M}} { unlock_index return } } else { - set mode stage + set failed_msg [mc "Failed to stage selected hunk."] if {[string index $mi 1] ne {M}} { unlock_index return @@ -328,7 +331,7 @@ proc apply_hunk {x y} { puts -nonewline $p $current_diff_header puts -nonewline $p [$ui_diff get $s_lno $e_lno] close $p} err]} { - error_popup "Failed to $mode selected hunk.\n\n$err" + error_popup [append $failed_msg "\n\n$err"] unlock_index return } diff --git a/git-gui/lib/error.tcl b/git-gui/lib/error.tcl index 16a22187b2..13565b7ab0 100644 --- a/git-gui/lib/error.tcl +++ b/git-gui/lib/error.tcl @@ -9,7 +9,7 @@ proc error_popup {msg} { set cmd [list tk_messageBox \ -icon error \ -type ok \ - -title "$title: error" \ + -title [append "$title: " [mc "error"]] \ -message $msg] if {[winfo ismapped .]} { lappend cmd -parent . @@ -25,7 +25,7 @@ proc warn_popup {msg} { set cmd [list tk_messageBox \ -icon warning \ -type ok \ - -title "$title: warning" \ + -title [append "$title: " [mc "warning"]] \ -message $msg] if {[winfo ismapped .]} { lappend cmd -parent . @@ -78,7 +78,7 @@ proc hook_failed_popup {hook msg} { -font font_diff \ -yscrollcommand [list $w.m.sby set] label $w.m.l2 \ - -text {You must correct the above errors before committing.} \ + -text [mc "You must correct the above errors before committing."] \ -anchor w \ -justify left \ -font font_uibold @@ -99,6 +99,6 @@ proc hook_failed_popup {hook msg} { bind $w <Visibility> "grab $w; focus $w" bind $w <Key-Return> "destroy $w" - wm title $w "[appname] ([reponame]): error" + wm title $w [append "[appname] ([reponame]): " [mc "error"]] tkwait window $w } diff --git a/git-gui/lib/git-gui.ico b/git-gui/lib/git-gui.ico Binary files differnew file mode 100644 index 0000000000..334cfa5a1a --- /dev/null +++ b/git-gui/lib/git-gui.ico diff --git a/git-gui/lib/index.tcl b/git-gui/lib/index.tcl index 44689ab63b..a0b22f2945 100644 --- a/git-gui/lib/index.tcl +++ b/git-gui/lib/index.tcl @@ -1,6 +1,56 @@ # git-gui index (add/remove) support # Copyright (C) 2006, 2007 Shawn Pearce +proc _delete_indexlock {} { + if {[catch {file delete -- [gitdir index.lock]} err]} { + error_popup [strcat [mc "Unable to unlock the index."] "\n\n$err"] + } +} + +proc _close_updateindex {fd after} { + fconfigure $fd -blocking 1 + if {[catch {close $fd} err]} { + set w .indexfried + toplevel $w + wm title $w [strcat "[appname] ([reponame]): " [mc "Index Error"]] + wm geometry $w "+[winfo rootx .]+[winfo rooty .]" + pack [label $w.msg \ + -justify left \ + -anchor w \ + -text [strcat \ + [mc "Updating the Git index failed. A rescan will be automatically started to resynchronize git-gui."] \ + "\n\n$err"] \ + ] -anchor w + + frame $w.buttons + button $w.buttons.continue \ + -text [mc "Continue"] \ + -command [list destroy $w] + pack $w.buttons.continue -side right -padx 5 + button $w.buttons.unlock \ + -text [mc "Unlock Index"] \ + -command "destroy $w; _delete_indexlock" + pack $w.buttons.unlock -side right + pack $w.buttons -side bottom -fill x -pady 10 -padx 10 + + wm protocol $w WM_DELETE_WINDOW update + bind $w.buttons.continue <Visibility> " + grab $w + focus $w.buttons.continue + " + tkwait window $w + + $::main_status stop + unlock_index + rescan $after 0 + return + } + + $::main_status stop + unlock_index + uplevel #0 $after +} + proc update_indexinfo {msg pathList after} { global update_index_cp @@ -12,12 +62,7 @@ proc update_indexinfo {msg pathList after} { set batch [expr {int($totalCnt * .01) + 1}] if {$batch > 25} {set batch 25} - ui_status [format \ - "%s... %i/%i files (%.2f%%)" \ - $msg \ - $update_index_cp \ - $totalCnt \ - 0.0] + $::main_status start $msg [mc "files"] set fd [git_write update-index -z --index-info] fconfigure $fd \ -blocking 0 \ @@ -31,19 +76,16 @@ proc update_indexinfo {msg pathList after} { $pathList \ $totalCnt \ $batch \ - $msg \ $after \ ] } -proc write_update_indexinfo {fd pathList totalCnt batch msg after} { +proc write_update_indexinfo {fd pathList totalCnt batch after} { global update_index_cp global file_states current_diff_path if {$update_index_cp >= $totalCnt} { - close $fd - unlock_index - uplevel #0 $after + _close_updateindex $fd $after return } @@ -68,12 +110,7 @@ proc write_update_indexinfo {fd pathList totalCnt batch msg after} { display_file $path $new } - ui_status [format \ - "%s... %i/%i files (%.2f%%)" \ - $msg \ - $update_index_cp \ - $totalCnt \ - [expr {100.0 * $update_index_cp / $totalCnt}]] + $::main_status update $update_index_cp $totalCnt } proc update_index {msg pathList after} { @@ -87,12 +124,7 @@ proc update_index {msg pathList after} { set batch [expr {int($totalCnt * .01) + 1}] if {$batch > 25} {set batch 25} - ui_status [format \ - "%s... %i/%i files (%.2f%%)" \ - $msg \ - $update_index_cp \ - $totalCnt \ - 0.0] + $::main_status start $msg [mc "files"] set fd [git_write update-index --add --remove -z --stdin] fconfigure $fd \ -blocking 0 \ @@ -106,19 +138,16 @@ proc update_index {msg pathList after} { $pathList \ $totalCnt \ $batch \ - $msg \ $after \ ] } -proc write_update_index {fd pathList totalCnt batch msg after} { +proc write_update_index {fd pathList totalCnt batch after} { global update_index_cp global file_states current_diff_path if {$update_index_cp >= $totalCnt} { - close $fd - unlock_index - uplevel #0 $after + _close_updateindex $fd $after return } @@ -147,12 +176,7 @@ proc write_update_index {fd pathList totalCnt batch msg after} { display_file $path $new } - ui_status [format \ - "%s... %i/%i files (%.2f%%)" \ - $msg \ - $update_index_cp \ - $totalCnt \ - [expr {100.0 * $update_index_cp / $totalCnt}]] + $::main_status update $update_index_cp $totalCnt } proc checkout_index {msg pathList after} { @@ -166,12 +190,7 @@ proc checkout_index {msg pathList after} { set batch [expr {int($totalCnt * .01) + 1}] if {$batch > 25} {set batch 25} - ui_status [format \ - "%s... %i/%i files (%.2f%%)" \ - $msg \ - $update_index_cp \ - $totalCnt \ - 0.0] + $::main_status start $msg [mc "files"] set fd [git_write checkout-index \ --index \ --quiet \ @@ -191,19 +210,16 @@ proc checkout_index {msg pathList after} { $pathList \ $totalCnt \ $batch \ - $msg \ $after \ ] } -proc write_checkout_index {fd pathList totalCnt batch msg after} { +proc write_checkout_index {fd pathList totalCnt batch after} { global update_index_cp global file_states current_diff_path if {$update_index_cp >= $totalCnt} { - close $fd - unlock_index - uplevel #0 $after + _close_updateindex $fd $after return } @@ -222,12 +238,7 @@ proc write_checkout_index {fd pathList totalCnt batch msg after} { } } - ui_status [format \ - "%s... %i/%i files (%.2f%%)" \ - $msg \ - $update_index_cp \ - $totalCnt \ - [expr {100.0 * $update_index_cp / $totalCnt}]] + $::main_status update $update_index_cp $totalCnt } proc unstage_helper {txt paths} { @@ -268,7 +279,7 @@ proc do_unstage_selection {} { [array names selected_paths] } elseif {$current_diff_path ne {}} { unstage_helper \ - "Unstaging [short_path $current_diff_path] from commit" \ + [mc "Unstaging %s from commit" [short_path $current_diff_path]] \ [list $current_diff_path] } } @@ -312,7 +323,7 @@ proc do_add_selection {} { [array names selected_paths] } elseif {$current_diff_path ne {}} { add_helper \ - "Adding [short_path $current_diff_path]" \ + [mc "Adding %s" [short_path $current_diff_path]] \ [list $current_diff_path] } } @@ -351,26 +362,35 @@ proc revert_helper {txt paths} { } } + + # Split question between singular and plural cases, because + # such distinction is needed in some languages. Previously, the + # code used "Revert changes in" for both, but that can't work + # in languages where 'in' must be combined with word from + # rest of string (in diffrent way for both cases of course). + # + # FIXME: Unfortunately, even that isn't enough in some languages + # as they have quite complex plural-form rules. Unfortunately, + # msgcat doesn't seem to support that kind of string translation. + # set n [llength $pathList] if {$n == 0} { unlock_index return } elseif {$n == 1} { - set s "[short_path [lindex $pathList]]" + set query [mc "Revert changes in file %s?" [short_path [lindex $pathList]]] } else { - set s "these $n files" + set query [mc "Revert changes in these %i files?" $n] } set reply [tk_dialog \ .confirm_revert \ "[appname] ([reponame])" \ - "Revert changes in $s? - -Any unstaged changes will be permanently lost by the revert." \ + [mc "Any unstaged changes will be permanently lost by the revert."] \ question \ 1 \ - {Do Nothing} \ - {Revert Changes} \ + [mc "Do Nothing"] \ + [mc "Revert Changes"] \ ] if {$reply == 1} { checkout_index \ diff --git a/git-gui/lib/logo.tcl b/git-gui/lib/logo.tcl new file mode 100644 index 0000000000..5ff76692f5 --- /dev/null +++ b/git-gui/lib/logo.tcl @@ -0,0 +1,43 @@ +# git-gui Git Gui logo +# Copyright (C) 2007 Shawn Pearce + +# Henrik Nyh's alternative Git logo, from his blog post +# http://henrik.nyh.se/2007/06/alternative-git-logo-and-favicon +# +image create photo ::git_logo_data -data { +R0lGODdhYQC8AIQbAGZmZtg4LW9vb3l5eYKCgoyMjEC/TOJpYZWVlZ+fn2/PeKmpqbKysry8vMXF +xZ/fpc/Pz7fnvPXNytnZ2eLi4s/v0vja1+zs7Of36fX19f3z8v///////////////////ywAAAAA +YQC8AAAF/uAmjmRpnmiqrmzrvq4hz3RtGw+s7zx5/7dcb0hUAY8zYXHJRCKVzGjPeYRKry8q0Irt +GrVBr3gFDo/PprKNix6ra+y2902Ly7H05L2dl9n3UX04gGeCf4RFhohiiotdjY5XkJGBfYeUOpOY +iZablXmXURgPpKWmp6ipqYIKqq6vqREjFYK1trUKs7e7vFq5IrS9wsM0vxvBxMm8xsjKzqy6z9J5 +zNPWatXX2k7Z29433d/iMuHj3+Xm2+jp1+vs0+7vz/HyyvT1xPf4wvr7y9H+pBkbBasgLFYGE8ba +o8nTlE4OOYGKKJFOKIopGmLMAnHjDo0eWYAM+WUiSRgj/k+eSKmyBMuWI17C3CATZs2WN1XmPLmT +ZM+QPz0G3VihqNGjSJNWwDCzqdOnUKPu0SChqtWrWLNq3cq1q9evYCVYGCEhgNmzaNOqXcu2rdu3 +cOMGOEBWrt27ePPCpSuirN6/gAO35bvBr+DDiPMSNpy4sWO2ix9Lnmw2MuXLiS1j3gxYM+fPdz2D +Hv1WNOnTak2jXj23LuvXlV3DZq16Nujatjnjzo15N2/Kvn9LDi7cMfHimaUqX868ufPn0KPPpOCA +AQMWCQBo3869u/fv4MNrd3DlQoMC3QlkSJFdvPv38LVDWJLBAYHwE1LE38+/+/UhGTAggHv5odDf +gfv9/seDgPAVeAKCELqnIAwU3BefgyZEqOF3E7rAQH8YlrDhiNt1uEIG6IGoH4kjmpjCBRaqaCCL +G7p4AgUDIhgiCTTW2AKOEe44Qo8a2khCBgNoKKQIREZopAgZxAjhkhs0CeGTG7Sn5IpW9vekAyRS +2eWBRl6Q44ZijhlfAQlQmeKIaarpHZsMTHABCxDQGKec3JH3QpIs7snndn6yAKaeXA7aZwuABppo +fAws0GiEhaKQJ40F3DkjfwVC8CaCAlCgAgIkJjDfCgdiOMGn/Q2w3gkZtPgqC6ma0ECECaBwa4QE +aOpCrSYAqeMJpEKYqw7ABnsmfwQ8aCwPySqLYKUb/kwAYbPQyoiCtQcOUMKHBwrgK7LaogBuuaxC +OkS0KEwa37EiLBufALPuwO4Jh/InwAixkknEvSe4C9+p3PY3rr3lpnDufguIcCmzRQAc7IHYLhxf +w/8mnILA74lg8cARa4xCsZxusMCBomZccgsfv0deuh2HvLKh/sLs3hJSvieuCwUzvIHN4tGXc3ih +vtDzmj8fSNLR8BWQdH9LH+g00OFF3d/UBx4cUcvuOc21eFRiouV+Xvvr0dDvlX21R/2uzTR89TqU +L3+5UoBgAxtRHd5/CHpLkd13i4D2e3hHRLKMY+9Hr0Nvx/fq3Pw57cng7/m9wQVObnIyhAiQwHF8 +/tQS8nDgI2wOYeh3CAvhuIBHiDEgqvdtwudkaz3GBPKaTcKuGgqAJRMZmK6h1hnk3ncDcUvhgPFS +o5B476ZKQcECzCN4qgmYN4lAncmzcAEEkhJp+QlfkyhAAdtbN8H67FvHQAF6b4g6v9UryqfkKkBu +v/0prxD//kR63YnqB8AeqcdoBRxU/1zAuwRaaX4reJ4DSSRAHUhwgrgqwgUx2B94EWGDHISPBzUY +QgSNcAn6K6F4fscDCtBOhdoRwPW6kIHDwZA7vWoDBF44Qd/tIUAEBCACbIeG4AXxfmFrQ4B4OCYE +JBEQELChmgbAACJioj4JOCKCCLCABZ6EAg1IHwDlyLYAB1gRJhSYgHUQAD9WnQ9+CWBAA+wknTpC +JwQAOw== +} + +proc git_logo {w} { + label $w \ + -borderwidth 1 \ + -relief sunken \ + -background white \ + -image ::git_logo_data + return $w +} diff --git a/git-gui/lib/merge.tcl b/git-gui/lib/merge.tcl index 0e50919d4c..63e14279c1 100644 --- a/git-gui/lib/merge.tcl +++ b/git-gui/lib/merge.tcl @@ -10,10 +10,10 @@ method _can_merge {} { global HEAD commit_type file_states if {[string match amend* $commit_type]} { - info_popup {Cannot merge while amending. + info_popup [mc "Cannot merge while amending. You must finish amending this commit before starting any type of merge. -} +"] return 0 } @@ -24,12 +24,12 @@ You must finish amending this commit before starting any type of merge. # repository_state curType curHEAD curMERGE_HEAD if {$commit_type ne $curType || $HEAD ne $curHEAD} { - info_popup {Last scanned state does not match repository state. + info_popup [mc "Last scanned state does not match repository state. Another Git program has modified this repository since the last scan. A rescan must be performed before a merge can be performed. The rescan will be automatically started now. -} +"] unlock_index rescan ui_ready return 0 @@ -41,22 +41,22 @@ The rescan will be automatically started now. continue; # and pray it works! } U? { - error_popup "You are in the middle of a conflicted merge. + error_popup [mc "You are in the middle of a conflicted merge. -File [short_path $path] has merge conflicts. +File %s has merge conflicts. You must resolve them, stage the file, and commit to complete the current merge. Only then can you begin another merge. -" +" [short_path $path]] unlock_index return 0 } ?? { - error_popup "You are in the middle of a change. + error_popup [mc "You are in the middle of a change. -File [short_path $path] is modified. +File %s is modified. You should complete the current commit before starting a merge. Doing so will help you abort a failed merge, should the need arise. -" +" [short_path $path]] unlock_index return 0 } @@ -103,7 +103,7 @@ method _start {} { regsub {^[^:@]*@} $remote {} remote } set branch [lindex $spec 2] - set stitle "$branch of $remote" + set stitle [mc "%s of %s" $branch $remote] } regsub ^refs/heads/ $branch {} branch puts $fh "$cmit\t\tbranch '$branch' of $remote" @@ -116,9 +116,9 @@ method _start {} { lappend cmd HEAD lappend cmd $name - set msg "Merging $current_branch and $stitle" + set msg [mc "Merging %s and %s" $current_branch $stitle] ui_status "$msg..." - set cons [console::new "Merge" "merge $stitle"] + set cons [console::new [mc "Merge"] "merge $stitle"] console::exec $cons $cmd [cb _finish $cons] wm protocol $w WM_DELETE_WINDOW {} @@ -128,9 +128,9 @@ method _start {} { method _finish {cons ok} { console::done $cons $ok if {$ok} { - set msg {Merge completed successfully.} + set msg [mc "Merge completed successfully."] } else { - set msg {Merge failed. Conflict resolution is required.} + set msg [mc "Merge failed. Conflict resolution is required."] } unlock_index rescan [list ui_status $msg] @@ -147,7 +147,7 @@ constructor dialog {} { } make_toplevel top w - wm title $top "[appname] ([reponame]): Merge" + wm title $top [append "[appname] ([reponame]): " [mc "Merge"]] if {$top ne {.}} { wm geometry $top "+[winfo rootx .]+[winfo rooty .]" } @@ -155,26 +155,26 @@ constructor dialog {} { set _start [cb _start] label $w.header \ - -text "Merge Into $current_branch" \ + -text [mc "Merge Into %s" $current_branch] \ -font font_uibold pack $w.header -side top -fill x frame $w.buttons button $w.buttons.visualize \ - -text Visualize \ + -text [mc Visualize] \ -command [cb _visualize] pack $w.buttons.visualize -side left button $w.buttons.merge \ - -text Merge \ + -text [mc Merge] \ -command $_start pack $w.buttons.merge -side right button $w.buttons.cancel \ - -text {Cancel} \ + -text [mc "Cancel"] \ -command [cb _cancel] pack $w.buttons.cancel -side right -padx 5 pack $w.buttons -side bottom -fill x -pady 10 -padx 10 - set w_rev [::choose_rev::new_unmerged $w.rev {Revision To Merge}] + set w_rev [::choose_rev::new_unmerged $w.rev [mc "Revision To Merge"]] pack $w.rev -anchor nw -fill both -expand 1 -pady 5 -padx 5 bind $w <$M1B-Key-Return> $_start @@ -209,34 +209,34 @@ proc reset_hard {} { global HEAD commit_type file_states if {[string match amend* $commit_type]} { - info_popup {Cannot abort while amending. + info_popup [mc "Cannot abort while amending. You must finish amending this commit. -} +"] return } if {![lock_index abort]} return if {[string match *merge* $commit_type]} { - set op_question "Abort merge? + set op_question [mc "Abort merge? Aborting the current merge will cause *ALL* uncommitted changes to be lost. -Continue with aborting the current merge?" +Continue with aborting the current merge?"] } else { - set op_question "Reset changes? + set op_question [mc "Reset changes? Resetting the changes will cause *ALL* uncommitted changes to be lost. -Continue with resetting the current changes?" +Continue with resetting the current changes?"] } if {[ask_popup $op_question] eq {yes}} { set fd [git_read --stderr read-tree --reset -u -v HEAD] fconfigure $fd -blocking 0 -translation binary fileevent $fd readable [namespace code [list _reset_wait $fd]] - $::main_status start {Aborting} {files reset} + $::main_status start [mc "Aborting"] {files reset} } else { unlock_index } @@ -263,9 +263,9 @@ proc _reset_wait {fd} { catch {file delete [gitdir GITGUI_MSG]} if {$fail} { - warn_popup "Abort failed.\n\n$err" + warn_popup "[mc "Abort failed."]\n\n$err" } - rescan {ui_status {Abort completed. Ready.}} + rescan {ui_status [mc "Abort completed. Ready."]} } else { fconfigure $fd -blocking 0 } diff --git a/git-gui/lib/option.tcl b/git-gui/lib/option.tcl index 063f5df6f7..f812e5e89a 100644 --- a/git-gui/lib/option.tcl +++ b/git-gui/lib/option.tcl @@ -54,85 +54,6 @@ proc save_config {} { } } -proc do_about {} { - global appvers copyright oguilib - global tcl_patchLevel tk_patchLevel - - set w .about_dialog - toplevel $w - wm geometry $w "+[winfo rootx .]+[winfo rooty .]" - - label $w.header -text "About [appname]" \ - -font font_uibold - pack $w.header -side top -fill x - - frame $w.buttons - button $w.buttons.close -text {Close} \ - -default active \ - -command [list destroy $w] - pack $w.buttons.close -side right - pack $w.buttons -side bottom -fill x -pady 10 -padx 10 - - label $w.desc \ - -text "git-gui - a graphical user interface for Git. -$copyright" \ - -padx 5 -pady 5 \ - -justify left \ - -anchor w \ - -borderwidth 1 \ - -relief solid - pack $w.desc -side top -fill x -padx 5 -pady 5 - - set v {} - append v "git-gui version $appvers\n" - append v "[git version]\n" - append v "\n" - if {$tcl_patchLevel eq $tk_patchLevel} { - append v "Tcl/Tk version $tcl_patchLevel" - } else { - append v "Tcl version $tcl_patchLevel" - append v ", Tk version $tk_patchLevel" - } - - set d {} - append d "git wrapper: $::_git\n" - append d "git exec dir: [gitexec]\n" - append d "git-gui lib: $oguilib" - - label $w.vers \ - -text $v \ - -padx 5 -pady 5 \ - -justify left \ - -anchor w \ - -borderwidth 1 \ - -relief solid - pack $w.vers -side top -fill x -padx 5 -pady 5 - - label $w.dirs \ - -text $d \ - -padx 5 -pady 5 \ - -justify left \ - -anchor w \ - -borderwidth 1 \ - -relief solid - pack $w.dirs -side top -fill x -padx 5 -pady 5 - - menu $w.ctxm -tearoff 0 - $w.ctxm add command \ - -label {Copy} \ - -command " - clipboard clear - clipboard append -format STRING -type STRING -- \[$w.vers cget -text\] - " - - bind $w <Visibility> "grab $w; focus $w.buttons.close" - bind $w <Key-Escape> "destroy $w" - bind $w <Key-Return> "destroy $w" - bind_button3 $w.vers "tk_popup $w.ctxm %X %Y; grab $w; focus $w" - wm title $w "About [appname]" - tkwait window $w -} - proc do_options {} { global repo_config global_config font_descs global repo_config_new global_config_new @@ -157,48 +78,44 @@ proc do_options {} { toplevel $w wm geometry $w "+[winfo rootx .]+[winfo rooty .]" - label $w.header -text "Options" \ - -font font_uibold - pack $w.header -side top -fill x - frame $w.buttons - button $w.buttons.restore -text {Restore Defaults} \ + button $w.buttons.restore -text [mc "Restore Defaults"] \ -default normal \ -command do_restore_defaults pack $w.buttons.restore -side left - button $w.buttons.save -text Save \ + button $w.buttons.save -text [mc Save] \ -default active \ -command [list do_save_config $w] pack $w.buttons.save -side right - button $w.buttons.cancel -text {Cancel} \ + button $w.buttons.cancel -text [mc "Cancel"] \ -default normal \ -command [list destroy $w] pack $w.buttons.cancel -side right -padx 5 pack $w.buttons -side bottom -fill x -pady 10 -padx 10 - labelframe $w.repo -text "[reponame] Repository" - labelframe $w.global -text {Global (All Repositories)} + labelframe $w.repo -text [mc "%s Repository" [reponame]] + labelframe $w.global -text [mc "Global (All Repositories)"] pack $w.repo -side left -fill both -expand 1 -pady 5 -padx 5 pack $w.global -side right -fill both -expand 1 -pady 5 -padx 5 set optid 0 foreach option { - {t user.name {User Name}} - {t user.email {Email Address}} - - {b merge.summary {Summarize Merge Commits}} - {i-1..5 merge.verbosity {Merge Verbosity}} - {b merge.diffstat {Show Diffstat After Merge}} - - {b gui.trustmtime {Trust File Modification Timestamps}} - {b gui.pruneduringfetch {Prune Tracking Branches During Fetch}} - {b gui.matchtrackingbranch {Match Tracking Branches}} - {i-0..99 gui.diffcontext {Number of Diff Context Lines}} - {t gui.newbranchtemplate {New Branch Name Template}} + {t user.name {mc "User Name"}} + {t user.email {mc "Email Address"}} + + {b merge.summary {mc "Summarize Merge Commits"}} + {i-1..5 merge.verbosity {mc "Merge Verbosity"}} + {b merge.diffstat {mc "Show Diffstat After Merge"}} + + {b gui.trustmtime {mc "Trust File Modification Timestamps"}} + {b gui.pruneduringfetch {mc "Prune Tracking Branches During Fetch"}} + {b gui.matchtrackingbranch {mc "Match Tracking Branches"}} + {i-0..99 gui.diffcontext {mc "Number of Diff Context Lines"}} + {t gui.newbranchtemplate {mc "New Branch Name Template"}} } { set type [lindex $option 0] set name [lindex $option 1] - set text [lindex $option 2] + set text [eval [lindex $option 2]] incr optid foreach f {repo global} { switch -glob -- $type { @@ -246,7 +163,7 @@ proc do_options {} { foreach option $font_descs { set name [lindex $option 0] set font [lindex $option 1] - set text [lindex $option 2] + set text [eval [lindex $option 2]] set global_config_new(gui.$font^^family) \ [font configure $font -family] @@ -278,7 +195,13 @@ proc do_options {} { bind $w <Visibility> "grab $w; focus $w.buttons.save" bind $w <Key-Escape> "destroy $w" bind $w <Key-Return> [list do_save_config $w] - wm title $w "[appname] ([reponame]): Options" + + if {[is_MacOSX]} { + set t [mc "Preferences"] + } else { + set t [mc "Options"] + } + wm title $w "[appname] ([reponame]): $t" tkwait window $w } @@ -309,7 +232,7 @@ proc do_restore_defaults {} { proc do_save_config {w} { if {[catch {save_config} err]} { - error_popup "Failed to completely save options:\n\n$err" + error_popup [strcat [mc "Failed to completely save options:"] "\n\n$err"] } reshow_diff destroy $w diff --git a/git-gui/lib/remote.tcl b/git-gui/lib/remote.tcl index cf9b9d5829..0e86ddac09 100644 --- a/git-gui/lib/remote.tcl +++ b/git-gui/lib/remote.tcl @@ -135,8 +135,10 @@ proc load_all_remotes {} { proc populate_fetch_menu {} { global all_remotes repo_config - set m .mbar.fetch - set prune_list [list] + set remote_m .mbar.remote + set fetch_m $remote_m.fetch + set prune_m $remote_m.prune + foreach r $all_remotes { set enable 0 if {![catch {set a $repo_config(remote.$r.url)}]} { @@ -157,28 +159,34 @@ proc populate_fetch_menu {} { } if {$enable} { - lappend prune_list $r - $m add command \ - -label "Fetch from $r..." \ + if {![winfo exists $fetch_m]} { + menu $prune_m + $remote_m insert 0 cascade \ + -label [mc "Prune from"] \ + -menu $prune_m + + menu $fetch_m + $remote_m insert 0 cascade \ + -label [mc "Fetch from"] \ + -menu $fetch_m + } + + $fetch_m add command \ + -label $r \ -command [list fetch_from $r] + $prune_m add command \ + -label $r \ + -command [list prune_from $r] } } - - if {$prune_list ne {}} { - $m add separator - } - foreach r $prune_list { - $m add command \ - -label "Prune from $r..." \ - -command [list prune_from $r] - } } proc populate_push_menu {} { global all_remotes repo_config - set m .mbar.push - set fast_count 0 + set remote_m .mbar.remote + set push_m $remote_m.push + foreach r $all_remotes { set enable 0 if {![catch {set a $repo_config(remote.$r.url)}]} { @@ -199,13 +207,16 @@ proc populate_push_menu {} { } if {$enable} { - if {!$fast_count} { - $m add separator + if {![winfo exists $push_m]} { + menu $push_m + $remote_m insert 0 cascade \ + -label [mc "Push to"] \ + -menu $push_m } - $m add command \ - -label "Push to $r..." \ + + $push_m add command \ + -label $r \ -command [list push_to $r] - incr fast_count } } } diff --git a/git-gui/lib/remote_branch_delete.tcl b/git-gui/lib/remote_branch_delete.tcl index c88a360db5..c7b8148698 100644 --- a/git-gui/lib/remote_branch_delete.tcl +++ b/git-gui/lib/remote_branch_delete.tcl @@ -26,28 +26,28 @@ constructor dialog {} { global all_remotes M1B make_toplevel top w - wm title $top "[appname] ([reponame]): Delete Remote Branch" + wm title $top [append "[appname] ([reponame]): " [mc "Delete Remote Branch"]] if {$top ne {.}} { wm geometry $top "+[winfo rootx .]+[winfo rooty .]" } - label $w.header -text {Delete Remote Branch} -font font_uibold + label $w.header -text [mc "Delete Remote Branch"] -font font_uibold pack $w.header -side top -fill x frame $w.buttons - button $w.buttons.delete -text Delete \ + button $w.buttons.delete -text [mc Delete] \ -default active \ -command [cb _delete] pack $w.buttons.delete -side right - button $w.buttons.cancel -text {Cancel} \ + button $w.buttons.cancel -text [mc "Cancel"] \ -command [list destroy $w] pack $w.buttons.cancel -side right -padx 5 pack $w.buttons -side bottom -fill x -pady 10 -padx 10 - labelframe $w.dest -text {From Repository} + labelframe $w.dest -text [mc "From Repository"] if {$all_remotes ne {}} { radiobutton $w.dest.remote_r \ - -text {Remote:} \ + -text [mc "Remote:"] \ -value remote \ -variable @urltype eval tk_optionMenu $w.dest.remote_m @remote $all_remotes @@ -63,7 +63,7 @@ constructor dialog {} { set urltype url } radiobutton $w.dest.url_r \ - -text {Arbitrary URL:} \ + -text [mc "Arbitrary URL:"] \ -value url \ -variable @urltype entry $w.dest.url_t \ @@ -81,7 +81,7 @@ constructor dialog {} { grid columnconfigure $w.dest 1 -weight 1 pack $w.dest -anchor nw -fill x -pady 5 -padx 5 - labelframe $w.heads -text {Branches} + labelframe $w.heads -text [mc "Branches"] listbox $w.heads.l \ -height 10 \ -width 70 \ @@ -96,7 +96,7 @@ constructor dialog {} { -anchor w \ -justify left button $w.heads.footer.rescan \ - -text {Rescan} \ + -text [mc "Rescan"] \ -command [cb _rescan] pack $w.heads.footer.status -side left -fill x pack $w.heads.footer.rescan -side right @@ -106,9 +106,9 @@ constructor dialog {} { pack $w.heads.l -side left -fill both -expand 1 pack $w.heads -fill both -expand 1 -pady 5 -padx 5 - labelframe $w.validate -text {Delete Only If} + labelframe $w.validate -text [mc "Delete Only If"] radiobutton $w.validate.head_r \ - -text {Merged Into:} \ + -text [mc "Merged Into:"] \ -value head \ -variable @checktype set head_m [tk_optionMenu $w.validate.head_m @check_head {}] @@ -116,7 +116,7 @@ constructor dialog {} { trace add variable @check_head write [cb _write_check_head] grid $w.validate.head_r $w.validate.head_m -sticky w radiobutton $w.validate.always_r \ - -text {Always (Do not perform merge checks)} \ + -text [mc "Always (Do not perform merge checks)"] \ -value always \ -variable @checktype grid $w.validate.always_r -columnspan 2 -sticky w @@ -149,7 +149,7 @@ method _delete {} { -type ok \ -title [wm title $w] \ -parent $w \ - -message "A branch is required for 'Merged Into'." + -message [mc "A branch is required for 'Merged Into'."] return } set crev $full_cache("$cache\nrefs/heads/$check_head") @@ -181,14 +181,12 @@ method _delete {} { } if {$not_merged ne {}} { - set msg "The following branches are not completely merged into $check_head: + set msg [mc "The following branches are not completely merged into %s: - - [join $not_merged "\n - "]" + - %s" $check_head [join $not_merged "\n - "]] if {$need_fetch} { - append msg " - -One or more of the merge tests failed because you have not fetched the necessary commits. Try fetching from $uri first." + append msg "\n\n" [mc "One or more of the merge tests failed because you have not fetched the necessary commits. Try fetching from %s first." $uri] } tk_messageBox \ @@ -206,7 +204,7 @@ One or more of the merge tests failed because you have not fetched the necessary -type ok \ -title [wm title $w] \ -parent $w \ - -message "Please select one or more branches to delete." + -message [mc "Please select one or more branches to delete."] return } @@ -215,9 +213,9 @@ One or more of the merge tests failed because you have not fetched the necessary -type yesno \ -title [wm title $w] \ -parent $w \ - -message {Recovering deleted branches is difficult. + -message [mc "Recovering deleted branches is difficult. -Delete the selected branches?}] ne yes} { +Delete the selected branches?"]] ne yes} { return } @@ -225,7 +223,7 @@ Delete the selected branches?}] ne yes} { set cons [console::new \ "push $uri" \ - "Deleting branches from $uri"] + [mc "Deleting branches from %s" $uri]] console::exec $cons $push_cmd } @@ -285,12 +283,12 @@ method _load {cache uri} { $w.heads.l conf -state disabled set head_list [list] set full_list [list] - set status {No repository selected.} + set status [mc "No repository selected."] return } if {[catch {set x $cached($cache)}]} { - set status "Scanning $uri..." + set status [mc "Scanning %s..." $uri] $w.heads.l conf -state disabled set head_list [list] set full_list [list] diff --git a/git-gui/lib/shortcut.tcl b/git-gui/lib/shortcut.tcl index c36be2f3cd..38c3151b05 100644 --- a/git-gui/lib/shortcut.tcl +++ b/git-gui/lib/shortcut.tcl @@ -2,28 +2,22 @@ # Copyright (C) 2006, 2007 Shawn Pearce proc do_windows_shortcut {} { - global argv0 - set fn [tk_getSaveFile \ -parent . \ - -title "[appname] ([reponame]): Create Desktop Icon" \ - -initialfile "Git [reponame].bat"] + -title [append "[appname] ([reponame]): " [mc "Create Desktop Icon"]] \ + -initialfile "Git [reponame].lnk"] if {$fn != {}} { - if {[file extension $fn] ne {.bat}} { - set fn ${fn}.bat + if {[file extension $fn] ne {.lnk}} { + set fn ${fn}.lnk } if {[catch { - set ge [file normalize [file dirname $::_git]] - set fd [open $fn w] - puts $fd "@ECHO Entering [reponame]" - puts $fd "@ECHO Starting git-gui... please wait..." - puts $fd "@SET PATH=$ge;%PATH%" - puts $fd "@SET GIT_DIR=[file normalize [gitdir]]" - puts -nonewline $fd "@\"[info nameofexecutable]\"" - puts $fd " \"[file normalize $argv0]\"" - close $fd + win32_create_lnk $fn [list \ + [info nameofexecutable] \ + [file normalize $::argv0] \ + ] \ + [file dirname [file normalize [gitdir]]] } err]} { - error_popup "Cannot write script:\n\n$err" + error_popup [strcat [mc "Cannot write shortcut:"] "\n\n$err"] } } } @@ -42,15 +36,14 @@ proc do_cygwin_shortcut {} { } set fn [tk_getSaveFile \ -parent . \ - -title "[appname] ([reponame]): Create Desktop Icon" \ + -title [append "[appname] ([reponame]): " [mc "Create Desktop Icon"]] \ -initialdir $desktop \ - -initialfile "Git [reponame].bat"] + -initialfile "Git [reponame].lnk"] if {$fn != {}} { - if {[file extension $fn] ne {.bat}} { - set fn ${fn}.bat + if {[file extension $fn] ne {.lnk}} { + set fn ${fn}.lnk } if {[catch { - set fd [open $fn w] set sh [exec cygpath \ --windows \ --absolute \ @@ -59,19 +52,13 @@ proc do_cygwin_shortcut {} { --unix \ --absolute \ $argv0] - set gd [exec cygpath \ - --unix \ - --absolute \ - [gitdir]] - puts $fd "@ECHO Entering [reponame]" - puts $fd "@ECHO Starting git-gui... please wait..." - puts -nonewline $fd "@\"$sh\" --login -c \"" - puts -nonewline $fd "GIT_DIR=[sq $gd]" - puts -nonewline $fd " [sq $me]" - puts $fd " &\"" - close $fd + win32_create_lnk $fn [list \ + $sh -c \ + "CHERE_INVOKING=1 source /etc/profile;[sq $me]" \ + ] \ + [file dirname [file normalize [gitdir]]] } err]} { - error_popup "Cannot write script:\n\n$err" + error_popup [strcat [mc "Cannot write shortcut:"] "\n\n$err"] } } } @@ -81,7 +68,7 @@ proc do_macosx_app {} { set fn [tk_getSaveFile \ -parent . \ - -title "[appname] ([reponame]): Create Desktop Icon" \ + -title [append "[appname] ([reponame]): " [mc "Create Desktop Icon"]] \ -initialdir [file join $env(HOME) Desktop] \ -initialfile "Git [reponame].app"] if {$fn != {}} { @@ -146,7 +133,7 @@ proc do_macosx_app {} { file attributes $exe -permissions u+x,g+x,o+x } err]} { - error_popup "Cannot write icon:\n\n$err" + error_popup [strcat [mc "Cannot write icon:"] "\n\n$err"] } } } diff --git a/git-gui/lib/status_bar.tcl b/git-gui/lib/status_bar.tcl index 3bf79eb6e0..51d4177551 100644 --- a/git-gui/lib/status_bar.tcl +++ b/git-gui/lib/status_bar.tcl @@ -6,6 +6,7 @@ class status_bar { field w ; # our own window path field w_l ; # text widget we draw messages into field w_c ; # canvas we draw a progress bar into +field c_pack ; # script to pack the canvas with field status {}; # single line of text we show field prefix {}; # text we format into status field units {}; # unit of progress @@ -24,6 +25,29 @@ constructor new {path} { -anchor w \ -justify left pack $w_l -side left + set c_pack [cb _oneline_pack] + + bind $w <Destroy> [cb _delete %W] + return $this +} + +method _oneline_pack {} { + $w_c conf -width 100 + pack $w_c -side right +} + +constructor two_line {path} { + set w $path + set w_l $w.l + set w_c $w.c + + frame $w + label $w_l \ + -textvariable @status \ + -anchor w \ + -justify left + pack $w_l -anchor w -fill x + set c_pack [list pack $w_c -fill x] bind $w <Destroy> [cb _delete %W] return $this @@ -34,13 +58,12 @@ method start {msg uds} { $w_c coords bar 0 0 0 20 } else { canvas $w_c \ - -width 100 \ -height [expr {int([winfo reqheight $w_l] * 0.6)}] \ -borderwidth 1 \ -relief groove \ -highlightt 0 $w_c create rectangle 0 0 0 20 -tags bar -fill navy - pack $w_c -side right + eval $c_pack } set status $msg @@ -53,11 +76,16 @@ method update {have total} { set pdone 0 if {$total > 0} { set pdone [expr {100 * $have / $total}] + set cdone [expr {[winfo width $w_c] * $have / $total}] } - set status [format "%s ... %i of %i %s (%2i%%)" \ - $prefix $have $total $units $pdone] - $w_c coords bar 0 0 $pdone 20 + set prec [string length [format %i $total]] + set status [mc "%s ... %*i of %*i %s (%3i%%)" \ + $prefix \ + $prec $have \ + $prec $total \ + $units $pdone] + $w_c coords bar 0 0 $cdone 20 } method update_meter {buf} { diff --git a/git-gui/lib/transport.tcl b/git-gui/lib/transport.tcl index 3a22bd40d4..8e6a9d0a60 100644 --- a/git-gui/lib/transport.tcl +++ b/git-gui/lib/transport.tcl @@ -3,8 +3,8 @@ proc fetch_from {remote} { set w [console::new \ - "fetch $remote" \ - "Fetching new changes from $remote"] + [mc "fetch %s" $remote] \ + [mc "Fetching new changes from %s" $remote]] set cmds [list] lappend cmds [list exec git fetch $remote] if {[is_config_true gui.pruneduringfetch]} { @@ -15,15 +15,15 @@ proc fetch_from {remote} { proc prune_from {remote} { set w [console::new \ - "remote prune $remote" \ - "Pruning tracking branches deleted from $remote"] + [mc "remote prune %s" $remote] \ + [mc "Pruning tracking branches deleted from %s" $remote]] console::exec $w [list git remote prune $remote] } proc push_to {remote} { set w [console::new \ - "push $remote" \ - "Pushing changes to $remote"] + [mc "push %s" $remote] \ + [mc "Pushing changes to %s" $remote]] set cmd [list git push] lappend cmd -v lappend cmd $remote @@ -32,6 +32,7 @@ proc push_to {remote} { proc start_push_anywhere_action {w} { global push_urltype push_remote push_url push_thin push_tags + global push_force set r_url {} switch -- $push_urltype { @@ -45,6 +46,9 @@ proc start_push_anywhere_action {w} { if {$push_thin} { lappend cmd --thin } + if {$push_force} { + lappend cmd --force + } if {$push_tags} { lappend cmd --tags } @@ -64,8 +68,8 @@ proc start_push_anywhere_action {w} { } set cons [console::new \ - "push $r_url" \ - "Pushing $cnt $unit to $r_url"] + [mc "push %s" $r_url] \ + [mc "Pushing %s %s to %s" $cnt $unit $r_url]] console::exec $cons $cmd destroy $w } @@ -76,26 +80,27 @@ trace add variable push_remote write \ proc do_push_anywhere {} { global all_remotes current_branch global push_urltype push_remote push_url push_thin push_tags + global push_force set w .push_setup toplevel $w wm geometry $w "+[winfo rootx .]+[winfo rooty .]" - label $w.header -text {Push Branches} -font font_uibold + label $w.header -text [mc "Push Branches"] -font font_uibold pack $w.header -side top -fill x frame $w.buttons - button $w.buttons.create -text Push \ + button $w.buttons.create -text [mc Push] \ -default active \ -command [list start_push_anywhere_action $w] pack $w.buttons.create -side right - button $w.buttons.cancel -text {Cancel} \ + button $w.buttons.cancel -text [mc "Cancel"] \ -default normal \ -command [list destroy $w] pack $w.buttons.cancel -side right -padx 5 pack $w.buttons -side bottom -fill x -pady 10 -padx 10 - labelframe $w.source -text {Source Branches} + labelframe $w.source -text [mc "Source Branches"] listbox $w.source.l \ -height 10 \ -width 70 \ @@ -112,10 +117,10 @@ proc do_push_anywhere {} { pack $w.source.l -side left -fill both -expand 1 pack $w.source -fill both -expand 1 -pady 5 -padx 5 - labelframe $w.dest -text {Destination Repository} + labelframe $w.dest -text [mc "Destination Repository"] if {$all_remotes ne {}} { radiobutton $w.dest.remote_r \ - -text {Remote:} \ + -text [mc "Remote:"] \ -value remote \ -variable push_urltype eval tk_optionMenu $w.dest.remote_m push_remote $all_remotes @@ -130,7 +135,7 @@ proc do_push_anywhere {} { set push_urltype url } radiobutton $w.dest.url_r \ - -text {Arbitrary URL:} \ + -text [mc "Arbitrary URL:"] \ -value url \ -variable push_urltype entry $w.dest.url_t \ @@ -150,25 +155,30 @@ proc do_push_anywhere {} { grid columnconfigure $w.dest 1 -weight 1 pack $w.dest -anchor nw -fill x -pady 5 -padx 5 - labelframe $w.options -text {Transfer Options} + labelframe $w.options -text [mc "Transfer Options"] + checkbutton $w.options.force \ + -text [mc "Force overwrite existing branch (may discard changes)"] \ + -variable push_force + grid $w.options.force -columnspan 2 -sticky w checkbutton $w.options.thin \ - -text {Use thin pack (for slow network connections)} \ + -text [mc "Use thin pack (for slow network connections)"] \ -variable push_thin grid $w.options.thin -columnspan 2 -sticky w checkbutton $w.options.tags \ - -text {Include tags} \ + -text [mc "Include tags"] \ -variable push_tags grid $w.options.tags -columnspan 2 -sticky w grid columnconfigure $w.options 1 -weight 1 pack $w.options -anchor nw -fill x -pady 5 -padx 5 set push_url {} + set push_force 0 set push_thin 0 set push_tags 0 bind $w <Visibility> "grab $w; focus $w.buttons.create" bind $w <Key-Escape> "destroy $w" bind $w <Key-Return> [list start_push_anywhere_action $w] - wm title $w "[appname] ([reponame]): Push" + wm title $w [append "[appname] ([reponame]): " [mc "Push"]] tkwait window $w } diff --git a/git-gui/lib/win32.tcl b/git-gui/lib/win32.tcl new file mode 100644 index 0000000000..d7f93d045d --- /dev/null +++ b/git-gui/lib/win32.tcl @@ -0,0 +1,26 @@ +# git-gui Misc. native Windows 32 support +# Copyright (C) 2007 Shawn Pearce + +proc win32_read_lnk {lnk_path} { + return [exec cscript.exe \ + /E:jscript \ + /nologo \ + [file join $::oguilib win32_shortcut.js] \ + $lnk_path] +} + +proc win32_create_lnk {lnk_path lnk_exec lnk_dir} { + global oguilib + + set lnk_args [lrange $lnk_exec 1 end] + set lnk_exec [lindex $lnk_exec 0] + + eval [list exec wscript.exe \ + /E:jscript \ + /nologo \ + [file join $oguilib win32_shortcut.js] \ + $lnk_path \ + [file join $oguilib git-gui.ico] \ + $lnk_dir \ + $lnk_exec] $lnk_args +} diff --git a/git-gui/lib/win32_shortcut.js b/git-gui/lib/win32_shortcut.js new file mode 100644 index 0000000000..117923f886 --- /dev/null +++ b/git-gui/lib/win32_shortcut.js @@ -0,0 +1,34 @@ +// git-gui Windows shortcut support +// Copyright (C) 2007 Shawn Pearce + +var WshShell = WScript.CreateObject("WScript.Shell"); +var argv = WScript.Arguments; +var argi = 0; +var lnk_path = argv.item(argi++); +var ico_path = argi < argv.length ? argv.item(argi++) : undefined; +var dir_path = argi < argv.length ? argv.item(argi++) : undefined; +var lnk_exec = argi < argv.length ? argv.item(argi++) : undefined; +var lnk_args = ''; +while (argi < argv.length) { + var s = argv.item(argi++); + if (lnk_args != '') + lnk_args += ' '; + if (s.indexOf(' ') >= 0) { + lnk_args += '"'; + lnk_args += s; + lnk_args += '"'; + } else { + lnk_args += s; + } +} + +var lnk = WshShell.CreateShortcut(lnk_path); +if (argv.length == 1) { + WScript.echo(lnk.TargetPath); +} else { + lnk.TargetPath = lnk_exec; + lnk.Arguments = lnk_args; + lnk.IconLocation = ico_path + ", 0"; + lnk.WorkingDirectory = dir_path; + lnk.Save(); +} diff --git a/git-gui/macosx/AppMain.tcl b/git-gui/macosx/AppMain.tcl new file mode 100644 index 0000000000..41ca08e2b7 --- /dev/null +++ b/git-gui/macosx/AppMain.tcl @@ -0,0 +1,22 @@ +set gitexecdir {@@gitexecdir@@} +set gitguilib {@@GITGUI_LIBDIR@@} +set env(PATH) "$gitexecdir:$env(PATH)" + +if {[string first -psn [lindex $argv 0]] == 0} { + lset argv 0 [file join $gitexecdir git-gui] +} + +if {[file tail [lindex $argv 0]] eq {gitk}} { + set argv0 [file join $gitexecdir gitk] + set AppMain_source $argv0 +} else { + set argv0 [file join $gitexecdir [file tail [lindex $argv 0]]] + set AppMain_source [file join $gitguilib git-gui.tcl] + if {[pwd] eq {/}} { + cd $env(HOME) + } +} + +unset gitexecdir gitguilib +set argv [lrange $argv 1 end] +source $AppMain_source diff --git a/git-gui/macosx/Info.plist b/git-gui/macosx/Info.plist new file mode 100644 index 0000000000..99913ec57a --- /dev/null +++ b/git-gui/macosx/Info.plist @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleDevelopmentRegion</key> + <string>English</string> + <key>CFBundleExecutable</key> + <string>Wish</string> + <key>CFBundleGetInfoString</key> + <string>Git Gui @@GITGUI_VERSION@@ © 2006-2007 Shawn Pearce, et. al.</string> + <key>CFBundleIconFile</key> + <string>git-gui.icns</string> + <key>CFBundleIdentifier</key> + <string>cz.or.repo.git-gui</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string>Git Gui</string> + <key>CFBundlePackageType</key> + <string>APPL</string> + <key>CFBundleShortVersionString</key> + <string>@@GITGUI_VERSION@@</string> + <key>CFBundleSignature</key> + <string>GITg</string> + <key>CFBundleVersion</key> + <string>@@GITGUI_VERSION@@</string> +</dict> +</plist> diff --git a/git-gui/macosx/git-gui.icns b/git-gui/macosx/git-gui.icns Binary files differnew file mode 100644 index 0000000000..77d88a77a7 --- /dev/null +++ b/git-gui/macosx/git-gui.icns diff --git a/git-gui/po/.gitignore b/git-gui/po/.gitignore new file mode 100644 index 0000000000..a89cf44969 --- /dev/null +++ b/git-gui/po/.gitignore @@ -0,0 +1,2 @@ +*.msg +*~ diff --git a/git-gui/po/README b/git-gui/po/README new file mode 100644 index 0000000000..9d8b7364fd --- /dev/null +++ b/git-gui/po/README @@ -0,0 +1,209 @@ +Localizing git-gui for your language +==================================== + +This short note is to help you, who reads and writes English and your +own language, help us getting git-gui localized for more languages. It +does not try to be a comprehensive manual of GNU gettext, which is the +i18n framework we use, but tries to help you get started by covering the +basics and how it is used in this project. + +1. Getting started. + +You would first need to have a working "git". Your distribution may +have it as "git-core" package (do not get "GNU Interactive Tools" -- +that is a different "git"). You would also need GNU gettext toolchain +to test the resulting translation out. Although you can work on message +translation files with a regular text editor, it is a good idea to have +specialized so-called "po file editors" (e.g. emacs po-mode, KBabel, +poedit, GTranslator --- any of them would work well). Please install +them. + +You would then need to clone the git-gui internationalization project +repository, so that you can work on it: + + $ git clone mob@repo.or.cz:/srv/git/git-gui/git-gui-i18n.git/ + $ cd git-gui-i18n + $ git checkout --track -b mob origin/mob + $ git config remote.origin.push mob + +The "git checkout" command creates a 'mob' branch from upstream's +corresponding branch and makes it your current branch. You will be +working on this branch. + +The "git config" command records in your repository configuration file +that you would push "mob" branch to the upstream when you say "git +push". + + +2. Starting a new language. + +In the git-gui-i18n directory is a po/ subdirectory. It has a +handful files whose names end with ".po". Is there a file that has +messages in your language? + +If you do not know what your language should be named, you need to find +it. This currently follows ISO 639-1 two letter codes: + + http://www.loc.gov/standards/iso639-2/php/code_list.php + +For example, if you are preparing a translation for Afrikaans, the +language code is "af". If there already is a translation for your +language, you do not have to perform any step in this section, but keep +reading, because we are covering the basics. + +If you did not find your language, you would need to start one yourself. +Copy po/git-gui.pot file to po/af.po (replace "af" with the code for +your language). Edit the first several lines to match existing *.po +files to make it clear this is a translation table for git-gui project, +and you are the primary translator. The result of your editing would +look something like this: + + # Translation of git-gui to Afrikaans + # Copyright (C) 2007 Shawn Pearce + # This file is distributed under the same license as the git-gui package. + # YOUR NAME <YOUR@E-MAIL.ADDRESS>, 2007. + # + #, fuzzy + msgid "" + msgstr "" + "Project-Id-Version: git-gui\n" + "Report-Msgid-Bugs-To: \n" + "POT-Creation-Date: 2007-07-24 22:19+0300\n" + "PO-Revision-Date: 2007-07-25 18:00+0900\n" + "Last-Translator: YOUR NAME <YOUR@E-MAIL.ADDRESS>\n" + "Language-Team: Afrikaans\n" + "MIME-Version: 1.0\n" + "Content-Type: text/plain; charset=UTF-8\n" + "Content-Transfer-Encoding: 8bit\n" + +You will find many pairs of a "msgid" line followed by a "msgstr" line. +These pairs define how messages in git-gui application are translated to +your language. Your primarily job is to fill in the empty double quote +pairs on msgstr lines with the translation of the strings on their +matching msgid lines. A few tips: + + - Control characters, such as newlines, are written in backslash + sequence similar to string literals in the C programming language. + When the string given on a msgid line has such a backslash sequence, + you would typically want to have corresponding ones in the string on + your msgstr line. + + - Some messages contain an optional context indicator at the end, + for example "@@noun" or "@@verb". This indicator allows the + software to select the correct translation depending upon the use. + The indicator is not actually part of the message and will not + be shown to the end-user. + + If your language does not require a different translation you + will still need to translate both messages. + + - Often the messages being translated are format strings given to + "printf()"-like functions. Make sure "%s", "%d", and "%%" in your + translated messages match the original. + + When you have to change the order of words, you can add "<number>\$" + between '%' and the conversion ('s', 'd', etc.) to say "<number>-th + parameter to the format string is used at this point". For example, + if the original message is like this: + + "Length is %d, Weight is %d" + + and if for whatever reason your translation needs to say weight first + and then length, you can say something like: + + "WEIGHT IS %2\$d, LENGTH IS %1\$d" + + The reason you need a backslash before dollar sign is because + this is a double quoted string in Tcl language, and without + it the letter introduces a variable interpolation, which you + do not want here. + + - A long message can be split across multiple lines by ending the + string with a double quote, and starting another string on the next + line with another double quote. They will be concatenated in the + result. For example: + + #: lib/remote_branch_delete.tcl:189 + #, tcl-format + msgid "" + "One or more of the merge tests failed because you have not fetched the " + "necessary commits. Try fetching from %s first." + msgstr "" + "HERE YOU WILL WRITE YOUR TRANSLATION OF THE ABOVE LONG " + "MESSAGE IN YOUR LANGUAGE." + +You can test your translation by running "make install", which would +create po/af.msg file and installs the result, and then running the +resulting git-gui under your locale: + + $ make install + $ LANG=af git-gui + +There is a trick to test your translation without first installing: + + $ make + $ LANG=af ./git-gui.sh + +When you are satisfied with your translation, commit your changes, and +push it back to the 'mob' branch: + + $ edit po/af.po + ... be sure to update Last-Translator: and + ... PO-Revision-Date: lines. + $ git add po/af.po + $ git commit -m 'Started Afrikaans translation.' + $ git push + + +3. Updating your translation. + +There may already be a translation for your language, and you may want +to contribute an update. This may be because you would want to improve +the translation of existing messages, or because the git-gui software +itself was updated and there are new messages that need translation. + +In any case, make sure you are up-to-date before starting your work: + + $ git pull + +In the former case, you will edit po/af.po (again, replace "af" with +your language code), and after testing and updating the Last-Translator: +and PO-Revision-Date: lines, "add/commit/push" as in the previous +section. + +By comparing "POT-Creation-Date:" line in po/git-gui.pot file and +po/af.po file, you can tell if there are new messages that need to be +translated. You would need the GNU gettext package to perform this +step. + + $ msgmerge -U po/af.po po/git-gui.pot + +[NEEDSWORK: who is responsible for updating po/git-gui.pot file by +running xgettext? IIRC, Christian recommended against running it +nilly-willy because it can become a source of unnecessary merge +conflicts. Perhaps we should mention something like " + +The po/git-gui.pot file is updated by the internationalization +coordinator from time to time. You _could_ update it yourself, but +translators are discouraged from doing so because we would want all +language teams to be working off of the same version of git-gui.pot. + +" here?] + +This updates po/af.po (again, replace "af" with your language +code) so that it contains msgid lines (i.e. the original) that +your translation did not have before. There are a few things to +watch out for: + + - The original text in English of an older message you already + translated might have been changed. You will notice a comment line + that begins with "#, fuzzy" in front of such a message. msgmerge + tool made its best effort to match your old translation with the + message from the updated software, but you may find cases that it + matched your old translated message to a new msgid and the pairing + does not make any sense -- you would need to fix them, and then + remove the "#, fuzzy" line from the message (your fixed translation + of the message will not be used before you remove the marker). + + - New messages added to the software will have msgstr lines with empty + strings. You would need to translate them. diff --git a/git-gui/po/de.po b/git-gui/po/de.po new file mode 100644 index 0000000000..ac8ae0a205 --- /dev/null +++ b/git-gui/po/de.po @@ -0,0 +1,1904 @@ +# Translation of git-gui to German. +# Copyright (C) 2007 Shawn Pearce, et al. +# This file is distributed under the same license as the git package. +# Christian Stimming <stimming@tuhh.de>, 2007 +# +msgid "" +msgstr "" +"Project-Id-Version: git-gui\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2007-11-24 10:36+0100\n" +"PO-Revision-Date: 2007-11-24 10:55+0100\n" +"Last-Translator: Christian Stimming <stimming@tuhh.de>\n" +"Language-Team: German\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: git-gui.sh:41 git-gui.sh:604 git-gui.sh:618 git-gui.sh:631 git-gui.sh:714 +#: git-gui.sh:733 +msgid "git-gui: fatal error" +msgstr "git-gui: Programmfehler" + +#: git-gui.sh:565 +#, tcl-format +msgid "Invalid font specified in %s:" +msgstr "Ungültige Zeichensatz-Angabe in %s:" + +#: git-gui.sh:590 +msgid "Main Font" +msgstr "Programmschriftart" + +#: git-gui.sh:591 +msgid "Diff/Console Font" +msgstr "Vergleich-Schriftart" + +#: git-gui.sh:605 +msgid "Cannot find git in PATH." +msgstr "Git kann im PATH nicht gefunden werden." + +#: git-gui.sh:632 +msgid "Cannot parse Git version string:" +msgstr "Git Versionsangabe kann nicht erkannt werden:" + +#: git-gui.sh:650 +#, tcl-format +msgid "" +"Git version cannot be determined.\n" +"\n" +"%s claims it is version '%s'.\n" +"\n" +"%s requires at least Git 1.5.0 or later.\n" +"\n" +"Assume '%s' is version 1.5.0?\n" +msgstr "" +"Die Version von Git kann nicht bestimmt werden.\n" +"\n" +"»%s« behauptet, es sei Version »%s«.\n" +"\n" +"%s benötigt mindestens Git 1.5.0 oder höher.\n" +"\n" +"Soll angenommen werden, »%s« sei Version 1.5.0?\n" + +#: git-gui.sh:888 +msgid "Git directory not found:" +msgstr "Git-Verzeichnis nicht gefunden:" + +#: git-gui.sh:895 +msgid "Cannot move to top of working directory:" +msgstr "" +"Es konnte nicht in das oberste Verzeichnis der Arbeitskopie gewechselt " +"werden:" + +#: git-gui.sh:902 +msgid "Cannot use funny .git directory:" +msgstr "Unerwartete Struktur des .git Verzeichnis:" + +#: git-gui.sh:907 +msgid "No working directory" +msgstr "Kein Arbeitsverzeichnis" + +#: git-gui.sh:1054 +msgid "Refreshing file status..." +msgstr "Dateistatus aktualisieren..." + +#: git-gui.sh:1119 +msgid "Scanning for modified files ..." +msgstr "Nach geänderten Dateien suchen..." + +#: git-gui.sh:1294 lib/browser.tcl:245 +msgid "Ready." +msgstr "Bereit." + +#: git-gui.sh:1560 +msgid "Unmodified" +msgstr "Unverändert" + +#: git-gui.sh:1562 +msgid "Modified, not staged" +msgstr "Verändert, nicht bereitgestellt" + +#: git-gui.sh:1563 git-gui.sh:1568 +msgid "Staged for commit" +msgstr "Bereitgestellt zum Eintragen" + +#: git-gui.sh:1564 git-gui.sh:1569 +msgid "Portions staged for commit" +msgstr "Teilweise bereitgestellt zum Eintragen" + +#: git-gui.sh:1565 git-gui.sh:1570 +msgid "Staged for commit, missing" +msgstr "Bereitgestellt zum Eintragen, fehlend" + +#: git-gui.sh:1567 +msgid "Untracked, not staged" +msgstr "Nicht unter Versionskontrolle, nicht bereitgestellt" + +#: git-gui.sh:1572 +msgid "Missing" +msgstr "Fehlend" + +#: git-gui.sh:1573 +msgid "Staged for removal" +msgstr "Bereitgestellt zum Löschen" + +#: git-gui.sh:1574 +msgid "Staged for removal, still present" +msgstr "Bereitgestellt zum Löschen, trotzdem vorhanden" + +#: git-gui.sh:1576 git-gui.sh:1577 git-gui.sh:1578 git-gui.sh:1579 +msgid "Requires merge resolution" +msgstr "Konfliktauflösung nötig" + +#: git-gui.sh:1614 +msgid "Starting gitk... please wait..." +msgstr "Gitk wird gestartet... bitte warten." + +#: git-gui.sh:1623 +#, tcl-format +msgid "" +"Unable to start gitk:\n" +"\n" +"%s does not exist" +msgstr "" +"Gitk kann nicht gestartet werden:\n" +"\n" +"%s existiert nicht" + +#: git-gui.sh:1823 lib/choose_repository.tcl:35 +msgid "Repository" +msgstr "Projektarchiv" + +#: git-gui.sh:1824 +msgid "Edit" +msgstr "Bearbeiten" + +#: git-gui.sh:1826 lib/choose_rev.tcl:560 +msgid "Branch" +msgstr "Zweig" + +#: git-gui.sh:1829 lib/choose_rev.tcl:547 +msgid "Commit@@noun" +msgstr "Version" + +#: git-gui.sh:1832 lib/merge.tcl:121 lib/merge.tcl:150 lib/merge.tcl:168 +msgid "Merge" +msgstr "Zusammenführen" + +#: git-gui.sh:1833 lib/choose_rev.tcl:556 +msgid "Remote" +msgstr "Andere Archive" + +#: git-gui.sh:1842 +msgid "Browse Current Branch's Files" +msgstr "Aktuellen Zweig durchblättern" + +#: git-gui.sh:1846 +msgid "Browse Branch Files..." +msgstr "Einen Zweig durchblättern..." + +#: git-gui.sh:1851 +msgid "Visualize Current Branch's History" +msgstr "Aktuellen Zweig darstellen" + +#: git-gui.sh:1855 +msgid "Visualize All Branch History" +msgstr "Alle Zweige darstellen" + +#: git-gui.sh:1862 +#, tcl-format +msgid "Browse %s's Files" +msgstr "Zweig »%s« durchblättern" + +#: git-gui.sh:1864 +#, tcl-format +msgid "Visualize %s's History" +msgstr "Historie von »%s« darstellen" + +#: git-gui.sh:1869 lib/database.tcl:27 lib/database.tcl:67 +msgid "Database Statistics" +msgstr "Datenbankstatistik" + +#: git-gui.sh:1872 lib/database.tcl:34 +msgid "Compress Database" +msgstr "Datenbank komprimieren" + +#: git-gui.sh:1875 +msgid "Verify Database" +msgstr "Datenbank überprüfen" + +#: git-gui.sh:1882 git-gui.sh:1886 git-gui.sh:1890 lib/shortcut.tcl:7 +#: lib/shortcut.tcl:39 lib/shortcut.tcl:71 +msgid "Create Desktop Icon" +msgstr "Desktop-Icon erstellen" + +#: git-gui.sh:1895 lib/choose_repository.tcl:176 lib/choose_repository.tcl:184 +msgid "Quit" +msgstr "Beenden" + +#: git-gui.sh:1902 +msgid "Undo" +msgstr "Rückgängig" + +#: git-gui.sh:1905 +msgid "Redo" +msgstr "Wiederholen" + +#: git-gui.sh:1909 git-gui.sh:2403 +msgid "Cut" +msgstr "Ausschneiden" + +#: git-gui.sh:1912 git-gui.sh:2406 git-gui.sh:2477 git-gui.sh:2549 +#: lib/console.tcl:67 +msgid "Copy" +msgstr "Kopieren" + +#: git-gui.sh:1915 git-gui.sh:2409 +msgid "Paste" +msgstr "Einfügen" + +#: git-gui.sh:1918 git-gui.sh:2412 lib/branch_delete.tcl:26 +#: lib/remote_branch_delete.tcl:38 +msgid "Delete" +msgstr "Löschen" + +#: git-gui.sh:1922 git-gui.sh:2416 git-gui.sh:2553 lib/console.tcl:69 +msgid "Select All" +msgstr "Alle auswählen" + +#: git-gui.sh:1931 +msgid "Create..." +msgstr "Erstellen..." + +#: git-gui.sh:1937 +msgid "Checkout..." +msgstr "Umstellen..." + +#: git-gui.sh:1943 +msgid "Rename..." +msgstr "Umbenennen..." + +#: git-gui.sh:1948 git-gui.sh:2048 +msgid "Delete..." +msgstr "Löschen..." + +#: git-gui.sh:1953 +msgid "Reset..." +msgstr "Zurücksetzen..." + +#: git-gui.sh:1965 git-gui.sh:2350 +msgid "New Commit" +msgstr "Neue Version" + +#: git-gui.sh:1973 git-gui.sh:2357 +msgid "Amend Last Commit" +msgstr "Letzte Version nachbessern" + +#: git-gui.sh:1982 git-gui.sh:2317 lib/remote_branch_delete.tcl:99 +msgid "Rescan" +msgstr "Neu laden" + +#: git-gui.sh:1988 +msgid "Stage To Commit" +msgstr "Zum Eintragen bereitstellen" + +#: git-gui.sh:1994 +msgid "Stage Changed Files To Commit" +msgstr "Geänderte Dateien zum Eintragen bereitstellen" + +#: git-gui.sh:2000 +msgid "Unstage From Commit" +msgstr "Aus der Bereitstellung herausnehmen" + +#: git-gui.sh:2005 lib/index.tcl:393 +msgid "Revert Changes" +msgstr "Änderungen revidieren" + +#: git-gui.sh:2012 git-gui.sh:2329 git-gui.sh:2427 +msgid "Sign Off" +msgstr "Abzeichnen" + +#: git-gui.sh:2016 git-gui.sh:2333 +msgid "Commit@@verb" +msgstr "Eintragen" + +#: git-gui.sh:2027 +msgid "Local Merge..." +msgstr "Lokales Zusammenführen..." + +#: git-gui.sh:2032 +msgid "Abort Merge..." +msgstr "Zusammenführen abbrechen..." + +#: git-gui.sh:2044 +msgid "Push..." +msgstr "Versenden..." + +#: git-gui.sh:2055 lib/choose_repository.tcl:40 +msgid "Apple" +msgstr "Apple" + +#: git-gui.sh:2058 git-gui.sh:2080 lib/about.tcl:13 +#: lib/choose_repository.tcl:43 lib/choose_repository.tcl:49 +#, tcl-format +msgid "About %s" +msgstr "Ãœber %s" + +#: git-gui.sh:2062 +msgid "Preferences..." +msgstr "Einstellungen..." + +#: git-gui.sh:2070 git-gui.sh:2595 +msgid "Options..." +msgstr "Optionen..." + +#: git-gui.sh:2076 lib/choose_repository.tcl:46 +msgid "Help" +msgstr "Hilfe" + +#: git-gui.sh:2117 +msgid "Online Documentation" +msgstr "Online-Dokumentation" + +#: git-gui.sh:2201 +#, tcl-format +msgid "fatal: cannot stat path %s: No such file or directory" +msgstr "Fehler: Verzeichnis »%s« kann nicht gelesen werden: Datei oder Verzeichnis nicht gefunden" + +#: git-gui.sh:2234 +msgid "Current Branch:" +msgstr "Aktueller Zweig:" + +#: git-gui.sh:2255 +msgid "Staged Changes (Will Commit)" +msgstr "Bereitgestellte Änderungen (zum Eintragen)" + +#: git-gui.sh:2274 +msgid "Unstaged Changes" +msgstr "Nicht bereitgestellte Änderungen" + +#: git-gui.sh:2323 +msgid "Stage Changed" +msgstr "Alles bereitstellen" + +#: git-gui.sh:2339 lib/transport.tcl:93 lib/transport.tcl:182 +msgid "Push" +msgstr "Versenden" + +#: git-gui.sh:2369 +msgid "Initial Commit Message:" +msgstr "Erste Versionsbeschreibung:" + +#: git-gui.sh:2370 +msgid "Amended Commit Message:" +msgstr "Nachgebesserte Versionsbeschreibung:" + +#: git-gui.sh:2371 +msgid "Amended Initial Commit Message:" +msgstr "Nachgebesserte erste Versionsbeschreibung:" + +#: git-gui.sh:2372 +msgid "Amended Merge Commit Message:" +msgstr "Nachgebesserte Zusammenführungs-Versionsbeschreibung:" + +#: git-gui.sh:2373 +msgid "Merge Commit Message:" +msgstr "Zusammenführungs-Versionsbeschreibung:" + +#: git-gui.sh:2374 +msgid "Commit Message:" +msgstr "Versionsbeschreibung:" + +#: git-gui.sh:2419 git-gui.sh:2557 lib/console.tcl:71 +msgid "Copy All" +msgstr "Alle kopieren" + +#: git-gui.sh:2443 lib/blame.tcl:104 +msgid "File:" +msgstr "Datei:" + +#: git-gui.sh:2545 +msgid "Refresh" +msgstr "Aktualisieren" + +#: git-gui.sh:2566 +msgid "Apply/Reverse Hunk" +msgstr "Änderung anwenden/umkehren" + +#: git-gui.sh:2572 +msgid "Decrease Font Size" +msgstr "Schriftgröße verkleinern" + +#: git-gui.sh:2576 +msgid "Increase Font Size" +msgstr "Schriftgröße vergrößern" + +#: git-gui.sh:2581 +msgid "Show Less Context" +msgstr "Weniger Kontext anzeigen" + +#: git-gui.sh:2588 +msgid "Show More Context" +msgstr "Mehr Kontext anzeigen" + +#: git-gui.sh:2602 +msgid "Unstage Hunk From Commit" +msgstr "Aus der Bereitstellung herausnehmen" + +#: git-gui.sh:2604 +msgid "Stage Hunk For Commit" +msgstr "In die Bereitstellung hinzufügen" + +#: git-gui.sh:2623 +msgid "Initializing..." +msgstr "Initialisieren..." + +#: git-gui.sh:2718 +#, tcl-format +msgid "" +"Possible environment issues exist.\n" +"\n" +"The following environment variables are probably\n" +"going to be ignored by any Git subprocess run\n" +"by %s:\n" +"\n" +msgstr "" +"Möglicherweise gibt es Probleme mit manchen Umgebungsvariablen.\n" +"\n" +"Die folgenden Umgebungsvariablen können vermutlich nicht \n" +"von %s an Git weitergegeben werden:\n" +"\n" + +#: git-gui.sh:2748 +msgid "" +"\n" +"This is due to a known issue with the\n" +"Tcl binary distributed by Cygwin." +msgstr "" +"\n" +"Dies ist ein bekanntes Problem der Tcl-Version, die\n" +"in Cygwin mitgeliefert wird." + +#: git-gui.sh:2753 +#, tcl-format +msgid "" +"\n" +"\n" +"A good replacement for %s\n" +"is placing values for the user.name and\n" +"user.email settings into your personal\n" +"~/.gitconfig file.\n" +msgstr "" +"\n" +"\n" +"Um den Namen »%s« zu ändern, sollten Sie die \n" +"gewünschten Werte für die Einstellung user.name und \n" +"user.email in Ihre Datei ~/.gitconfig einfügen.\n" + +#: lib/about.tcl:25 +msgid "git-gui - a graphical user interface for Git." +msgstr "git-gui - eine grafische Oberfläche für Git." + +#: lib/blame.tcl:77 +msgid "File Viewer" +msgstr "Datei-Browser" + +#: lib/blame.tcl:81 +msgid "Commit:" +msgstr "Version:" + +#: lib/blame.tcl:249 +msgid "Copy Commit" +msgstr "Version kopieren" + +#: lib/blame.tcl:369 +#, tcl-format +msgid "Reading %s..." +msgstr "%s lesen..." + +#: lib/blame.tcl:473 +msgid "Loading copy/move tracking annotations..." +msgstr "Annotierungen für Kopieren/Verschieben werden geladen..." + +#: lib/blame.tcl:493 +msgid "lines annotated" +msgstr "Zeilen annotiert" + +#: lib/blame.tcl:674 +msgid "Loading original location annotations..." +msgstr "Annotierungen für ursprünglichen Ort werden geladen..." + +#: lib/blame.tcl:677 +msgid "Annotation complete." +msgstr "Annotierung vollständig." + +#: lib/blame.tcl:731 +msgid "Loading annotation..." +msgstr "Annotierung laden..." + +#: lib/blame.tcl:787 +msgid "Author:" +msgstr "Autor:" + +#: lib/blame.tcl:791 +msgid "Committer:" +msgstr "Eintragender:" + +#: lib/blame.tcl:796 +msgid "Original File:" +msgstr "Ursprüngliche Datei:" + +#: lib/blame.tcl:910 +msgid "Originally By:" +msgstr "Ursprünglich von:" + +#: lib/blame.tcl:916 +msgid "In File:" +msgstr "In Datei:" + +#: lib/blame.tcl:921 +msgid "Copied Or Moved Here By:" +msgstr "Kopiert oder verschoben durch:" + +#: lib/branch_checkout.tcl:14 lib/branch_checkout.tcl:19 +msgid "Checkout Branch" +msgstr "Zweig umstellen" + +#: lib/branch_checkout.tcl:23 +msgid "Checkout" +msgstr "Umstellen" + +#: lib/branch_checkout.tcl:27 lib/branch_create.tcl:35 +#: lib/branch_delete.tcl:32 lib/branch_rename.tcl:30 lib/browser.tcl:281 +#: lib/checkout_op.tcl:522 lib/choose_font.tcl:43 lib/merge.tcl:172 +#: lib/option.tcl:90 lib/remote_branch_delete.tcl:42 lib/transport.tcl:97 +msgid "Cancel" +msgstr "Abbrechen" + +#: lib/branch_checkout.tcl:32 lib/browser.tcl:286 +msgid "Revision" +msgstr "Version" + +#: lib/branch_checkout.tcl:36 lib/branch_create.tcl:69 lib/option.tcl:202 +msgid "Options" +msgstr "Optionen" + +#: lib/branch_checkout.tcl:39 lib/branch_create.tcl:92 +msgid "Fetch Tracking Branch" +msgstr "Ãœbernahmezweig anfordern" + +#: lib/branch_checkout.tcl:44 +msgid "Detach From Local Branch" +msgstr "Verbindung zu lokalem Zweig lösen" + +#: lib/branch_create.tcl:22 +msgid "Create Branch" +msgstr "Zweig erstellen" + +#: lib/branch_create.tcl:27 +msgid "Create New Branch" +msgstr "Neuen Zweig erstellen" + +#: lib/branch_create.tcl:31 lib/choose_repository.tcl:375 +msgid "Create" +msgstr "Erstellen" + +#: lib/branch_create.tcl:40 +msgid "Branch Name" +msgstr "Zweigname" + +#: lib/branch_create.tcl:43 +msgid "Name:" +msgstr "Name:" + +#: lib/branch_create.tcl:58 +msgid "Match Tracking Branch Name" +msgstr "Passend zu Ãœbernahmezweig-Name" + +#: lib/branch_create.tcl:66 +msgid "Starting Revision" +msgstr "Anfangsversion" + +#: lib/branch_create.tcl:72 +msgid "Update Existing Branch:" +msgstr "Existierenden Zweig aktualisieren:" + +#: lib/branch_create.tcl:75 +msgid "No" +msgstr "Nein" + +#: lib/branch_create.tcl:80 +msgid "Fast Forward Only" +msgstr "Nur Schnellzusammenführung" + +#: lib/branch_create.tcl:85 lib/checkout_op.tcl:514 +msgid "Reset" +msgstr "Zurücksetzen" + +#: lib/branch_create.tcl:97 +msgid "Checkout After Creation" +msgstr "Arbeitskopie umstellen nach Erstellen" + +#: lib/branch_create.tcl:131 +msgid "Please select a tracking branch." +msgstr "Bitte wählen Sie einen Ãœbernahmezweig." + +#: lib/branch_create.tcl:140 +#, tcl-format +msgid "Tracking branch %s is not a branch in the remote repository." +msgstr "Ãœbernahmezweig »%s« ist kein Zweig im anderen Projektarchiv." + +#: lib/branch_create.tcl:153 lib/branch_rename.tcl:86 +msgid "Please supply a branch name." +msgstr "Bitte geben Sie einen Zweignamen an." + +#: lib/branch_create.tcl:164 lib/branch_rename.tcl:106 +#, tcl-format +msgid "'%s' is not an acceptable branch name." +msgstr "»%s« ist kein zulässiger Zweigname." + +#: lib/branch_delete.tcl:15 +msgid "Delete Branch" +msgstr "Zweig löschen" + +#: lib/branch_delete.tcl:20 +msgid "Delete Local Branch" +msgstr "Lokalen Zweig löschen" + +#: lib/branch_delete.tcl:37 +msgid "Local Branches" +msgstr "Lokale Zweige" + +#: lib/branch_delete.tcl:52 +msgid "Delete Only If Merged Into" +msgstr "Nur löschen, wenn darin zusammengeführt" + +#: lib/branch_delete.tcl:54 +msgid "Always (Do not perform merge test.)" +msgstr "Immer (ohne Zusammenführungstest)" + +#: lib/branch_delete.tcl:103 +#, tcl-format +msgid "The following branches are not completely merged into %s:" +msgstr "Folgende Zweige sind noch nicht mit »%s« zusammengeführt:" + +#: lib/branch_delete.tcl:115 +msgid "" +"Recovering deleted branches is difficult. \n" +"\n" +" Delete the selected branches?" +msgstr "" +"Gelöschte Zweige können nur mit größerem Aufwand wiederhergestellt werden.\n" +"\n" +"Gewählte Zweige jetzt löschen?" + +#: lib/branch_delete.tcl:141 +#, tcl-format +msgid "" +"Failed to delete branches:\n" +"%s" +msgstr "" +"Fehler beim Löschen der Zweige:\n" +"%s" + +#: lib/branch_rename.tcl:14 lib/branch_rename.tcl:22 +msgid "Rename Branch" +msgstr "Zweig umbenennen" + +#: lib/branch_rename.tcl:26 +msgid "Rename" +msgstr "Umbenennen" + +#: lib/branch_rename.tcl:36 +msgid "Branch:" +msgstr "Zweig:" + +#: lib/branch_rename.tcl:39 +msgid "New Name:" +msgstr "Neuer Name:" + +#: lib/branch_rename.tcl:75 +msgid "Please select a branch to rename." +msgstr "Bitte wählen Sie einen Zweig zum umbenennen." + +#: lib/branch_rename.tcl:96 lib/checkout_op.tcl:179 +#, tcl-format +msgid "Branch '%s' already exists." +msgstr "Zweig »%s« existiert bereits." + +#: lib/branch_rename.tcl:117 +#, tcl-format +msgid "Failed to rename '%s'." +msgstr "Fehler beim Umbenennen von »%s«." + +#: lib/browser.tcl:17 +msgid "Starting..." +msgstr "Starten..." + +#: lib/browser.tcl:26 +msgid "File Browser" +msgstr "Datei-Browser" + +#: lib/browser.tcl:125 lib/browser.tcl:142 +#, tcl-format +msgid "Loading %s..." +msgstr "%s laden..." + +#: lib/browser.tcl:186 +msgid "[Up To Parent]" +msgstr "[Nach oben]" + +#: lib/browser.tcl:266 lib/browser.tcl:272 +msgid "Browse Branch Files" +msgstr "Dateien des Zweigs durchblättern" + +#: lib/browser.tcl:277 lib/choose_repository.tcl:391 +#: lib/choose_repository.tcl:482 lib/choose_repository.tcl:492 +#: lib/choose_repository.tcl:989 +msgid "Browse" +msgstr "Blättern" + +#: lib/checkout_op.tcl:79 +#, tcl-format +msgid "Fetching %s from %s" +msgstr "Änderungen »%s« von »%s« anfordern" + +#: lib/checkout_op.tcl:127 +#, tcl-format +msgid "fatal: Cannot resolve %s" +msgstr "Fehler: »%s« kann nicht als Zweig oder Version erkannt werden" + +#: lib/checkout_op.tcl:140 lib/console.tcl:79 lib/database.tcl:31 +msgid "Close" +msgstr "Schließen" + +#: lib/checkout_op.tcl:169 +#, tcl-format +msgid "Branch '%s' does not exist." +msgstr "Zweig »%s« existiert nicht." + +#: lib/checkout_op.tcl:206 +#, tcl-format +msgid "" +"Branch '%s' already exists.\n" +"\n" +"It cannot fast-forward to %s.\n" +"A merge is required." +msgstr "" +"Zweig »%s« existiert bereits.\n" +"\n" +"Zweig kann nicht mit »%s« schnellzusammengeführt werden. Reguläres " +"Zusammenführen ist notwendig." + +#: lib/checkout_op.tcl:220 +#, tcl-format +msgid "Merge strategy '%s' not supported." +msgstr "Zusammenführungsmethode »%s« nicht unterstützt." + +#: lib/checkout_op.tcl:239 +#, tcl-format +msgid "Failed to update '%s'." +msgstr "Aktualisieren von »%s« fehlgeschlagen." + +#: lib/checkout_op.tcl:251 +msgid "Staging area (index) is already locked." +msgstr "Bereitstellung (»index«) ist zur Bearbeitung gesperrt (»locked«)." + +#: lib/checkout_op.tcl:266 +msgid "" +"Last scanned state does not match repository state.\n" +"\n" +"Another Git program has modified this repository since the last scan. A " +"rescan must be performed before the current branch can be changed.\n" +"\n" +"The rescan will be automatically started now.\n" +msgstr "" +"Der letzte geladene Status stimmt nicht mehr mit dem Projektarchiv überein.\n" +"\n" +"Ein anderes Git-Programm hat das Projektarchiv seit dem letzten Laden " +"geändert. Vor dem Wechseln des lokalen Zweigs muss neu geladen werden.\n" +"\n" +"Es wird gleich neu geladen.\n" + +#: lib/checkout_op.tcl:322 +#, tcl-format +msgid "Updating working directory to '%s'..." +msgstr "Arbeitskopie umstellen auf »%s«..." + +#: lib/checkout_op.tcl:353 +#, tcl-format +msgid "Aborted checkout of '%s' (file level merging is required)." +msgstr "" +"Zweig umstellen von »%s« abgebrochen (Zusammenführen der Dateien ist " +"notwendig)." + +#: lib/checkout_op.tcl:354 +msgid "File level merge required." +msgstr "Zusammenführen der Dateien ist notwendig." + +#: lib/checkout_op.tcl:358 +#, tcl-format +msgid "Staying on branch '%s'." +msgstr "Es wird auf Zweig »%s« verblieben." + +#: lib/checkout_op.tcl:429 +msgid "" +"You are no longer on a local branch.\n" +"\n" +"If you wanted to be on a branch, create one now starting from 'This Detached " +"Checkout'." +msgstr "" +"Die Arbeitskopie ist nicht auf einem lokalen Zweig.\n" +"\n" +"Wenn Sie auf einem Zweig arbeiten möchten, erstellen Sie bitte jetzt einen " +"Zweig mit der Auswahl »Abgetrennte Arbeitskopie-Version«." + +#: lib/checkout_op.tcl:446 +#, tcl-format +msgid "Checked out '%s'." +msgstr "Umgestellt auf »%s«." + +#: lib/checkout_op.tcl:478 +#, tcl-format +msgid "Resetting '%s' to '%s' will lose the following commits:" +msgstr "Zurücksetzen von »%s« nach »%s« wird folgende Versionen verwerfen:" + +#: lib/checkout_op.tcl:500 +msgid "Recovering lost commits may not be easy." +msgstr "" +"Verworfene Versionen können nur mit größerem Aufwand wiederhergestellt " +"werden." + +#: lib/checkout_op.tcl:505 +#, tcl-format +msgid "Reset '%s'?" +msgstr "»%s« zurücksetzen?" + +#: lib/checkout_op.tcl:510 lib/merge.tcl:164 +msgid "Visualize" +msgstr "Darstellen" + +#: lib/checkout_op.tcl:578 +#, tcl-format +msgid "" +"Failed to set current branch.\n" +"\n" +"This working directory is only partially switched. We successfully updated " +"your files, but failed to update an internal Git file.\n" +"\n" +"This should not have occurred. %s will now close and give up." +msgstr "" +"Lokaler Zweig kann nicht gesetzt werden.\n" +"\n" +"Diese Arbeitskopie ist nur teilweise umgestellt. Die Dateien sind korrekt " +"aktualisiert, aber einige interne Git-Dateien konnten nicht geändert " +"werden.\n" +"\n" +"Dies ist ein interner Programmfehler von %s. Programm wird jetzt abgebrochen." + +#: lib/choose_font.tcl:39 +msgid "Select" +msgstr "Auswählen" + +#: lib/choose_font.tcl:53 +msgid "Font Family" +msgstr "Schriftfamilie" + +#: lib/choose_font.tcl:73 +msgid "Font Size" +msgstr "Schriftgröße" + +#: lib/choose_font.tcl:90 +msgid "Font Example" +msgstr "Schriftbeispiel" + +#: lib/choose_font.tcl:101 +msgid "" +"This is example text.\n" +"If you like this text, it can be your font." +msgstr "" +"Dies ist ein Beispieltext.\n" +"Wenn Ihnen dieser Text gefällt, sollten Sie diese Schriftart wählen." + +#: lib/choose_repository.tcl:27 +msgid "Git Gui" +msgstr "Git Gui" + +#: lib/choose_repository.tcl:80 lib/choose_repository.tcl:380 +msgid "Create New Repository" +msgstr "Neues Projektarchiv" + +#: lib/choose_repository.tcl:86 +msgid "New..." +msgstr "Neu..." + +#: lib/choose_repository.tcl:93 lib/choose_repository.tcl:468 +msgid "Clone Existing Repository" +msgstr "Projektarchiv kopieren" + +#: lib/choose_repository.tcl:99 +msgid "Clone..." +msgstr "Kopieren..." + +#: lib/choose_repository.tcl:106 lib/choose_repository.tcl:978 +msgid "Open Existing Repository" +msgstr "Projektarchiv öffnen" + +#: lib/choose_repository.tcl:112 +msgid "Open..." +msgstr "Öffnen..." + +#: lib/choose_repository.tcl:125 +msgid "Recent Repositories" +msgstr "Zuletzt benutzte Projektarchive" + +#: lib/choose_repository.tcl:131 +msgid "Open Recent Repository:" +msgstr "Zuletzt benutztes Projektarchiv öffnen:" + +#: lib/choose_repository.tcl:294 +#, tcl-format +msgid "Location %s already exists." +msgstr "Projektarchiv »%s« existiert bereits." + +#: lib/choose_repository.tcl:300 lib/choose_repository.tcl:307 +#: lib/choose_repository.tcl:314 +#, tcl-format +msgid "Failed to create repository %s:" +msgstr "Projektarchiv »%s« konnte nicht erstellt werden:" + +#: lib/choose_repository.tcl:385 lib/choose_repository.tcl:486 +msgid "Directory:" +msgstr "Verzeichnis:" + +#: lib/choose_repository.tcl:415 lib/choose_repository.tcl:544 +#: lib/choose_repository.tcl:1013 +msgid "Git Repository" +msgstr "Git Projektarchiv" + +#: lib/choose_repository.tcl:430 lib/choose_repository.tcl:437 +#, tcl-format +msgid "Directory %s already exists." +msgstr "Verzeichnis »%s« existiert bereits." + +#: lib/choose_repository.tcl:442 +#, tcl-format +msgid "File %s already exists." +msgstr "Datei »%s« existiert bereits." + +#: lib/choose_repository.tcl:463 +msgid "Clone" +msgstr "Kopieren" + +#: lib/choose_repository.tcl:476 +msgid "URL:" +msgstr "URL:" + +#: lib/choose_repository.tcl:496 +msgid "Clone Type:" +msgstr "Art der Kopie:" + +#: lib/choose_repository.tcl:502 +msgid "Standard (Fast, Semi-Redundant, Hardlinks)" +msgstr "Standard (schnell, teilweise redundant, Hardlinks)" + +#: lib/choose_repository.tcl:508 +msgid "Full Copy (Slower, Redundant Backup)" +msgstr "Alles kopieren (langsamer, volle Redundanz)" + +#: lib/choose_repository.tcl:514 +msgid "Shared (Fastest, Not Recommended, No Backup)" +msgstr "Verknüpft (schnell, nicht empfohlen, kein Backup)" + +#: lib/choose_repository.tcl:550 lib/choose_repository.tcl:597 +#: lib/choose_repository.tcl:738 lib/choose_repository.tcl:808 +#: lib/choose_repository.tcl:1019 lib/choose_repository.tcl:1027 +#, tcl-format +msgid "Not a Git repository: %s" +msgstr "Kein Git-Projektarchiv in »%s« gefunden." + +#: lib/choose_repository.tcl:586 +msgid "Standard only available for local repository." +msgstr "Standard ist nur für lokale Projektarchive verfügbar." + +#: lib/choose_repository.tcl:590 +msgid "Shared only available for local repository." +msgstr "Verknüpft ist nur für lokale Projektarchive verfügbar." + +#: lib/choose_repository.tcl:617 +msgid "Failed to configure origin" +msgstr "Der Ursprungsort konnte nicht eingerichtet werden" + +#: lib/choose_repository.tcl:629 +msgid "Counting objects" +msgstr "Objekte werden gezählt" + +#: lib/choose_repository.tcl:630 +msgid "buckets" +msgstr "Buckets" + +#: lib/choose_repository.tcl:654 +#, tcl-format +msgid "Unable to copy objects/info/alternates: %s" +msgstr "Kopien von Objekten/Info/Alternates konnten nicht erstellt werden: %s" + +#: lib/choose_repository.tcl:690 +#, tcl-format +msgid "Nothing to clone from %s." +msgstr "Von »%s« konnte nichts kopiert werden." + +#: lib/choose_repository.tcl:692 lib/choose_repository.tcl:906 +#: lib/choose_repository.tcl:918 +msgid "The 'master' branch has not been initialized." +msgstr "Der »master«-Zweig wurde noch nicht initialisiert." + +#: lib/choose_repository.tcl:705 +msgid "Hardlinks are unavailable. Falling back to copying." +msgstr "Hardlinks nicht verfügbar. Stattdessen wird kopiert." + +#: lib/choose_repository.tcl:717 +#, tcl-format +msgid "Cloning from %s" +msgstr "Kopieren von »%s«" + +#: lib/choose_repository.tcl:748 +msgid "Copying objects" +msgstr "Objektdatenbank kopieren" + +#: lib/choose_repository.tcl:749 +msgid "KiB" +msgstr "KB" + +#: lib/choose_repository.tcl:773 +#, tcl-format +msgid "Unable to copy object: %s" +msgstr "Objekt kann nicht kopiert werden: %s" + +#: lib/choose_repository.tcl:783 +msgid "Linking objects" +msgstr "Objekte verlinken" + +#: lib/choose_repository.tcl:784 +msgid "objects" +msgstr "Objekte" + +#: lib/choose_repository.tcl:792 +#, tcl-format +msgid "Unable to hardlink object: %s" +msgstr "Für Objekt konnte kein Hardlink erstellt werden: %s" + +#: lib/choose_repository.tcl:847 +msgid "Cannot fetch branches and objects. See console output for details." +msgstr "Zweige und Objekte konnten nicht angefordert werden. Kontrollieren Sie die Ausgaben auf der Konsole für weitere Angaben." + +#: lib/choose_repository.tcl:858 +msgid "Cannot fetch tags. See console output for details." +msgstr "Markierungen konnten nicht angefordert werden. Kontrollieren Sie die Ausgaben auf der Konsole für weitere Angaben." + +#: lib/choose_repository.tcl:882 +msgid "Cannot determine HEAD. See console output for details." +msgstr "Die Zweigspitze (HEAD) konnte nicht gefunden werden. Kontrollieren Sie die Ausgaben auf der Konsole für weitere Angaben." + +#: lib/choose_repository.tcl:891 +#, tcl-format +msgid "Unable to cleanup %s" +msgstr "Verzeichnis »%s« kann nicht aufgeräumt werden." + +#: lib/choose_repository.tcl:897 +msgid "Clone failed." +msgstr "Kopieren fehlgeschlagen." + +#: lib/choose_repository.tcl:904 +msgid "No default branch obtained." +msgstr "Kein voreingestellter Zweig gefunden." + +#: lib/choose_repository.tcl:915 +#, tcl-format +msgid "Cannot resolve %s as a commit." +msgstr "»%s« wurde nicht als Version gefunden." + +#: lib/choose_repository.tcl:927 +msgid "Creating working directory" +msgstr "Arbeitskopie erstellen" + +#: lib/choose_repository.tcl:928 lib/index.tcl:65 lib/index.tcl:127 +#: lib/index.tcl:193 +msgid "files" +msgstr "Dateien" + +#: lib/choose_repository.tcl:957 +msgid "Initial file checkout failed." +msgstr "Erstellen der Arbeitskopie fehlgeschlagen." + +#: lib/choose_repository.tcl:973 +msgid "Open" +msgstr "Öffnen" + +#: lib/choose_repository.tcl:983 +msgid "Repository:" +msgstr "Projektarchiv:" + +#: lib/choose_repository.tcl:1033 +#, tcl-format +msgid "Failed to open repository %s:" +msgstr "Projektarchiv »%s« konnte nicht geöffnet werden." + +#: lib/choose_rev.tcl:53 +msgid "This Detached Checkout" +msgstr "Abgetrennte Arbeitskopie-Version" + +#: lib/choose_rev.tcl:60 +msgid "Revision Expression:" +msgstr "Version Regexp-Ausdruck:" + +#: lib/choose_rev.tcl:74 +msgid "Local Branch" +msgstr "Lokaler Zweig" + +#: lib/choose_rev.tcl:79 +msgid "Tracking Branch" +msgstr "Ãœbernahmezweig" + +#: lib/choose_rev.tcl:84 lib/choose_rev.tcl:537 +msgid "Tag" +msgstr "Markierung" + +#: lib/choose_rev.tcl:317 +#, tcl-format +msgid "Invalid revision: %s" +msgstr "Ungültige Version: %s" + +#: lib/choose_rev.tcl:338 +msgid "No revision selected." +msgstr "Keine Version ausgewählt." + +#: lib/choose_rev.tcl:346 +msgid "Revision expression is empty." +msgstr "Versions-Ausdruck ist leer." + +#: lib/choose_rev.tcl:530 +msgid "Updated" +msgstr "Aktualisiert" + +#: lib/choose_rev.tcl:558 +msgid "URL" +msgstr "URL" + +#: lib/commit.tcl:9 +msgid "" +"There is nothing to amend.\n" +"\n" +"You are about to create the initial commit. There is no commit before this " +"to amend.\n" +msgstr "" +"Keine Version zur Nachbesserung vorhanden.\n" +"\n" +"Sie sind dabei, die erste Version zu übertragen. Es gibt keine existierende " +"Version, die Sie nachbessern könnten.\n" + +#: lib/commit.tcl:18 +msgid "" +"Cannot amend while merging.\n" +"\n" +"You are currently in the middle of a merge that has not been fully " +"completed. You cannot amend the prior commit unless you first abort the " +"current merge activity.\n" +msgstr "" +"Nachbesserung währen Zusammenführung nicht möglich.\n" +"\n" +"Sie haben das Zusammenführen von Versionen angefangen, aber noch nicht " +"beendet. Sie können keine vorige Ãœbertragung nachbessern, solange eine " +"unfertige Zusammenführung existiert. Dazu müssen Sie die Zusammenführung " +"beenden oder abbrechen.\n" + +#: lib/commit.tcl:49 +msgid "Error loading commit data for amend:" +msgstr "Fehler beim Laden der Versionsdaten für Nachbessern:" + +#: lib/commit.tcl:76 +msgid "Unable to obtain your identity:" +msgstr "Benutzername konnte nicht bestimmt werden:" + +#: lib/commit.tcl:81 +msgid "Invalid GIT_COMMITTER_IDENT:" +msgstr "Ungültiger Wert von GIT_COMMITTER_INDENT:" + +#: lib/commit.tcl:133 +msgid "" +"Last scanned state does not match repository state.\n" +"\n" +"Another Git program has modified this repository since the last scan. A " +"rescan must be performed before another commit can be created.\n" +"\n" +"The rescan will be automatically started now.\n" +msgstr "" +"Der letzte geladene Status stimmt nicht mehr mit dem Projektarchiv überein.\n" +"\n" +"Ein anderes Git-Programm hat das Projektarchiv seit dem letzten Laden " +"geändert. Vor dem Eintragen einer neuen Version muss neu geladen werden.\n" +"\n" +"Es wird gleich neu geladen.\n" + +#: lib/commit.tcl:154 +#, tcl-format +msgid "" +"Unmerged files cannot be committed.\n" +"\n" +"File %s has merge conflicts. You must resolve them and stage the file " +"before committing.\n" +msgstr "" +"Nicht zusammengeführte Dateien können nicht eingetragen werden.\n" +"\n" +"Die Datei »%s« hat noch nicht aufgelöste Zusammenführungs-Konflikte. Sie " +"müssen diese Konflikte auflösen, bevor Sie eintragen können.\n" + +#: lib/commit.tcl:162 +#, tcl-format +msgid "" +"Unknown file state %s detected.\n" +"\n" +"File %s cannot be committed by this program.\n" +msgstr "" +"Unbekannter Dateizustand »%s«.\n" +"\n" +"Datei »%s« kann nicht eingetragen werden.\n" + +#: lib/commit.tcl:170 +msgid "" +"No changes to commit.\n" +"\n" +"You must stage at least 1 file before you can commit.\n" +msgstr "" +"Keine Änderungen vorhanden, die eingetragen werden könnten.\n" +"\n" +"Sie müssen mindestens eine Datei bereitstellen, bevor Sie eintragen können.\n" + +#: lib/commit.tcl:183 +msgid "" +"Please supply a commit message.\n" +"\n" +"A good commit message has the following format:\n" +"\n" +"- First line: Describe in one sentence what you did.\n" +"- Second line: Blank\n" +"- Remaining lines: Describe why this change is good.\n" +msgstr "" +"Bitte geben Sie eine Versionsbeschreibung ein.\n" +"\n" +"Eine gute Versionsbeschreibung enthält folgende Abschnitte:\n" +"\n" +"- Erste Zeile: Eine Zusammenfassung, was man gemacht hat.\n" +"\n" +"- Zweite Zeile: Leerzeile\n" +"\n" +"- Rest: Eine ausführliche Beschreibung, warum diese Änderung hilfreich ist.\n" + +#: lib/commit.tcl:257 +msgid "write-tree failed:" +msgstr "write-tree fehlgeschlagen:" + +#: lib/commit.tcl:275 +#, tcl-format +msgid "Commit %s appears to be corrupt" +msgstr "Version »%s« scheint beschädigt zu sein" + +#: lib/commit.tcl:279 +msgid "" +"No changes to commit.\n" +"\n" +"No files were modified by this commit and it was not a merge commit.\n" +"\n" +"A rescan will be automatically started now.\n" +msgstr "" +"Keine Änderungen einzutragen.\n" +"\n" +"Es gibt keine geänderte Datei bei dieser Version und es wurde auch nichts " +"zusammengeführt.\n" +"\n" +"Das Arbeitsverzeichnis wird daher jetzt neu geladen.\n" + +#: lib/commit.tcl:286 +msgid "No changes to commit." +msgstr "Keine Änderungen, die eingetragen werden können." + +#: lib/commit.tcl:303 +#, tcl-format +msgid "warning: Tcl does not support encoding '%s'." +msgstr "Warning: Tcl/Tk unterstützt die Zeichencodierung »%s« nicht." + +#: lib/commit.tcl:317 +msgid "commit-tree failed:" +msgstr "commit-tree fehlgeschlagen:" + +#: lib/commit.tcl:339 +msgid "update-ref failed:" +msgstr "update-ref fehlgeschlagen:" + +#: lib/commit.tcl:430 +#, tcl-format +msgid "Created commit %s: %s" +msgstr "Version %s übertragen: %s" + +#: lib/console.tcl:57 +msgid "Working... please wait..." +msgstr "Verarbeitung. Bitte warten..." + +#: lib/console.tcl:183 +msgid "Success" +msgstr "Erfolgreich" + +#: lib/console.tcl:196 +msgid "Error: Command Failed" +msgstr "Fehler: Kommando fehlgeschlagen" + +#: lib/database.tcl:43 +msgid "Number of loose objects" +msgstr "Anzahl unverknüpfter Objekte" + +#: lib/database.tcl:44 +msgid "Disk space used by loose objects" +msgstr "Festplattenplatz von unverknüpften Objekten" + +#: lib/database.tcl:45 +msgid "Number of packed objects" +msgstr "Anzahl komprimierter Objekte" + +#: lib/database.tcl:46 +msgid "Number of packs" +msgstr "Anzahl Komprimierungseinheiten" + +#: lib/database.tcl:47 +msgid "Disk space used by packed objects" +msgstr "Festplattenplatz von komprimierten Objekten" + +#: lib/database.tcl:48 +msgid "Packed objects waiting for pruning" +msgstr "Komprimierte Objekte, die zum Entfernen vorgesehen sind" + +#: lib/database.tcl:49 +msgid "Garbage files" +msgstr "Dateien im Mülleimer" + +#: lib/database.tcl:72 +msgid "Compressing the object database" +msgstr "Objektdatenbank komprimieren" + +#: lib/database.tcl:83 +msgid "Verifying the object database with fsck-objects" +msgstr "Die Objektdatenbank durch »fsck-objects« überprüfen lassen" + +#: lib/database.tcl:108 +#, tcl-format +msgid "" +"This repository currently has approximately %i loose objects.\n" +"\n" +"To maintain optimal performance it is strongly recommended that you compress " +"the database when more than %i loose objects exist.\n" +"\n" +"Compress the database now?" +msgstr "" +"Dieses Projektarchiv enthält ungefähr %i nicht verknüpfte Objekte.\n" +"\n" +"Für eine optimale Performance wird empfohlen, die Datenbank des " +"Projektarchivs zu komprimieren, sobald mehr als %i nicht verknüpfte Objekte " +"vorliegen.\n" +"\n" +"Soll die Datenbank jetzt komprimiert werden?" + +#: lib/date.tcl:25 +#, tcl-format +msgid "Invalid date from Git: %s" +msgstr "Ungültiges Datum von Git: %s" + +#: lib/diff.tcl:42 +#, tcl-format +msgid "" +"No differences detected.\n" +"\n" +"%s has no changes.\n" +"\n" +"The modification date of this file was updated by another application, but " +"the content within the file was not changed.\n" +"\n" +"A rescan will be automatically started to find other files which may have " +"the same state." +msgstr "" +"Keine Änderungen feststellbar.\n" +"\n" +"»%s« enthält keine Änderungen. Zwar wurde das Änderungsdatum dieser Datei " +"von einem anderen Programm modifiziert, aber der Inhalt der Datei ist " +"unverändert.\n" +"\n" +"Das Arbeitsverzeichnis wird jetzt neu geladen, um diese Änderung bei allen " +"Dateien zu prüfen." + +#: lib/diff.tcl:81 +#, tcl-format +msgid "Loading diff of %s..." +msgstr "Vergleich von »%s« laden..." + +#: lib/diff.tcl:114 lib/diff.tcl:184 +#, tcl-format +msgid "Unable to display %s" +msgstr "Datei »%s« kann nicht angezeigt werden" + +#: lib/diff.tcl:115 +msgid "Error loading file:" +msgstr "Fehler beim Laden der Datei:" + +#: lib/diff.tcl:122 +msgid "Git Repository (subproject)" +msgstr "Git-Projektarchiv (Unterprojekt)" + +#: lib/diff.tcl:134 +msgid "* Binary file (not showing content)." +msgstr "* Binärdatei (Inhalt wird nicht angezeigt)" + +#: lib/diff.tcl:185 +msgid "Error loading diff:" +msgstr "Fehler beim Laden des Vergleichs:" + +#: lib/diff.tcl:302 +msgid "Failed to unstage selected hunk." +msgstr "Fehler beim Herausnehmen der gewählten Dateien aus der Bereitstellung." + +#: lib/diff.tcl:309 +msgid "Failed to stage selected hunk." +msgstr "Fehler beim Bereitstellen der gewählten Dateien." + +#: lib/error.tcl:12 lib/error.tcl:102 +msgid "error" +msgstr "Fehler" + +#: lib/error.tcl:28 +msgid "warning" +msgstr "Warnung" + +#: lib/error.tcl:81 +msgid "You must correct the above errors before committing." +msgstr "" +"Sie müssen die obigen Fehler zuerst beheben, bevor Sie eintragen können." + +#: lib/index.tcl:6 +msgid "Unable to unlock the index." +msgstr "Bereitstellung kann nicht wieder freigegeben werden." + +#: lib/index.tcl:15 +msgid "Index Error" +msgstr "Fehler in Bereitstellung" + +#: lib/index.tcl:21 +msgid "" +"Updating the Git index failed. A rescan will be automatically started to " +"resynchronize git-gui." +msgstr "Das Aktualisieren der Git-Bereitstellung ist fehlgeschlagen. Eine allgemeine Git-Aktualisierung wird jetzt gestartet, um git-gui wieder mit git zu synchronisieren." + +#: lib/index.tcl:27 +msgid "Continue" +msgstr "Fortsetzen" + +#: lib/index.tcl:31 +msgid "Unlock Index" +msgstr "Bereitstellung freigeben" + +#: lib/index.tcl:282 +#, tcl-format +msgid "Unstaging %s from commit" +msgstr "Datei »%s« aus der Bereitstellung herausnehmen" + +#: lib/index.tcl:326 +#, tcl-format +msgid "Adding %s" +msgstr "»%s« hinzufügen..." + +#: lib/index.tcl:381 +#, tcl-format +msgid "Revert changes in file %s?" +msgstr "Änderungen in Datei »%s« revidieren?" + +#: lib/index.tcl:383 +#, tcl-format +msgid "Revert changes in these %i files?" +msgstr "Änderungen in den gewählten %i Dateien revidieren?" + +#: lib/index.tcl:389 +msgid "Any unstaged changes will be permanently lost by the revert." +msgstr "" +"Alle nicht bereitgestellten Änderungen werden beim Revidieren verloren gehen." + +#: lib/index.tcl:392 +msgid "Do Nothing" +msgstr "Nichts tun" + +#: lib/merge.tcl:13 +msgid "" +"Cannot merge while amending.\n" +"\n" +"You must finish amending this commit before starting any type of merge.\n" +msgstr "" +"Zusammenführen kann nicht gleichzeitig mit Nachbessern durchgeführt werden.\n" +"\n" +"Sie müssen zuerst die Nachbesserungs-Version abschließen, bevor Sie " +"zusammenführen können.\n" + +#: lib/merge.tcl:27 +msgid "" +"Last scanned state does not match repository state.\n" +"\n" +"Another Git program has modified this repository since the last scan. A " +"rescan must be performed before a merge can be performed.\n" +"\n" +"The rescan will be automatically started now.\n" +msgstr "" +"Der letzte geladene Status stimmt nicht mehr mit dem Projektarchiv überein.\n" +"\n" +"Ein anderes Git-Programm hat das Projektarchiv seit dem letzten Laden " +"geändert. Vor einem Zusammenführen muss neu geladen werden.\n" +"\n" +"Es wird gleich neu geladen.\n" + +#: lib/merge.tcl:44 +#, tcl-format +msgid "" +"You are in the middle of a conflicted merge.\n" +"\n" +"File %s has merge conflicts.\n" +"\n" +"You must resolve them, stage the file, and commit to complete the current " +"merge. Only then can you begin another merge.\n" +msgstr "" +"Zusammenführung mit Konflikten.\n" +"\n" +"Die Datei »%s« enthält Konflikte beim Zusammenführen. Sie müssen diese " +"Konflikte per Hand auflösen. Anschließend müssen Sie die Datei wieder " +"bereitstellen und eintragen, um die Zusammenführung abzuschließen. Erst " +"danach kann eine neue Zusammenführung begonnen werden.\n" + +#: lib/merge.tcl:54 +#, tcl-format +msgid "" +"You are in the middle of a change.\n" +"\n" +"File %s is modified.\n" +"\n" +"You should complete the current commit before starting a merge. Doing so " +"will help you abort a failed merge, should the need arise.\n" +msgstr "" +"Es liegen Änderungen vor.\n" +"\n" +"Die Datei »%s« wurde geändert. Sie sollten zuerst die bereitgestellte " +"Version abschließen, bevor Sie eine Zusammenführung beginnen. Mit dieser " +"Reihenfolge können Sie mögliche Konflikte beim Zusammenführen wesentlich " +"einfacher beheben oder abbrechen.\n" + +#: lib/merge.tcl:106 +#, tcl-format +msgid "%s of %s" +msgstr "%s von %s" + +#: lib/merge.tcl:119 +#, tcl-format +msgid "Merging %s and %s" +msgstr "Zusammenführen von %s und %s" + +#: lib/merge.tcl:131 +msgid "Merge completed successfully." +msgstr "Zusammenführen erfolgreich abgeschlossen." + +#: lib/merge.tcl:133 +msgid "Merge failed. Conflict resolution is required." +msgstr "Zusammenführen fehlgeschlagen. Konfliktauflösung ist notwendig." + +#: lib/merge.tcl:158 +#, tcl-format +msgid "Merge Into %s" +msgstr "Zusammenführen in %s" + +#: lib/merge.tcl:177 +msgid "Revision To Merge" +msgstr "Zusammenzuführende Version" + +#: lib/merge.tcl:212 +msgid "" +"Cannot abort while amending.\n" +"\n" +"You must finish amending this commit.\n" +msgstr "" +"Abbruch der Nachbesserung ist nicht möglich.\n" +"\n" +"Sie müssen die Nachbesserung der Version abschließen.\n" + +#: lib/merge.tcl:222 +msgid "" +"Abort merge?\n" +"\n" +"Aborting the current merge will cause *ALL* uncommitted changes to be lost.\n" +"\n" +"Continue with aborting the current merge?" +msgstr "" +"Zusammenführen abbrechen?\n" +"\n" +"Wenn Sie abbrechen, gehen alle noch nicht eingetragenen Änderungen " +"verloren.\n" +"\n" +"Zusammenführen jetzt abbrechen?" + +#: lib/merge.tcl:228 +msgid "" +"Reset changes?\n" +"\n" +"Resetting the changes will cause *ALL* uncommitted changes to be lost.\n" +"\n" +"Continue with resetting the current changes?" +msgstr "" +"Änderungen zurücksetzen?\n" +"\n" +"Wenn Sie zurücksetzen, gehen alle noch nicht eingetragenen Änderungen " +"verloren.\n" +"\n" +"Änderungen jetzt zurücksetzen?" + +#: lib/merge.tcl:239 +msgid "Aborting" +msgstr "Abbruch" + +#: lib/merge.tcl:266 +msgid "Abort failed." +msgstr "Abbruch fehlgeschlagen." + +#: lib/merge.tcl:268 +msgid "Abort completed. Ready." +msgstr "Abbruch durchgeführt. Bereit." + +#: lib/option.tcl:82 +msgid "Restore Defaults" +msgstr "Voreinstellungen wiederherstellen" + +#: lib/option.tcl:86 +msgid "Save" +msgstr "Speichern" + +#: lib/option.tcl:96 +#, tcl-format +msgid "%s Repository" +msgstr "Projektarchiv %s" + +#: lib/option.tcl:97 +msgid "Global (All Repositories)" +msgstr "Global (Alle Projektarchive)" + +#: lib/option.tcl:103 +msgid "User Name" +msgstr "Benutzername" + +#: lib/option.tcl:104 +msgid "Email Address" +msgstr "E-Mail-Adresse" + +#: lib/option.tcl:106 +msgid "Summarize Merge Commits" +msgstr "Zusammenführungs-Versionen zusammenfassen" + +#: lib/option.tcl:107 +msgid "Merge Verbosity" +msgstr "Ausführlichkeit der Zusammenführen-Meldungen" + +#: lib/option.tcl:108 +msgid "Show Diffstat After Merge" +msgstr "Vergleichsstatistik nach Zusammenführen anzeigen" + +#: lib/option.tcl:110 +msgid "Trust File Modification Timestamps" +msgstr "Auf Dateiänderungsdatum verlassen" + +#: lib/option.tcl:111 +msgid "Prune Tracking Branches During Fetch" +msgstr "Ãœbernahmezweige entfernen während Anforderung" + +#: lib/option.tcl:112 +msgid "Match Tracking Branches" +msgstr "Passend zu Ãœbernahmezweig" + +#: lib/option.tcl:113 +msgid "Number of Diff Context Lines" +msgstr "Anzahl der Kontextzeilen beim Vergleich" + +#: lib/option.tcl:114 +msgid "New Branch Name Template" +msgstr "Namensvorschlag für neue Zweige" + +#: lib/option.tcl:176 +msgid "Change Font" +msgstr "Schriftart ändern" + +#: lib/option.tcl:180 +#, tcl-format +msgid "Choose %s" +msgstr "%s wählen" + +#: lib/option.tcl:186 +msgid "pt." +msgstr "pt." + +#: lib/option.tcl:200 +msgid "Preferences" +msgstr "Einstellungen" + +#: lib/option.tcl:235 +msgid "Failed to completely save options:" +msgstr "Optionen konnten nicht gespeichert werden:" + +#: lib/remote_branch_delete.tcl:29 lib/remote_branch_delete.tcl:34 +msgid "Delete Remote Branch" +msgstr "Zweig im anderen Projektarchiv löschen" + +#: lib/remote_branch_delete.tcl:47 +msgid "From Repository" +msgstr "Von Projektarchiv" + +#: lib/remote_branch_delete.tcl:50 lib/transport.tcl:123 +msgid "Remote:" +msgstr "Anderes Archiv:" + +#: lib/remote_branch_delete.tcl:66 lib/transport.tcl:138 +msgid "Arbitrary URL:" +msgstr "Kommunikation mit URL:" + +#: lib/remote_branch_delete.tcl:84 +msgid "Branches" +msgstr "Zweige" + +#: lib/remote_branch_delete.tcl:109 +msgid "Delete Only If" +msgstr "Löschen, falls" + +#: lib/remote_branch_delete.tcl:111 +msgid "Merged Into:" +msgstr "Zusammenführen mit:" + +#: lib/remote_branch_delete.tcl:119 +msgid "Always (Do not perform merge checks)" +msgstr "Immer (Keine Zusammenführungsprüfung)" + +#: lib/remote_branch_delete.tcl:152 +msgid "A branch is required for 'Merged Into'." +msgstr "Für »Zusammenführen mit« muss ein Zweig angegeben werden." + +#: lib/remote_branch_delete.tcl:184 +#, tcl-format +msgid "" +"The following branches are not completely merged into %s:\n" +"\n" +" - %s" +msgstr "" +"Folgende Zweige sind noch nicht mit »%s« zusammengeführt:\n" +"\n" +" - %s" + +#: lib/remote_branch_delete.tcl:189 +#, tcl-format +msgid "" +"One or more of the merge tests failed because you have not fetched the " +"necessary commits. Try fetching from %s first." +msgstr "" +"Ein oder mehrere Zusammenführungen sind fehlgeschlagen, da Sie nicht die " +"notwendigen Versionen vorher angefordert haben. Sie sollten versuchen, " +"zuerst von »%s« anzufordern." + +#: lib/remote_branch_delete.tcl:207 +msgid "Please select one or more branches to delete." +msgstr "Bitte wählen Sie mindestens einen Zweig, der gelöscht werden soll." + +#: lib/remote_branch_delete.tcl:216 +msgid "" +"Recovering deleted branches is difficult.\n" +"\n" +"Delete the selected branches?" +msgstr "" +"Das Wiederherstellen von gelöschten Zweigen ist nur mit größerem Aufwand " +"möglich.\n" +"\n" +"Sollen die ausgewählten Zweige gelöscht werden?" + +#: lib/remote_branch_delete.tcl:226 +#, tcl-format +msgid "Deleting branches from %s" +msgstr "Zweige auf »%s« werden gelöscht" + +#: lib/remote_branch_delete.tcl:286 +msgid "No repository selected." +msgstr "Kein Projektarchiv ausgewählt." + +#: lib/remote_branch_delete.tcl:291 +#, tcl-format +msgid "Scanning %s..." +msgstr "»%s« laden..." + +#: lib/remote.tcl:165 +msgid "Prune from" +msgstr "Entfernen von" + +#: lib/remote.tcl:170 +msgid "Fetch from" +msgstr "Anfordern von" + +#: lib/remote.tcl:213 +msgid "Push to" +msgstr "Versenden nach" + +#: lib/shortcut.tcl:20 lib/shortcut.tcl:61 +msgid "Cannot write shortcut:" +msgstr "Fehler beim Schreiben der Verknüpfung:" + +#: lib/shortcut.tcl:136 +msgid "Cannot write icon:" +msgstr "Fehler beim Erstellen des Icons:" + +#: lib/status_bar.tcl:83 +#, tcl-format +msgid "%s ... %*i of %*i %s (%3i%%)" +msgstr "%s ... %*i von %*i %s (%3i%%)" + +#: lib/transport.tcl:6 +#, tcl-format +msgid "fetch %s" +msgstr "»%s« anfordern" + +#: lib/transport.tcl:7 +#, tcl-format +msgid "Fetching new changes from %s" +msgstr "Neue Änderungen von »%s« holen" + +#: lib/transport.tcl:18 +#, tcl-format +msgid "remote prune %s" +msgstr "Entfernen von »%s« im anderen Archiv" + +#: lib/transport.tcl:19 +#, tcl-format +msgid "Pruning tracking branches deleted from %s" +msgstr "Ãœbernahmezweige entfernen, die in »%s« gelöscht wurden" + +#: lib/transport.tcl:25 lib/transport.tcl:71 +#, tcl-format +msgid "push %s" +msgstr "»%s« versenden..." + +#: lib/transport.tcl:26 +#, tcl-format +msgid "Pushing changes to %s" +msgstr "Änderungen nach »%s« versenden" + +#: lib/transport.tcl:72 +#, tcl-format +msgid "Pushing %s %s to %s" +msgstr "%s %s nach %s versenden" + +#: lib/transport.tcl:89 +msgid "Push Branches" +msgstr "Zweige versenden" + +#: lib/transport.tcl:103 +msgid "Source Branches" +msgstr "Herkunftszweige" + +#: lib/transport.tcl:120 +msgid "Destination Repository" +msgstr "Ziel-Projektarchiv" + +#: lib/transport.tcl:158 +msgid "Transfer Options" +msgstr "Netzwerk-Einstellungen" + +#: lib/transport.tcl:160 +msgid "Force overwrite existing branch (may discard changes)" +msgstr "" +"Ãœberschreiben von existierenden Zweigen erzwingen (könnte Änderungen löschen)" + +#: lib/transport.tcl:164 +msgid "Use thin pack (for slow network connections)" +msgstr "Kompaktes Datenformat benutzen (für langsame Netzverbindungen)" + +#: lib/transport.tcl:168 +msgid "Include tags" +msgstr "Mit Markierungen übertragen" diff --git a/git-gui/po/git-gui.pot b/git-gui/po/git-gui.pot new file mode 100644 index 0000000000..dfa48ae263 --- /dev/null +++ b/git-gui/po/git-gui.pot @@ -0,0 +1,1742 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2007-11-24 10:36+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" + +#: git-gui.sh:41 git-gui.sh:604 git-gui.sh:618 git-gui.sh:631 git-gui.sh:714 +#: git-gui.sh:733 +msgid "git-gui: fatal error" +msgstr "" + +#: git-gui.sh:565 +#, tcl-format +msgid "Invalid font specified in %s:" +msgstr "" + +#: git-gui.sh:590 +msgid "Main Font" +msgstr "" + +#: git-gui.sh:591 +msgid "Diff/Console Font" +msgstr "" + +#: git-gui.sh:605 +msgid "Cannot find git in PATH." +msgstr "" + +#: git-gui.sh:632 +msgid "Cannot parse Git version string:" +msgstr "" + +#: git-gui.sh:650 +#, tcl-format +msgid "" +"Git version cannot be determined.\n" +"\n" +"%s claims it is version '%s'.\n" +"\n" +"%s requires at least Git 1.5.0 or later.\n" +"\n" +"Assume '%s' is version 1.5.0?\n" +msgstr "" + +#: git-gui.sh:888 +msgid "Git directory not found:" +msgstr "" + +#: git-gui.sh:895 +msgid "Cannot move to top of working directory:" +msgstr "" + +#: git-gui.sh:902 +msgid "Cannot use funny .git directory:" +msgstr "" + +#: git-gui.sh:907 +msgid "No working directory" +msgstr "" + +#: git-gui.sh:1054 +msgid "Refreshing file status..." +msgstr "" + +#: git-gui.sh:1119 +msgid "Scanning for modified files ..." +msgstr "" + +#: git-gui.sh:1294 lib/browser.tcl:245 +msgid "Ready." +msgstr "" + +#: git-gui.sh:1560 +msgid "Unmodified" +msgstr "" + +#: git-gui.sh:1562 +msgid "Modified, not staged" +msgstr "" + +#: git-gui.sh:1563 git-gui.sh:1568 +msgid "Staged for commit" +msgstr "" + +#: git-gui.sh:1564 git-gui.sh:1569 +msgid "Portions staged for commit" +msgstr "" + +#: git-gui.sh:1565 git-gui.sh:1570 +msgid "Staged for commit, missing" +msgstr "" + +#: git-gui.sh:1567 +msgid "Untracked, not staged" +msgstr "" + +#: git-gui.sh:1572 +msgid "Missing" +msgstr "" + +#: git-gui.sh:1573 +msgid "Staged for removal" +msgstr "" + +#: git-gui.sh:1574 +msgid "Staged for removal, still present" +msgstr "" + +#: git-gui.sh:1576 git-gui.sh:1577 git-gui.sh:1578 git-gui.sh:1579 +msgid "Requires merge resolution" +msgstr "" + +#: git-gui.sh:1614 +msgid "Starting gitk... please wait..." +msgstr "" + +#: git-gui.sh:1623 +#, tcl-format +msgid "" +"Unable to start gitk:\n" +"\n" +"%s does not exist" +msgstr "" + +#: git-gui.sh:1823 lib/choose_repository.tcl:35 +msgid "Repository" +msgstr "" + +#: git-gui.sh:1824 +msgid "Edit" +msgstr "" + +#: git-gui.sh:1826 lib/choose_rev.tcl:560 +msgid "Branch" +msgstr "" + +#: git-gui.sh:1829 lib/choose_rev.tcl:547 +msgid "Commit@@noun" +msgstr "" + +#: git-gui.sh:1832 lib/merge.tcl:121 lib/merge.tcl:150 lib/merge.tcl:168 +msgid "Merge" +msgstr "" + +#: git-gui.sh:1833 lib/choose_rev.tcl:556 +msgid "Remote" +msgstr "" + +#: git-gui.sh:1842 +msgid "Browse Current Branch's Files" +msgstr "" + +#: git-gui.sh:1846 +msgid "Browse Branch Files..." +msgstr "" + +#: git-gui.sh:1851 +msgid "Visualize Current Branch's History" +msgstr "" + +#: git-gui.sh:1855 +msgid "Visualize All Branch History" +msgstr "" + +#: git-gui.sh:1862 +#, tcl-format +msgid "Browse %s's Files" +msgstr "" + +#: git-gui.sh:1864 +#, tcl-format +msgid "Visualize %s's History" +msgstr "" + +#: git-gui.sh:1869 lib/database.tcl:27 lib/database.tcl:67 +msgid "Database Statistics" +msgstr "" + +#: git-gui.sh:1872 lib/database.tcl:34 +msgid "Compress Database" +msgstr "" + +#: git-gui.sh:1875 +msgid "Verify Database" +msgstr "" + +#: git-gui.sh:1882 git-gui.sh:1886 git-gui.sh:1890 lib/shortcut.tcl:7 +#: lib/shortcut.tcl:39 lib/shortcut.tcl:71 +msgid "Create Desktop Icon" +msgstr "" + +#: git-gui.sh:1895 lib/choose_repository.tcl:176 lib/choose_repository.tcl:184 +msgid "Quit" +msgstr "" + +#: git-gui.sh:1902 +msgid "Undo" +msgstr "" + +#: git-gui.sh:1905 +msgid "Redo" +msgstr "" + +#: git-gui.sh:1909 git-gui.sh:2403 +msgid "Cut" +msgstr "" + +#: git-gui.sh:1912 git-gui.sh:2406 git-gui.sh:2477 git-gui.sh:2549 +#: lib/console.tcl:67 +msgid "Copy" +msgstr "" + +#: git-gui.sh:1915 git-gui.sh:2409 +msgid "Paste" +msgstr "" + +#: git-gui.sh:1918 git-gui.sh:2412 lib/branch_delete.tcl:26 +#: lib/remote_branch_delete.tcl:38 +msgid "Delete" +msgstr "" + +#: git-gui.sh:1922 git-gui.sh:2416 git-gui.sh:2553 lib/console.tcl:69 +msgid "Select All" +msgstr "" + +#: git-gui.sh:1931 +msgid "Create..." +msgstr "" + +#: git-gui.sh:1937 +msgid "Checkout..." +msgstr "" + +#: git-gui.sh:1943 +msgid "Rename..." +msgstr "" + +#: git-gui.sh:1948 git-gui.sh:2048 +msgid "Delete..." +msgstr "" + +#: git-gui.sh:1953 +msgid "Reset..." +msgstr "" + +#: git-gui.sh:1965 git-gui.sh:2350 +msgid "New Commit" +msgstr "" + +#: git-gui.sh:1973 git-gui.sh:2357 +msgid "Amend Last Commit" +msgstr "" + +#: git-gui.sh:1982 git-gui.sh:2317 lib/remote_branch_delete.tcl:99 +msgid "Rescan" +msgstr "" + +#: git-gui.sh:1988 +msgid "Stage To Commit" +msgstr "" + +#: git-gui.sh:1994 +msgid "Stage Changed Files To Commit" +msgstr "" + +#: git-gui.sh:2000 +msgid "Unstage From Commit" +msgstr "" + +#: git-gui.sh:2005 lib/index.tcl:393 +msgid "Revert Changes" +msgstr "" + +#: git-gui.sh:2012 git-gui.sh:2329 git-gui.sh:2427 +msgid "Sign Off" +msgstr "" + +#: git-gui.sh:2016 git-gui.sh:2333 +msgid "Commit@@verb" +msgstr "" + +#: git-gui.sh:2027 +msgid "Local Merge..." +msgstr "" + +#: git-gui.sh:2032 +msgid "Abort Merge..." +msgstr "" + +#: git-gui.sh:2044 +msgid "Push..." +msgstr "" + +#: git-gui.sh:2055 lib/choose_repository.tcl:40 +msgid "Apple" +msgstr "" + +#: git-gui.sh:2058 git-gui.sh:2080 lib/about.tcl:13 +#: lib/choose_repository.tcl:43 lib/choose_repository.tcl:49 +#, tcl-format +msgid "About %s" +msgstr "" + +#: git-gui.sh:2062 +msgid "Preferences..." +msgstr "" + +#: git-gui.sh:2070 git-gui.sh:2595 +msgid "Options..." +msgstr "" + +#: git-gui.sh:2076 lib/choose_repository.tcl:46 +msgid "Help" +msgstr "" + +#: git-gui.sh:2117 +msgid "Online Documentation" +msgstr "" + +#: git-gui.sh:2201 +#, tcl-format +msgid "fatal: cannot stat path %s: No such file or directory" +msgstr "" + +#: git-gui.sh:2234 +msgid "Current Branch:" +msgstr "" + +#: git-gui.sh:2255 +msgid "Staged Changes (Will Commit)" +msgstr "" + +#: git-gui.sh:2274 +msgid "Unstaged Changes" +msgstr "" + +#: git-gui.sh:2323 +msgid "Stage Changed" +msgstr "" + +#: git-gui.sh:2339 lib/transport.tcl:93 lib/transport.tcl:182 +msgid "Push" +msgstr "" + +#: git-gui.sh:2369 +msgid "Initial Commit Message:" +msgstr "" + +#: git-gui.sh:2370 +msgid "Amended Commit Message:" +msgstr "" + +#: git-gui.sh:2371 +msgid "Amended Initial Commit Message:" +msgstr "" + +#: git-gui.sh:2372 +msgid "Amended Merge Commit Message:" +msgstr "" + +#: git-gui.sh:2373 +msgid "Merge Commit Message:" +msgstr "" + +#: git-gui.sh:2374 +msgid "Commit Message:" +msgstr "" + +#: git-gui.sh:2419 git-gui.sh:2557 lib/console.tcl:71 +msgid "Copy All" +msgstr "" + +#: git-gui.sh:2443 lib/blame.tcl:104 +msgid "File:" +msgstr "" + +#: git-gui.sh:2545 +msgid "Refresh" +msgstr "" + +#: git-gui.sh:2566 +msgid "Apply/Reverse Hunk" +msgstr "" + +#: git-gui.sh:2572 +msgid "Decrease Font Size" +msgstr "" + +#: git-gui.sh:2576 +msgid "Increase Font Size" +msgstr "" + +#: git-gui.sh:2581 +msgid "Show Less Context" +msgstr "" + +#: git-gui.sh:2588 +msgid "Show More Context" +msgstr "" + +#: git-gui.sh:2602 +msgid "Unstage Hunk From Commit" +msgstr "" + +#: git-gui.sh:2604 +msgid "Stage Hunk For Commit" +msgstr "" + +#: git-gui.sh:2623 +msgid "Initializing..." +msgstr "" + +#: git-gui.sh:2718 +#, tcl-format +msgid "" +"Possible environment issues exist.\n" +"\n" +"The following environment variables are probably\n" +"going to be ignored by any Git subprocess run\n" +"by %s:\n" +"\n" +msgstr "" + +#: git-gui.sh:2748 +msgid "" +"\n" +"This is due to a known issue with the\n" +"Tcl binary distributed by Cygwin." +msgstr "" + +#: git-gui.sh:2753 +#, tcl-format +msgid "" +"\n" +"\n" +"A good replacement for %s\n" +"is placing values for the user.name and\n" +"user.email settings into your personal\n" +"~/.gitconfig file.\n" +msgstr "" + +#: lib/about.tcl:25 +msgid "git-gui - a graphical user interface for Git." +msgstr "" + +#: lib/blame.tcl:77 +msgid "File Viewer" +msgstr "" + +#: lib/blame.tcl:81 +msgid "Commit:" +msgstr "" + +#: lib/blame.tcl:249 +msgid "Copy Commit" +msgstr "" + +#: lib/blame.tcl:369 +#, tcl-format +msgid "Reading %s..." +msgstr "" + +#: lib/blame.tcl:473 +msgid "Loading copy/move tracking annotations..." +msgstr "" + +#: lib/blame.tcl:493 +msgid "lines annotated" +msgstr "" + +#: lib/blame.tcl:674 +msgid "Loading original location annotations..." +msgstr "" + +#: lib/blame.tcl:677 +msgid "Annotation complete." +msgstr "" + +#: lib/blame.tcl:731 +msgid "Loading annotation..." +msgstr "" + +#: lib/blame.tcl:787 +msgid "Author:" +msgstr "" + +#: lib/blame.tcl:791 +msgid "Committer:" +msgstr "" + +#: lib/blame.tcl:796 +msgid "Original File:" +msgstr "" + +#: lib/blame.tcl:910 +msgid "Originally By:" +msgstr "" + +#: lib/blame.tcl:916 +msgid "In File:" +msgstr "" + +#: lib/blame.tcl:921 +msgid "Copied Or Moved Here By:" +msgstr "" + +#: lib/branch_checkout.tcl:14 lib/branch_checkout.tcl:19 +msgid "Checkout Branch" +msgstr "" + +#: lib/branch_checkout.tcl:23 +msgid "Checkout" +msgstr "" + +#: lib/branch_checkout.tcl:27 lib/branch_create.tcl:35 +#: lib/branch_delete.tcl:32 lib/branch_rename.tcl:30 lib/browser.tcl:281 +#: lib/checkout_op.tcl:522 lib/choose_font.tcl:43 lib/merge.tcl:172 +#: lib/option.tcl:90 lib/remote_branch_delete.tcl:42 lib/transport.tcl:97 +msgid "Cancel" +msgstr "" + +#: lib/branch_checkout.tcl:32 lib/browser.tcl:286 +msgid "Revision" +msgstr "" + +#: lib/branch_checkout.tcl:36 lib/branch_create.tcl:69 lib/option.tcl:202 +msgid "Options" +msgstr "" + +#: lib/branch_checkout.tcl:39 lib/branch_create.tcl:92 +msgid "Fetch Tracking Branch" +msgstr "" + +#: lib/branch_checkout.tcl:44 +msgid "Detach From Local Branch" +msgstr "" + +#: lib/branch_create.tcl:22 +msgid "Create Branch" +msgstr "" + +#: lib/branch_create.tcl:27 +msgid "Create New Branch" +msgstr "" + +#: lib/branch_create.tcl:31 lib/choose_repository.tcl:375 +msgid "Create" +msgstr "" + +#: lib/branch_create.tcl:40 +msgid "Branch Name" +msgstr "" + +#: lib/branch_create.tcl:43 +msgid "Name:" +msgstr "" + +#: lib/branch_create.tcl:58 +msgid "Match Tracking Branch Name" +msgstr "" + +#: lib/branch_create.tcl:66 +msgid "Starting Revision" +msgstr "" + +#: lib/branch_create.tcl:72 +msgid "Update Existing Branch:" +msgstr "" + +#: lib/branch_create.tcl:75 +msgid "No" +msgstr "" + +#: lib/branch_create.tcl:80 +msgid "Fast Forward Only" +msgstr "" + +#: lib/branch_create.tcl:85 lib/checkout_op.tcl:514 +msgid "Reset" +msgstr "" + +#: lib/branch_create.tcl:97 +msgid "Checkout After Creation" +msgstr "" + +#: lib/branch_create.tcl:131 +msgid "Please select a tracking branch." +msgstr "" + +#: lib/branch_create.tcl:140 +#, tcl-format +msgid "Tracking branch %s is not a branch in the remote repository." +msgstr "" + +#: lib/branch_create.tcl:153 lib/branch_rename.tcl:86 +msgid "Please supply a branch name." +msgstr "" + +#: lib/branch_create.tcl:164 lib/branch_rename.tcl:106 +#, tcl-format +msgid "'%s' is not an acceptable branch name." +msgstr "" + +#: lib/branch_delete.tcl:15 +msgid "Delete Branch" +msgstr "" + +#: lib/branch_delete.tcl:20 +msgid "Delete Local Branch" +msgstr "" + +#: lib/branch_delete.tcl:37 +msgid "Local Branches" +msgstr "" + +#: lib/branch_delete.tcl:52 +msgid "Delete Only If Merged Into" +msgstr "" + +#: lib/branch_delete.tcl:54 +msgid "Always (Do not perform merge test.)" +msgstr "" + +#: lib/branch_delete.tcl:103 +#, tcl-format +msgid "The following branches are not completely merged into %s:" +msgstr "" + +#: lib/branch_delete.tcl:115 +msgid "" +"Recovering deleted branches is difficult. \n" +"\n" +" Delete the selected branches?" +msgstr "" + +#: lib/branch_delete.tcl:141 +#, tcl-format +msgid "" +"Failed to delete branches:\n" +"%s" +msgstr "" + +#: lib/branch_rename.tcl:14 lib/branch_rename.tcl:22 +msgid "Rename Branch" +msgstr "" + +#: lib/branch_rename.tcl:26 +msgid "Rename" +msgstr "" + +#: lib/branch_rename.tcl:36 +msgid "Branch:" +msgstr "" + +#: lib/branch_rename.tcl:39 +msgid "New Name:" +msgstr "" + +#: lib/branch_rename.tcl:75 +msgid "Please select a branch to rename." +msgstr "" + +#: lib/branch_rename.tcl:96 lib/checkout_op.tcl:179 +#, tcl-format +msgid "Branch '%s' already exists." +msgstr "" + +#: lib/branch_rename.tcl:117 +#, tcl-format +msgid "Failed to rename '%s'." +msgstr "" + +#: lib/browser.tcl:17 +msgid "Starting..." +msgstr "" + +#: lib/browser.tcl:26 +msgid "File Browser" +msgstr "" + +#: lib/browser.tcl:125 lib/browser.tcl:142 +#, tcl-format +msgid "Loading %s..." +msgstr "" + +#: lib/browser.tcl:186 +msgid "[Up To Parent]" +msgstr "" + +#: lib/browser.tcl:266 lib/browser.tcl:272 +msgid "Browse Branch Files" +msgstr "" + +#: lib/browser.tcl:277 lib/choose_repository.tcl:391 +#: lib/choose_repository.tcl:482 lib/choose_repository.tcl:492 +#: lib/choose_repository.tcl:989 +msgid "Browse" +msgstr "" + +#: lib/checkout_op.tcl:79 +#, tcl-format +msgid "Fetching %s from %s" +msgstr "" + +#: lib/checkout_op.tcl:127 +#, tcl-format +msgid "fatal: Cannot resolve %s" +msgstr "" + +#: lib/checkout_op.tcl:140 lib/console.tcl:79 lib/database.tcl:31 +msgid "Close" +msgstr "" + +#: lib/checkout_op.tcl:169 +#, tcl-format +msgid "Branch '%s' does not exist." +msgstr "" + +#: lib/checkout_op.tcl:206 +#, tcl-format +msgid "" +"Branch '%s' already exists.\n" +"\n" +"It cannot fast-forward to %s.\n" +"A merge is required." +msgstr "" + +#: lib/checkout_op.tcl:220 +#, tcl-format +msgid "Merge strategy '%s' not supported." +msgstr "" + +#: lib/checkout_op.tcl:239 +#, tcl-format +msgid "Failed to update '%s'." +msgstr "" + +#: lib/checkout_op.tcl:251 +msgid "Staging area (index) is already locked." +msgstr "" + +#: lib/checkout_op.tcl:266 +msgid "" +"Last scanned state does not match repository state.\n" +"\n" +"Another Git program has modified this repository since the last scan. A " +"rescan must be performed before the current branch can be changed.\n" +"\n" +"The rescan will be automatically started now.\n" +msgstr "" + +#: lib/checkout_op.tcl:322 +#, tcl-format +msgid "Updating working directory to '%s'..." +msgstr "" + +#: lib/checkout_op.tcl:353 +#, tcl-format +msgid "Aborted checkout of '%s' (file level merging is required)." +msgstr "" + +#: lib/checkout_op.tcl:354 +msgid "File level merge required." +msgstr "" + +#: lib/checkout_op.tcl:358 +#, tcl-format +msgid "Staying on branch '%s'." +msgstr "" + +#: lib/checkout_op.tcl:429 +msgid "" +"You are no longer on a local branch.\n" +"\n" +"If you wanted to be on a branch, create one now starting from 'This Detached " +"Checkout'." +msgstr "" + +#: lib/checkout_op.tcl:446 +#, tcl-format +msgid "Checked out '%s'." +msgstr "" + +#: lib/checkout_op.tcl:478 +#, tcl-format +msgid "Resetting '%s' to '%s' will lose the following commits:" +msgstr "" + +#: lib/checkout_op.tcl:500 +msgid "Recovering lost commits may not be easy." +msgstr "" + +#: lib/checkout_op.tcl:505 +#, tcl-format +msgid "Reset '%s'?" +msgstr "" + +#: lib/checkout_op.tcl:510 lib/merge.tcl:164 +msgid "Visualize" +msgstr "" + +#: lib/checkout_op.tcl:578 +#, tcl-format +msgid "" +"Failed to set current branch.\n" +"\n" +"This working directory is only partially switched. We successfully updated " +"your files, but failed to update an internal Git file.\n" +"\n" +"This should not have occurred. %s will now close and give up." +msgstr "" + +#: lib/choose_font.tcl:39 +msgid "Select" +msgstr "" + +#: lib/choose_font.tcl:53 +msgid "Font Family" +msgstr "" + +#: lib/choose_font.tcl:73 +msgid "Font Size" +msgstr "" + +#: lib/choose_font.tcl:90 +msgid "Font Example" +msgstr "" + +#: lib/choose_font.tcl:101 +msgid "" +"This is example text.\n" +"If you like this text, it can be your font." +msgstr "" + +#: lib/choose_repository.tcl:27 +msgid "Git Gui" +msgstr "" + +#: lib/choose_repository.tcl:80 lib/choose_repository.tcl:380 +msgid "Create New Repository" +msgstr "" + +#: lib/choose_repository.tcl:86 +msgid "New..." +msgstr "" + +#: lib/choose_repository.tcl:93 lib/choose_repository.tcl:468 +msgid "Clone Existing Repository" +msgstr "" + +#: lib/choose_repository.tcl:99 +msgid "Clone..." +msgstr "" + +#: lib/choose_repository.tcl:106 lib/choose_repository.tcl:978 +msgid "Open Existing Repository" +msgstr "" + +#: lib/choose_repository.tcl:112 +msgid "Open..." +msgstr "" + +#: lib/choose_repository.tcl:125 +msgid "Recent Repositories" +msgstr "" + +#: lib/choose_repository.tcl:131 +msgid "Open Recent Repository:" +msgstr "" + +#: lib/choose_repository.tcl:294 +#, tcl-format +msgid "Location %s already exists." +msgstr "" + +#: lib/choose_repository.tcl:300 lib/choose_repository.tcl:307 +#: lib/choose_repository.tcl:314 +#, tcl-format +msgid "Failed to create repository %s:" +msgstr "" + +#: lib/choose_repository.tcl:385 lib/choose_repository.tcl:486 +msgid "Directory:" +msgstr "" + +#: lib/choose_repository.tcl:415 lib/choose_repository.tcl:544 +#: lib/choose_repository.tcl:1013 +msgid "Git Repository" +msgstr "" + +#: lib/choose_repository.tcl:430 lib/choose_repository.tcl:437 +#, tcl-format +msgid "Directory %s already exists." +msgstr "" + +#: lib/choose_repository.tcl:442 +#, tcl-format +msgid "File %s already exists." +msgstr "" + +#: lib/choose_repository.tcl:463 +msgid "Clone" +msgstr "" + +#: lib/choose_repository.tcl:476 +msgid "URL:" +msgstr "" + +#: lib/choose_repository.tcl:496 +msgid "Clone Type:" +msgstr "" + +#: lib/choose_repository.tcl:502 +msgid "Standard (Fast, Semi-Redundant, Hardlinks)" +msgstr "" + +#: lib/choose_repository.tcl:508 +msgid "Full Copy (Slower, Redundant Backup)" +msgstr "" + +#: lib/choose_repository.tcl:514 +msgid "Shared (Fastest, Not Recommended, No Backup)" +msgstr "" + +#: lib/choose_repository.tcl:550 lib/choose_repository.tcl:597 +#: lib/choose_repository.tcl:738 lib/choose_repository.tcl:808 +#: lib/choose_repository.tcl:1019 lib/choose_repository.tcl:1027 +#, tcl-format +msgid "Not a Git repository: %s" +msgstr "" + +#: lib/choose_repository.tcl:586 +msgid "Standard only available for local repository." +msgstr "" + +#: lib/choose_repository.tcl:590 +msgid "Shared only available for local repository." +msgstr "" + +#: lib/choose_repository.tcl:617 +msgid "Failed to configure origin" +msgstr "" + +#: lib/choose_repository.tcl:629 +msgid "Counting objects" +msgstr "" + +#: lib/choose_repository.tcl:630 +msgid "buckets" +msgstr "" + +#: lib/choose_repository.tcl:654 +#, tcl-format +msgid "Unable to copy objects/info/alternates: %s" +msgstr "" + +#: lib/choose_repository.tcl:690 +#, tcl-format +msgid "Nothing to clone from %s." +msgstr "" + +#: lib/choose_repository.tcl:692 lib/choose_repository.tcl:906 +#: lib/choose_repository.tcl:918 +msgid "The 'master' branch has not been initialized." +msgstr "" + +#: lib/choose_repository.tcl:705 +msgid "Hardlinks are unavailable. Falling back to copying." +msgstr "" + +#: lib/choose_repository.tcl:717 +#, tcl-format +msgid "Cloning from %s" +msgstr "" + +#: lib/choose_repository.tcl:748 +msgid "Copying objects" +msgstr "" + +#: lib/choose_repository.tcl:749 +msgid "KiB" +msgstr "" + +#: lib/choose_repository.tcl:773 +#, tcl-format +msgid "Unable to copy object: %s" +msgstr "" + +#: lib/choose_repository.tcl:783 +msgid "Linking objects" +msgstr "" + +#: lib/choose_repository.tcl:784 +msgid "objects" +msgstr "" + +#: lib/choose_repository.tcl:792 +#, tcl-format +msgid "Unable to hardlink object: %s" +msgstr "" + +#: lib/choose_repository.tcl:847 +msgid "Cannot fetch branches and objects. See console output for details." +msgstr "" + +#: lib/choose_repository.tcl:858 +msgid "Cannot fetch tags. See console output for details." +msgstr "" + +#: lib/choose_repository.tcl:882 +msgid "Cannot determine HEAD. See console output for details." +msgstr "" + +#: lib/choose_repository.tcl:891 +#, tcl-format +msgid "Unable to cleanup %s" +msgstr "" + +#: lib/choose_repository.tcl:897 +msgid "Clone failed." +msgstr "" + +#: lib/choose_repository.tcl:904 +msgid "No default branch obtained." +msgstr "" + +#: lib/choose_repository.tcl:915 +#, tcl-format +msgid "Cannot resolve %s as a commit." +msgstr "" + +#: lib/choose_repository.tcl:927 +msgid "Creating working directory" +msgstr "" + +#: lib/choose_repository.tcl:928 lib/index.tcl:65 lib/index.tcl:127 +#: lib/index.tcl:193 +msgid "files" +msgstr "" + +#: lib/choose_repository.tcl:957 +msgid "Initial file checkout failed." +msgstr "" + +#: lib/choose_repository.tcl:973 +msgid "Open" +msgstr "" + +#: lib/choose_repository.tcl:983 +msgid "Repository:" +msgstr "" + +#: lib/choose_repository.tcl:1033 +#, tcl-format +msgid "Failed to open repository %s:" +msgstr "" + +#: lib/choose_rev.tcl:53 +msgid "This Detached Checkout" +msgstr "" + +#: lib/choose_rev.tcl:60 +msgid "Revision Expression:" +msgstr "" + +#: lib/choose_rev.tcl:74 +msgid "Local Branch" +msgstr "" + +#: lib/choose_rev.tcl:79 +msgid "Tracking Branch" +msgstr "" + +#: lib/choose_rev.tcl:84 lib/choose_rev.tcl:537 +msgid "Tag" +msgstr "" + +#: lib/choose_rev.tcl:317 +#, tcl-format +msgid "Invalid revision: %s" +msgstr "" + +#: lib/choose_rev.tcl:338 +msgid "No revision selected." +msgstr "" + +#: lib/choose_rev.tcl:346 +msgid "Revision expression is empty." +msgstr "" + +#: lib/choose_rev.tcl:530 +msgid "Updated" +msgstr "" + +#: lib/choose_rev.tcl:558 +msgid "URL" +msgstr "" + +#: lib/commit.tcl:9 +msgid "" +"There is nothing to amend.\n" +"\n" +"You are about to create the initial commit. There is no commit before this " +"to amend.\n" +msgstr "" + +#: lib/commit.tcl:18 +msgid "" +"Cannot amend while merging.\n" +"\n" +"You are currently in the middle of a merge that has not been fully " +"completed. You cannot amend the prior commit unless you first abort the " +"current merge activity.\n" +msgstr "" + +#: lib/commit.tcl:49 +msgid "Error loading commit data for amend:" +msgstr "" + +#: lib/commit.tcl:76 +msgid "Unable to obtain your identity:" +msgstr "" + +#: lib/commit.tcl:81 +msgid "Invalid GIT_COMMITTER_IDENT:" +msgstr "" + +#: lib/commit.tcl:133 +msgid "" +"Last scanned state does not match repository state.\n" +"\n" +"Another Git program has modified this repository since the last scan. A " +"rescan must be performed before another commit can be created.\n" +"\n" +"The rescan will be automatically started now.\n" +msgstr "" + +#: lib/commit.tcl:154 +#, tcl-format +msgid "" +"Unmerged files cannot be committed.\n" +"\n" +"File %s has merge conflicts. You must resolve them and stage the file " +"before committing.\n" +msgstr "" + +#: lib/commit.tcl:162 +#, tcl-format +msgid "" +"Unknown file state %s detected.\n" +"\n" +"File %s cannot be committed by this program.\n" +msgstr "" + +#: lib/commit.tcl:170 +msgid "" +"No changes to commit.\n" +"\n" +"You must stage at least 1 file before you can commit.\n" +msgstr "" + +#: lib/commit.tcl:183 +msgid "" +"Please supply a commit message.\n" +"\n" +"A good commit message has the following format:\n" +"\n" +"- First line: Describe in one sentence what you did.\n" +"- Second line: Blank\n" +"- Remaining lines: Describe why this change is good.\n" +msgstr "" + +#: lib/commit.tcl:257 +msgid "write-tree failed:" +msgstr "" + +#: lib/commit.tcl:275 +#, tcl-format +msgid "Commit %s appears to be corrupt" +msgstr "" + +#: lib/commit.tcl:279 +msgid "" +"No changes to commit.\n" +"\n" +"No files were modified by this commit and it was not a merge commit.\n" +"\n" +"A rescan will be automatically started now.\n" +msgstr "" + +#: lib/commit.tcl:286 +msgid "No changes to commit." +msgstr "" + +#: lib/commit.tcl:303 +#, tcl-format +msgid "warning: Tcl does not support encoding '%s'." +msgstr "" + +#: lib/commit.tcl:317 +msgid "commit-tree failed:" +msgstr "" + +#: lib/commit.tcl:339 +msgid "update-ref failed:" +msgstr "" + +#: lib/commit.tcl:430 +#, tcl-format +msgid "Created commit %s: %s" +msgstr "" + +#: lib/console.tcl:57 +msgid "Working... please wait..." +msgstr "" + +#: lib/console.tcl:183 +msgid "Success" +msgstr "" + +#: lib/console.tcl:196 +msgid "Error: Command Failed" +msgstr "" + +#: lib/database.tcl:43 +msgid "Number of loose objects" +msgstr "" + +#: lib/database.tcl:44 +msgid "Disk space used by loose objects" +msgstr "" + +#: lib/database.tcl:45 +msgid "Number of packed objects" +msgstr "" + +#: lib/database.tcl:46 +msgid "Number of packs" +msgstr "" + +#: lib/database.tcl:47 +msgid "Disk space used by packed objects" +msgstr "" + +#: lib/database.tcl:48 +msgid "Packed objects waiting for pruning" +msgstr "" + +#: lib/database.tcl:49 +msgid "Garbage files" +msgstr "" + +#: lib/database.tcl:72 +msgid "Compressing the object database" +msgstr "" + +#: lib/database.tcl:83 +msgid "Verifying the object database with fsck-objects" +msgstr "" + +#: lib/database.tcl:108 +#, tcl-format +msgid "" +"This repository currently has approximately %i loose objects.\n" +"\n" +"To maintain optimal performance it is strongly recommended that you compress " +"the database when more than %i loose objects exist.\n" +"\n" +"Compress the database now?" +msgstr "" + +#: lib/date.tcl:25 +#, tcl-format +msgid "Invalid date from Git: %s" +msgstr "" + +#: lib/diff.tcl:42 +#, tcl-format +msgid "" +"No differences detected.\n" +"\n" +"%s has no changes.\n" +"\n" +"The modification date of this file was updated by another application, but " +"the content within the file was not changed.\n" +"\n" +"A rescan will be automatically started to find other files which may have " +"the same state." +msgstr "" + +#: lib/diff.tcl:81 +#, tcl-format +msgid "Loading diff of %s..." +msgstr "" + +#: lib/diff.tcl:114 lib/diff.tcl:184 +#, tcl-format +msgid "Unable to display %s" +msgstr "" + +#: lib/diff.tcl:115 +msgid "Error loading file:" +msgstr "" + +#: lib/diff.tcl:122 +msgid "Git Repository (subproject)" +msgstr "" + +#: lib/diff.tcl:134 +msgid "* Binary file (not showing content)." +msgstr "" + +#: lib/diff.tcl:185 +msgid "Error loading diff:" +msgstr "" + +#: lib/diff.tcl:302 +msgid "Failed to unstage selected hunk." +msgstr "" + +#: lib/diff.tcl:309 +msgid "Failed to stage selected hunk." +msgstr "" + +#: lib/error.tcl:12 lib/error.tcl:102 +msgid "error" +msgstr "" + +#: lib/error.tcl:28 +msgid "warning" +msgstr "" + +#: lib/error.tcl:81 +msgid "You must correct the above errors before committing." +msgstr "" + +#: lib/index.tcl:6 +msgid "Unable to unlock the index." +msgstr "" + +#: lib/index.tcl:15 +msgid "Index Error" +msgstr "" + +#: lib/index.tcl:21 +msgid "" +"Updating the Git index failed. A rescan will be automatically started to " +"resynchronize git-gui." +msgstr "" + +#: lib/index.tcl:27 +msgid "Continue" +msgstr "" + +#: lib/index.tcl:31 +msgid "Unlock Index" +msgstr "" + +#: lib/index.tcl:282 +#, tcl-format +msgid "Unstaging %s from commit" +msgstr "" + +#: lib/index.tcl:326 +#, tcl-format +msgid "Adding %s" +msgstr "" + +#: lib/index.tcl:381 +#, tcl-format +msgid "Revert changes in file %s?" +msgstr "" + +#: lib/index.tcl:383 +#, tcl-format +msgid "Revert changes in these %i files?" +msgstr "" + +#: lib/index.tcl:389 +msgid "Any unstaged changes will be permanently lost by the revert." +msgstr "" + +#: lib/index.tcl:392 +msgid "Do Nothing" +msgstr "" + +#: lib/merge.tcl:13 +msgid "" +"Cannot merge while amending.\n" +"\n" +"You must finish amending this commit before starting any type of merge.\n" +msgstr "" + +#: lib/merge.tcl:27 +msgid "" +"Last scanned state does not match repository state.\n" +"\n" +"Another Git program has modified this repository since the last scan. A " +"rescan must be performed before a merge can be performed.\n" +"\n" +"The rescan will be automatically started now.\n" +msgstr "" + +#: lib/merge.tcl:44 +#, tcl-format +msgid "" +"You are in the middle of a conflicted merge.\n" +"\n" +"File %s has merge conflicts.\n" +"\n" +"You must resolve them, stage the file, and commit to complete the current " +"merge. Only then can you begin another merge.\n" +msgstr "" + +#: lib/merge.tcl:54 +#, tcl-format +msgid "" +"You are in the middle of a change.\n" +"\n" +"File %s is modified.\n" +"\n" +"You should complete the current commit before starting a merge. Doing so " +"will help you abort a failed merge, should the need arise.\n" +msgstr "" + +#: lib/merge.tcl:106 +#, tcl-format +msgid "%s of %s" +msgstr "" + +#: lib/merge.tcl:119 +#, tcl-format +msgid "Merging %s and %s" +msgstr "" + +#: lib/merge.tcl:131 +msgid "Merge completed successfully." +msgstr "" + +#: lib/merge.tcl:133 +msgid "Merge failed. Conflict resolution is required." +msgstr "" + +#: lib/merge.tcl:158 +#, tcl-format +msgid "Merge Into %s" +msgstr "" + +#: lib/merge.tcl:177 +msgid "Revision To Merge" +msgstr "" + +#: lib/merge.tcl:212 +msgid "" +"Cannot abort while amending.\n" +"\n" +"You must finish amending this commit.\n" +msgstr "" + +#: lib/merge.tcl:222 +msgid "" +"Abort merge?\n" +"\n" +"Aborting the current merge will cause *ALL* uncommitted changes to be lost.\n" +"\n" +"Continue with aborting the current merge?" +msgstr "" + +#: lib/merge.tcl:228 +msgid "" +"Reset changes?\n" +"\n" +"Resetting the changes will cause *ALL* uncommitted changes to be lost.\n" +"\n" +"Continue with resetting the current changes?" +msgstr "" + +#: lib/merge.tcl:239 +msgid "Aborting" +msgstr "" + +#: lib/merge.tcl:266 +msgid "Abort failed." +msgstr "" + +#: lib/merge.tcl:268 +msgid "Abort completed. Ready." +msgstr "" + +#: lib/option.tcl:82 +msgid "Restore Defaults" +msgstr "" + +#: lib/option.tcl:86 +msgid "Save" +msgstr "" + +#: lib/option.tcl:96 +#, tcl-format +msgid "%s Repository" +msgstr "" + +#: lib/option.tcl:97 +msgid "Global (All Repositories)" +msgstr "" + +#: lib/option.tcl:103 +msgid "User Name" +msgstr "" + +#: lib/option.tcl:104 +msgid "Email Address" +msgstr "" + +#: lib/option.tcl:106 +msgid "Summarize Merge Commits" +msgstr "" + +#: lib/option.tcl:107 +msgid "Merge Verbosity" +msgstr "" + +#: lib/option.tcl:108 +msgid "Show Diffstat After Merge" +msgstr "" + +#: lib/option.tcl:110 +msgid "Trust File Modification Timestamps" +msgstr "" + +#: lib/option.tcl:111 +msgid "Prune Tracking Branches During Fetch" +msgstr "" + +#: lib/option.tcl:112 +msgid "Match Tracking Branches" +msgstr "" + +#: lib/option.tcl:113 +msgid "Number of Diff Context Lines" +msgstr "" + +#: lib/option.tcl:114 +msgid "New Branch Name Template" +msgstr "" + +#: lib/option.tcl:176 +msgid "Change Font" +msgstr "" + +#: lib/option.tcl:180 +#, tcl-format +msgid "Choose %s" +msgstr "" + +#: lib/option.tcl:186 +msgid "pt." +msgstr "" + +#: lib/option.tcl:200 +msgid "Preferences" +msgstr "" + +#: lib/option.tcl:235 +msgid "Failed to completely save options:" +msgstr "" + +#: lib/remote_branch_delete.tcl:29 lib/remote_branch_delete.tcl:34 +msgid "Delete Remote Branch" +msgstr "" + +#: lib/remote_branch_delete.tcl:47 +msgid "From Repository" +msgstr "" + +#: lib/remote_branch_delete.tcl:50 lib/transport.tcl:123 +msgid "Remote:" +msgstr "" + +#: lib/remote_branch_delete.tcl:66 lib/transport.tcl:138 +msgid "Arbitrary URL:" +msgstr "" + +#: lib/remote_branch_delete.tcl:84 +msgid "Branches" +msgstr "" + +#: lib/remote_branch_delete.tcl:109 +msgid "Delete Only If" +msgstr "" + +#: lib/remote_branch_delete.tcl:111 +msgid "Merged Into:" +msgstr "" + +#: lib/remote_branch_delete.tcl:119 +msgid "Always (Do not perform merge checks)" +msgstr "" + +#: lib/remote_branch_delete.tcl:152 +msgid "A branch is required for 'Merged Into'." +msgstr "" + +#: lib/remote_branch_delete.tcl:184 +#, tcl-format +msgid "" +"The following branches are not completely merged into %s:\n" +"\n" +" - %s" +msgstr "" + +#: lib/remote_branch_delete.tcl:189 +#, tcl-format +msgid "" +"One or more of the merge tests failed because you have not fetched the " +"necessary commits. Try fetching from %s first." +msgstr "" + +#: lib/remote_branch_delete.tcl:207 +msgid "Please select one or more branches to delete." +msgstr "" + +#: lib/remote_branch_delete.tcl:216 +msgid "" +"Recovering deleted branches is difficult.\n" +"\n" +"Delete the selected branches?" +msgstr "" + +#: lib/remote_branch_delete.tcl:226 +#, tcl-format +msgid "Deleting branches from %s" +msgstr "" + +#: lib/remote_branch_delete.tcl:286 +msgid "No repository selected." +msgstr "" + +#: lib/remote_branch_delete.tcl:291 +#, tcl-format +msgid "Scanning %s..." +msgstr "" + +#: lib/remote.tcl:165 +msgid "Prune from" +msgstr "" + +#: lib/remote.tcl:170 +msgid "Fetch from" +msgstr "" + +#: lib/remote.tcl:213 +msgid "Push to" +msgstr "" + +#: lib/shortcut.tcl:20 lib/shortcut.tcl:61 +msgid "Cannot write shortcut:" +msgstr "" + +#: lib/shortcut.tcl:136 +msgid "Cannot write icon:" +msgstr "" + +#: lib/status_bar.tcl:83 +#, tcl-format +msgid "%s ... %*i of %*i %s (%3i%%)" +msgstr "" + +#: lib/transport.tcl:6 +#, tcl-format +msgid "fetch %s" +msgstr "" + +#: lib/transport.tcl:7 +#, tcl-format +msgid "Fetching new changes from %s" +msgstr "" + +#: lib/transport.tcl:18 +#, tcl-format +msgid "remote prune %s" +msgstr "" + +#: lib/transport.tcl:19 +#, tcl-format +msgid "Pruning tracking branches deleted from %s" +msgstr "" + +#: lib/transport.tcl:25 lib/transport.tcl:71 +#, tcl-format +msgid "push %s" +msgstr "" + +#: lib/transport.tcl:26 +#, tcl-format +msgid "Pushing changes to %s" +msgstr "" + +#: lib/transport.tcl:72 +#, tcl-format +msgid "Pushing %s %s to %s" +msgstr "" + +#: lib/transport.tcl:89 +msgid "Push Branches" +msgstr "" + +#: lib/transport.tcl:103 +msgid "Source Branches" +msgstr "" + +#: lib/transport.tcl:120 +msgid "Destination Repository" +msgstr "" + +#: lib/transport.tcl:158 +msgid "Transfer Options" +msgstr "" + +#: lib/transport.tcl:160 +msgid "Force overwrite existing branch (may discard changes)" +msgstr "" + +#: lib/transport.tcl:164 +msgid "Use thin pack (for slow network connections)" +msgstr "" + +#: lib/transport.tcl:168 +msgid "Include tags" +msgstr "" diff --git a/git-gui/po/glossary/Makefile b/git-gui/po/glossary/Makefile new file mode 100644 index 0000000000..749aa2e7ec --- /dev/null +++ b/git-gui/po/glossary/Makefile @@ -0,0 +1,9 @@ +PO_TEMPLATE = git-gui-glossary.pot + +ALL_POFILES = $(wildcard *.po) + +$(PO_TEMPLATE): $(subst .pot,.txt,$(PO_TEMPLATE)) + ./txt-to-pot.sh $< > $@ + +update-po:: git-gui-glossary.pot + $(foreach p, $(ALL_POFILES), echo Updating $p ; msgmerge -U $p $(PO_TEMPLATE) ; ) diff --git a/git-gui/po/glossary/de.po b/git-gui/po/glossary/de.po new file mode 100644 index 0000000000..c94786c6ab --- /dev/null +++ b/git-gui/po/glossary/de.po @@ -0,0 +1,185 @@ +# Translation of git-gui glossary to German +# Copyright (C) 2007 Shawn Pearce, et al. +# This file is distributed under the same license as the git package. +# Christian Stimming <stimming@tuhh.de>, 2007 +# +msgid "" +msgstr "" +"Project-Id-Version: git-gui glossary\n" +"POT-Creation-Date: 2007-10-19 21:43+0200\n" +"PO-Revision-Date: 2007-10-20 15:24+0200\n" +"Last-Translator: Christian Stimming <stimming@tuhh.de>\n" +"Language-Team: German \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#. "English Definition (Dear translator: This file will never be visible to the user! It should only serve as a tool for you, the translator. Nothing more.)" +msgid "" +"English Term (Dear translator: This file will never be visible to the user!)" +msgstr "" +"Deutsche Ãœbersetzung.\n" +"Andere deutsche SCM:\n" +" http://tortoisesvn.net/docs/release/TortoiseSVN_de/index.html und http://" +"tortoisesvn.tigris.org/svn/tortoisesvn/trunk/Languages/Tortoise_de.po " +"(username=guest, password empty, gut),\n" +" http://msdn.microsoft.com/de-de/library/ms181038(vs.80).aspx (MS Visual " +"Source Safe, kommerziell),\n" +" http://cvsbook.red-bean.com/translations/german/Kap_06.html " +"(mittelmäßig),\n" +" http://tortoisecvs.cvs.sourceforge.net/tortoisecvs/po/TortoiseCVS/de_DE.po?" +"view=markup (mittelmäßig),\n" +" http://rapidsvn.tigris.org/svn/rapidsvn/trunk/src/locale/de/rapidsvn.po " +"(username=guest, password empty, schlecht)" + +#. "" +msgid "amend" +msgstr "nachbessern (ergänzen)" + +#. "" +msgid "annotate" +msgstr "annotieren" + +#. "A 'branch' is an active line of development." +msgid "branch [noun]" +msgstr "Zweig" + +#. "" +msgid "branch [verb]" +msgstr "verzweigen" + +#. "" +msgid "checkout [noun]" +msgstr "" +"Arbeitskopie (Erstellung einer Arbeitskopie; Auscheck? Ausspielung? Abruf? " +"Source Safe: Auscheckvorgang)" + +#. "The action of updating the working tree to a revision which was stored in the object database." +msgid "checkout [verb]" +msgstr "" +"Arbeitskopie erstellen; Zweig umstellen [checkout a branch] (auschecken? " +"ausspielen? abrufen? Source Safe: auschecken)" + +#. "" +msgid "clone [verb]" +msgstr "kopieren" + +#. "A single point in the git history." +msgid "commit [noun]" +msgstr "" +"Version; Eintragung; Änderung (Buchung?, Eintragung?, Ãœbertragung?, " +"Sendung?, Ãœbergabe?, Einspielung?, Ablagevorgang?)" + +#. "The action of storing a new snapshot of the project's state in the git history." +msgid "commit [verb]" +msgstr "" +"eintragen (TortoiseSVN: übertragen; Source Safe: einchecken; senden?, " +"übergeben?, einspielen?, einpflegen?, ablegen?)" + +#. "" +msgid "diff [noun]" +msgstr "Vergleich (Source Safe: Unterschiede)" + +#. "" +msgid "diff [verb]" +msgstr "vergleichen" + +#. "A fast-forward is a special type of merge where you have a revision and you are merging another branch's changes that happen to be a descendant of what you have." +msgid "fast forward merge" +msgstr "Schnellzusammenführung" + +#. "Fetching a branch means to get the branch's head from a remote repository, to find out which objects are missing from the local object database, and to get them, too." +msgid "fetch" +msgstr "anfordern (holen?)" + +#. "A collection of files. The index is a stored version of your working tree." +msgid "index (in git-gui: staging area)" +msgstr "Bereitstellung" + +#. "A successful merge results in the creation of a new commit representing the result of the merge." +msgid "merge [noun]" +msgstr "Zusammenführung" + +#. "To bring the contents of another branch into the current branch." +msgid "merge [verb]" +msgstr "zusammenführen" + +#. "" +msgid "message" +msgstr "Beschreibung (Meldung?, Nachricht?; Source Safe: Kommentar)" + +#. "Deletes all stale tracking branches under <name>. These stale branches have already been removed from the remote repository referenced by <name>, but are still locally available in 'remotes/<name>'." +msgid "prune" +msgstr "entfernen" + +#. "Pulling a branch means to fetch it and merge it." +msgid "pull" +msgstr "übernehmen (ziehen?)" + +#. "Pushing a branch means to get the branch's head ref from a remote repository, and ... (well, can someone please explain it for mere mortals?)" +msgid "push" +msgstr "versenden (ausliefern? hochladen? verschicken? schieben?)" + +#. "" +msgid "redo" +msgstr "wiederholen" + +#. "An other repository ('remote'). One might have a set of remotes whose branches one tracks." +msgid "remote" +msgstr "Andere Archive (Gegenseite?, Entfernte?, Server?)" + +#. "A collection of refs (?) together with an object database containing all objects which are reachable from the refs... (oops, you've lost me here. Again, please an explanation for mere mortals?)" +msgid "repository" +msgstr "Projektarchiv" + +#. "" +msgid "reset" +msgstr "zurücksetzen (zurückkehren?)" + +#. "" +msgid "revert" +msgstr "revidieren" + +#. "A particular state of files and directories which was stored in the object database." +msgid "revision" +msgstr "Version (TortoiseSVN: Revision; Source Safe: Version)" + +#. "" +msgid "sign off" +msgstr "abzeichnen (gegenzeichnen?, freizeichnen?, absegnen?)" + +#. "" +msgid "staging area" +msgstr "Bereitstellung" + +#. "" +msgid "status" +msgstr "Status" + +#. "A ref pointing to a tag or commit object" +msgid "tag [noun]" +msgstr "Markierung" + +#. "" +msgid "tag [verb]" +msgstr "markieren" + +#. "A regular git branch that is used to follow changes from another repository." +msgid "tracking branch" +msgstr "Ãœbernahmezweig" + +#. "" +msgid "undo" +msgstr "rückgängig" + +#. "" +msgid "update" +msgstr "aktualisieren" + +#. "" +msgid "verify" +msgstr "überprüfen" + +#. "The tree of actual checked out files." +msgid "working copy, working tree" +msgstr "Arbeitskopie" diff --git a/git-gui/po/glossary/git-gui-glossary.pot b/git-gui/po/glossary/git-gui-glossary.pot new file mode 100644 index 0000000000..48af803314 --- /dev/null +++ b/git-gui/po/glossary/git-gui-glossary.pot @@ -0,0 +1,164 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Free Software Foundation, Inc. +# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2007-10-19 21:43+0200\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: ENCODING\n" + +#. "English Definition (Dear translator: This file will never be visible to the user! It should only serve as a tool for you, the translator. Nothing more.)" +msgid "English Term (Dear translator: This file will never be visible to the user!)" +msgstr "" + +#. "" +msgid "amend" +msgstr "" + +#. "" +msgid "annotate" +msgstr "" + +#. "A 'branch' is an active line of development." +msgid "branch [noun]" +msgstr "" + +#. "" +msgid "branch [verb]" +msgstr "" + +#. "" +msgid "checkout [noun]" +msgstr "" + +#. "The action of updating the working tree to a revision which was stored in the object database." +msgid "checkout [verb]" +msgstr "" + +#. "" +msgid "clone [verb]" +msgstr "" + +#. "A single point in the git history." +msgid "commit [noun]" +msgstr "" + +#. "The action of storing a new snapshot of the project's state in the git history." +msgid "commit [verb]" +msgstr "" + +#. "" +msgid "diff [noun]" +msgstr "" + +#. "" +msgid "diff [verb]" +msgstr "" + +#. "A fast-forward is a special type of merge where you have a revision and you are merging another branch's changes that happen to be a descendant of what you have." +msgid "fast forward merge" +msgstr "" + +#. "Fetching a branch means to get the branch's head from a remote repository, to find out which objects are missing from the local object database, and to get them, too." +msgid "fetch" +msgstr "" + +#. "A collection of files. The index is a stored version of your working tree." +msgid "index (in git-gui: staging area)" +msgstr "" + +#. "A successful merge results in the creation of a new commit representing the result of the merge." +msgid "merge [noun]" +msgstr "" + +#. "To bring the contents of another branch into the current branch." +msgid "merge [verb]" +msgstr "" + +#. "" +msgid "message" +msgstr "" + +#. "Deletes all stale tracking branches under <name>. These stale branches have already been removed from the remote repository referenced by <name>, but are still locally available in 'remotes/<name>'." +msgid "prune" +msgstr "" + +#. "Pulling a branch means to fetch it and merge it." +msgid "pull" +msgstr "" + +#. "Pushing a branch means to get the branch's head ref from a remote repository, and ... (well, can someone please explain it for mere mortals?)" +msgid "push" +msgstr "" + +#. "" +msgid "redo" +msgstr "" + +#. "An other repository ('remote'). One might have a set of remotes whose branches one tracks." +msgid "remote" +msgstr "" + +#. "A collection of refs (?) together with an object database containing all objects which are reachable from the refs... (oops, you've lost me here. Again, please an explanation for mere mortals?)" +msgid "repository" +msgstr "" + +#. "" +msgid "reset" +msgstr "" + +#. "" +msgid "revert" +msgstr "" + +#. "A particular state of files and directories which was stored in the object database." +msgid "revision" +msgstr "" + +#. "" +msgid "sign off" +msgstr "" + +#. "" +msgid "staging area" +msgstr "" + +#. "" +msgid "status" +msgstr "" + +#. "A ref pointing to a tag or commit object" +msgid "tag [noun]" +msgstr "" + +#. "" +msgid "tag [verb]" +msgstr "" + +#. "A regular git branch that is used to follow changes from another repository." +msgid "tracking branch" +msgstr "" + +#. "" +msgid "undo" +msgstr "" + +#. "" +msgid "update" +msgstr "" + +#. "" +msgid "verify" +msgstr "" + +#. "The tree of actual checked out files." +msgid "working copy, working tree" +msgstr "" + diff --git a/git-gui/po/glossary/git-gui-glossary.txt b/git-gui/po/glossary/git-gui-glossary.txt new file mode 100644 index 0000000000..500d0a0ea7 --- /dev/null +++ b/git-gui/po/glossary/git-gui-glossary.txt @@ -0,0 +1,37 @@ +"English Term (Dear translator: This file will never be visible to the user!)" "English Definition (Dear translator: This file will never be visible to the user! It should only serve as a tool for you, the translator. Nothing more.)" +"amend" "" +"annotate" "" +"branch [noun]" "A 'branch' is an active line of development." +"branch [verb]" "" +"checkout [noun]" "" +"checkout [verb]" "The action of updating the working tree to a revision which was stored in the object database." +"clone [verb]" "" +"commit [noun]" "A single point in the git history." +"commit [verb]" "The action of storing a new snapshot of the project's state in the git history." +"diff [noun]" "" +"diff [verb]" "" +"fast forward merge" "A fast-forward is a special type of merge where you have a revision and you are merging another branch's changes that happen to be a descendant of what you have." +"fetch" "Fetching a branch means to get the branch's head from a remote repository, to find out which objects are missing from the local object database, and to get them, too." +"index (in git-gui: staging area)" "A collection of files. The index is a stored version of your working tree." +"merge [noun]" "A successful merge results in the creation of a new commit representing the result of the merge." +"merge [verb]" "To bring the contents of another branch into the current branch." +"message" "" +"prune" "Deletes all stale tracking branches under <name>. These stale branches have already been removed from the remote repository referenced by <name>, but are still locally available in 'remotes/<name>'." +"pull" "Pulling a branch means to fetch it and merge it." +"push" "Pushing a branch means to get the branch's head ref from a remote repository, and ... (well, can someone please explain it for mere mortals?)" +"redo" "" +"remote" "An other repository ('remote'). One might have a set of remotes whose branches one tracks." +"repository" "A collection of refs (?) together with an object database containing all objects which are reachable from the refs... (oops, you've lost me here. Again, please an explanation for mere mortals?)" +"reset" "" +"revert" "" +"revision" "A particular state of files and directories which was stored in the object database." +"sign off" "" +"staging area" "" +"status" "" +"tag [noun]" "A ref pointing to a tag or commit object" +"tag [verb]" "" +"tracking branch" "A regular git branch that is used to follow changes from another repository." +"undo" "" +"update" "" +"verify" "" +"working copy, working tree" "The tree of actual checked out files." diff --git a/git-gui/po/glossary/it.po b/git-gui/po/glossary/it.po new file mode 100644 index 0000000000..bb46b48d6b --- /dev/null +++ b/git-gui/po/glossary/it.po @@ -0,0 +1,184 @@ +# Translation of git-gui glossary to Italian +# Copyright (C) 2007 Shawn Pearce, et al. +# This file is distributed under the same license as the git package. +# Christian Stimming <stimming@tuhh.de>, 2007 +# +msgid "" +msgstr "" +"Project-Id-Version: git-gui glossary\n" +"POT-Creation-Date: 2007-10-19 21:43+0200\n" +"PO-Revision-Date: 2007-10-10 15:24+0200\n" +"Last-Translator: Michele Ballabio <barra_cuda@katamail.com>\n" +"Language-Team: Italian \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#. "English Definition (Dear translator: This file will never be visible to the user! It should only serve as a tool for you, the translator. Nothing more.)" +msgid "" +"English Term (Dear translator: This file will never be visible to the user!)" +msgstr "" +"Traduzione italiana.\n" +"Altri SCM in italiano:\n" +" http://tortoisesvn.tigris.org/svn/tortoisesvn/trunk/Languages/Tortoise_it." +"po (username=guest, password empty),\n" +" http://tortoisecvs.cvs.sourceforge.net/tortoisecvs/po/TortoiseCVS/it_IT.po?" +"view=markup ,\n" +" http://rapidsvn.tigris.org/svn/rapidsvn/trunk/src/locale/it_IT/rapidsvn.po " +"(username=guest, password empty)" + +#. "" +msgid "amend" +msgstr "correggere, correzione" + +#. "" +msgid "annotate" +msgstr "annotare, annotazione" + +#. "A 'branch' is an active line of development." +msgid "branch [noun]" +msgstr "ramo, diramazione, ramificazione" + +#. "" +msgid "branch [verb]" +msgstr "creare ramo, ramificare, diramare" + +#. "" +msgid "checkout [noun]" +msgstr "attivazione, checkout, revisione attiva, prelievo (TortoiseCVS)?" + +#. "The action of updating the working tree to a revision which was stored in the object database." +msgid "checkout [verb]" +msgstr "" +"attivare, effettuare un checkout, attivare revisione, prelevare " +"(TortoiseCVS), ritirare (TSVN)?" + +#. "" +msgid "clone [verb]" +msgstr "clonare" + +#. "A single point in the git history." +msgid "commit [noun]" +msgstr "revisione, commit, deposito (TortoiseCVS), invio (TSVN)?" + +#. "The action of storing a new snapshot of the project's state in the git history." +msgid "commit [verb]" +msgstr "" +"creare una nuova revisione, archiviare, effettuare un commit, depositare " +"(nel server), fare un deposito (TortoiseCVS), inviare (TSVN)?" + +#. "" +msgid "diff [noun]" +msgstr "differenza, confronto, comparazione, raffronto" + +#. "" +msgid "diff [verb]" +msgstr "confronta, mostra le differenze" + +#. "A fast-forward is a special type of merge where you have a revision and you are merging another branch's changes that happen to be a descendant of what you have." +msgid "fast forward merge" +msgstr "fusione in 'fast-forward', fusione in avanti veloce" + +#. "Fetching a branch means to get the branch's head from a remote repository, to find out which objects are missing from the local object database, and to get them, too." +msgid "fetch" +msgstr "recuperare, prelevare, prendere da, recuperare (TSVN)" + +#. "A collection of files. The index is a stored version of your working tree." +msgid "index (in git-gui: staging area)" +msgstr "indice" + +#. "A successful merge results in the creation of a new commit representing the result of the merge." +msgid "merge [noun]" +msgstr "fusione, unione" + +#. "To bring the contents of another branch into the current branch." +msgid "merge [verb]" +msgstr "effettuare la fusione, unire, fondere, eseguire la fusione" + +#. "" +msgid "message" +msgstr "messaggio, commento" + +#. "Deletes all stale tracking branches under <name>. These stale branches have already been removed from the remote repository referenced by <name>, but are still locally available in 'remotes/<name>'." +msgid "prune" +msgstr "potatura" + +#. "Pulling a branch means to fetch it and merge it." +msgid "pull" +msgstr "" +"prendi (recupera) e fondi (unisci)? (in pratica una traduzione di fetch + " +"merge)" + +#. "Pushing a branch means to get the branch's head ref from a remote repository, and ... (well, can someone please explain it for mere mortals?)" +msgid "push" +msgstr "propaga" + +#. "" +msgid "redo" +msgstr "ripeti, rifai" + +#. "An other repository ('remote'). One might have a set of remotes whose branches one tracks." +msgid "remote" +msgstr "remoto" + +#. "A collection of refs (?) together with an object database containing all objects which are reachable from the refs... (oops, you've lost me here. Again, please an explanation for mere mortals?)" +msgid "repository" +msgstr "archivio, repository, database? deposito (rapidsvn)?" + +#. "" +msgid "reset" +msgstr "ripristinare, annullare, azzerare, ripristinare" + +#. "" +msgid "revert" +msgstr "" +"annullare, inverti (rapidsvn), ritorna allo stato precedente, annulla le " +"modifiche della revisione" + +#. "A particular state of files and directories which was stored in the object database." +msgid "revision" +msgstr "revisione (TortoiseSVN)" + +#. "" +msgid "sign off" +msgstr "sign off, firma" + +#. "" +msgid "staging area" +msgstr "" +"area di preparazione, zona di preparazione, modifiche in preparazione? " +"modifiche in allestimento?" + +#. "" +msgid "status" +msgstr "stato" + +#. "A ref pointing to a tag or commit object" +msgid "tag [noun]" +msgstr "etichetta, etichettatura (TortoiseCVS)" + +#. "" +msgid "tag [verb]" +msgstr "etichettare" + +#. "A regular git branch that is used to follow changes from another repository." +msgid "tracking branch" +msgstr "" +"duplicato locale di ramo remoto, ramo in 'tracking', ramo inseguitore? ramo " +"di {inseguimento,allineamento,rilevamento,puntamento}?" + +#. "" +msgid "undo" +msgstr "annulla" + +#. "" +msgid "update" +msgstr "aggiornamento, aggiornare" + +#. "" +msgid "verify" +msgstr "verifica, verificare" + +#. "The tree of actual checked out files." +msgid "working copy, working tree" +msgstr "directory di lavoro, copia di lavoro" diff --git a/git-gui/po/glossary/txt-to-pot.sh b/git-gui/po/glossary/txt-to-pot.sh new file mode 100755 index 0000000000..49bf7c5365 --- /dev/null +++ b/git-gui/po/glossary/txt-to-pot.sh @@ -0,0 +1,48 @@ +#!/bin/sh +# This is a very, _very_, simple script to convert a tab-separated +# .txt file into a .pot/.po. +# Its not clever but it took me 2 minutes to write :) +# Michael Twomey <michael.twomey@ireland.sun.com> +# 23 March 2001 +# with slight GnuCash modifications by Christian Stimming <stimming@tuhh.de> +# 19 Aug 2001, 23 Jul 2007 + +#check args +if [ $# -eq 0 ] +then + cat <<! +Usage: `basename $0` git-gui-glossary.txt > git-gui-glossary.pot +! + exit 1; +fi + +GLOSSARY_CSV="$1"; + +if [ ! -f "$GLOSSARY_CSV" ] +then + echo "Can't find $GLOSSARY_CSV."; + exit 1; +fi + +cat <<! +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Free Software Foundation, Inc. +# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: `date +'%Y-%m-%d %H:%M%z'`\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: ENCODING\n" + +! + +#Yes this is the most simple awk script you've ever seen :) +awk -F'\t' '{if ($2 != "") print "#. "$2; print "msgid "$1; print "msgstr \"\"\n"}' \ +$GLOSSARY_CSV diff --git a/git-gui/po/glossary/zh_cn.po b/git-gui/po/glossary/zh_cn.po new file mode 100644 index 0000000000..158835b5c1 --- /dev/null +++ b/git-gui/po/glossary/zh_cn.po @@ -0,0 +1,170 @@ +# Translation of git-gui glossary to Simplified Chinese +# Copyright (C) 2007 Shawn Pearce, et al. +# This file is distributed under the same license as the git package. +# Xudong Guan <xudong.guan@gmail.com> and the zh-kernel.org mailing list, 2007 +# +msgid "" +msgstr "" +"Project-Id-Version: git-gui glossary\n" +"PO-Revision-Date: 2007-07-23 22:07+0200\n" +"Last-Translator: Xudong Guan <xudong.guan@gmail.com>\n" +"Language-Team: Simplified Chinese \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#. "English Definition (Dear translator: This file will never be visible to the user! It should only serve as a tool for you, the translator. Nothing more.)" +msgid "" +"English Term (Dear translator: This file will never be visible to the user!)" +msgstr "注:这个文件是为了帮助翻译人员统一åè¯æœ¯è¯ã€‚最终用户ä¸ä¼šå…³å¿ƒè¿™ä¸ªæ–‡ä»¶ã€‚" + +#. "" +#. amend指用户修改最近一次commitçš„æ“作,修订?修改?修æ£ï¼Ÿ +#. [WANG Cong]: æ ¹æ®æˆ‘的了解,这个è¯ä¼¼ä¹Žç¿»è¯‘æˆâ€œä¿®è®¢â€å¤šä¸€äº›ã€‚“修æ£â€ä¹Ÿå¯ä»¥ï¼Œâ€œä¿®æ”¹â€å†æ¬¡ä¹‹ã€‚ +#. [ZHANG Le]: 修订,感觉一般指对一些大型出版物的大规模å‡çº§ï¼Œæ¯”如修订新åŽå—å…¸ +# ä¿®æ£ï¼Œå…¶å®žæ¯æ¬¡amend的结果也ä¸ä¸€å®šå°±æ˜¯æœ€åŽç»“果,说ä¸å®šè¿˜éœ€è¦ä¿®æ”¹ã€‚æ‰€ä»¥ä¸ +# 如就å«ä¿®æ”¹ +msgid "amend" +msgstr "修订" + +#. "" +#. git annotate 文件å:用æ¥æ ‡æ³¨æ–‡ä»¶çš„æ¯ä¸€è¡Œåœ¨ä»€ä¹ˆæ—¶å€™è¢«è°æœ€åŽä¿®æ”¹ã€‚ +#. [WANG Cong]: "æ ‡è®°"一般是mark。;) +#. [ZHANG Le]: æ ‡æ³¨ï¼Œæˆ–è€…å¹²è„†ç”¨åŽŸæ„:注解,或注释 +msgid "annotate" +msgstr "æ ‡æ³¨" + +#. "A 'branch' is an active line of development." +msgid "branch [noun]" +msgstr "分支" + +#. "" +msgid "branch [verb]" +msgstr "建立分支" + +#. "" +#. [WANG Cong]: 网上有人翻译æˆâ€œæ£€å‡ºâ€ï¼Œæˆ‘感觉更好一些,毕竟把checkçš„æ„æ€ç¿»è¯‘出æ¥äº†ã€‚ +#. [ZHNAG Le]: æå–å§ï¼Œæå–分支ï¼ç‰ˆæœ¬ +#. [rae l]: ç¾å‡ºã€‚subversion软件ä¸çš„大多è¯æ±‡å·²æœ‰ç¿»è¯‘,既然git与subversionåŒæ˜¯SCM管ç†ï¼Œå¯ä»¥å‚考åŒç±»è½¯ä»¶çš„翻译也ä¸é”™ã€‚ +msgid "checkout [noun]" +msgstr "ç¾å‡º" + +#. "The action of updating the working tree to a revision which was stored in the object database." +msgid "checkout [verb]" +msgstr "ç¾å‡º" + +#. "A single point in the git history." +msgid "commit [noun]" +msgstr "æ交" + +#. "The action of storing a new snapshot of the project's state in the git history." +msgid "commit [verb]" +msgstr "æ交" + +#. "" +#. 差异?差别? +#. [ZHANG Le]: ä¸ªäººæ„Ÿè§‰å·®åˆ«æ›´åŠ ä¸æ€§ä¸€äº› +msgid "diff [noun]" +msgstr "差别" + +#. "" +msgid "diff [verb]" +msgstr "比较" + +#. "A fast-forward is a special type of merge where you have a revision and you are merging another branch's changes that happen to be a descendant of what you have." +msgid "fast forward merge" +msgstr "å¿«è¿›å¼åˆå¹¶" + +#. "Fetching a branch means to get the branch's head from a remote repository, to find out which objects are missing from the local object database, and to get them, too." +#. 获å–?å–得?下载?更新?注æ„å’Œupdate的区分 +msgid "fetch" +msgstr "获å–" + +#. "A collection of files. The index is a stored version of your working tree." +#. index是working treeå’Œrepositoryä¹‹é—´çš„ç¼“å˜ +msgid "index (in git-gui: staging area)" +msgstr "工作缓å˜ï¼Ÿ" + +#. "A successful merge results in the creation of a new commit representing the result of the merge." +msgid "merge [noun]" +msgstr "åˆå¹¶" + +#. "To bring the contents of another branch into the current branch." +msgid "merge [verb]" +msgstr "åˆå¹¶" + +#. "" +#. message是指commitä¸çš„æ–‡å—ä¿¡æ¯ +msgid "message" +msgstr "æè¿°" + +#. "Pulling a branch means to fetch it and merge it." +msgid "pull" +msgstr "获å–+åˆå¹¶" + +#. "Pushing a branch means to get the branch's head ref from a remote repository, and ... (well, can someone please explain it for mere mortals?)" +msgid "push" +msgstr "推入" + +#. "" +msgid "redo" +msgstr "é‡åš" + +#. "A collection of refs (?) together with an object database containing all objects which are reachable from the refs... (oops, you've lost me here. Again, please an explanation for mere mortals?)" +msgid "repository" +msgstr "仓库" + +#. "" +msgid "reset" +msgstr "é‡ç½®" + +#. "" +msgid "revert" +msgstr "æ¢å¤" + +#. "A particular state of files and directories which was stored in the object database." +msgid "revision" +msgstr "版本" + +#. "" +msgid "sign off" +msgstr "ç¾å" + +#. "" +#. 似乎是git-gui里é¢æ˜¾ç¤ºçš„本次æ交的文件清å•åŒºåŸŸ +msgid "staging area" +msgstr "æ交暂å˜åŒº" + +#. "" +msgid "status" +msgstr "状æ€" + +#. "A ref pointing to a tag or commit object" +msgid "tag [noun]" +msgstr "æ ‡ç¾" + +#. "" +msgid "tag [verb]" +msgstr "æ·»åŠ æ ‡ç¾" + +#. "A regular git branch that is used to follow changes from another repository." +msgid "tracking branch" +msgstr "跟踪分支" + +#. "" +msgid "undo" +msgstr "撤销" + +#. "" +msgid "update" +msgstr "更新。注æ„å’Œfetch的区分" + +#. "" +msgid "verify" +msgstr "验è¯" + +#. "The tree of actual checked out files." +#. "工作副本?工作区域?工作目录" +#. [LI Yang]: 当å‰å‰¯æœ¬ï¼Œ 当å‰æºç æ ‘ï¼Ÿ +msgid "working copy, working tree" +msgstr "工作副本,工作æºç æ ‘" diff --git a/git-gui/po/hu.po b/git-gui/po/hu.po new file mode 100644 index 0000000000..627c05eb99 --- /dev/null +++ b/git-gui/po/hu.po @@ -0,0 +1,1937 @@ +# Hungarian translations for git-gui-i package. +# Copyright (C) 2007 THE git-gui-i'S COPYRIGHT HOLDER +# This file is distributed under the same license as the git-gui-i package. +# Miklos Vajna <vmiklos@frugalware.org>, 2007. +# +msgid "" +msgstr "" +"Project-Id-Version: git-gui-i 18n\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2007-11-24 10:36+0100\n" +"PO-Revision-Date: 2007-12-04 01:15+0100\n" +"Last-Translator: Miklos Vajna <vmiklos@frugalware.org>\n" +"Language-Team: Hungarian\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" + +#: git-gui.sh:41 git-gui.sh:604 git-gui.sh:618 git-gui.sh:631 git-gui.sh:714 +#: git-gui.sh:733 +msgid "git-gui: fatal error" +msgstr "git-gui: végzetes hiba" + +#: git-gui.sh:565 +#, tcl-format +msgid "Invalid font specified in %s:" +msgstr "Érvénytelen font lett megadva itt: %s:" + +#: git-gui.sh:590 +msgid "Main Font" +msgstr "FÅ‘ betűtÃpus" + +#: git-gui.sh:591 +msgid "Diff/Console Font" +msgstr "Diff/konzol betűtÃpus" + +#: git-gui.sh:605 +msgid "Cannot find git in PATH." +msgstr "A git nem található a PATH-ban." + +#: git-gui.sh:632 +msgid "Cannot parse Git version string:" +msgstr "Nem értelmezhetÅ‘ a Git verzió sztring:" + +#: git-gui.sh:650 +#, tcl-format +msgid "" +"Git version cannot be determined.\n" +"\n" +"%s claims it is version '%s'.\n" +"\n" +"%s requires at least Git 1.5.0 or later.\n" +"\n" +"Assume '%s' is version 1.5.0?\n" +msgstr "" +"Nem állÃpÃtható meg a Git verziója.\n" +"\n" +"A(z) %s szerint a verzió '%s'.\n" +"\n" +"A(z) %s a Git 1.5.0 vagy késÅ‘bbi verzióját igényli.\n" +"\n" +"Feltételezhetjük, hogy a(z) '%s' verziója legalább 1.5.0?\n" + +#: git-gui.sh:888 +msgid "Git directory not found:" +msgstr "A Git könyvtár nem található:" + +#: git-gui.sh:895 +msgid "Cannot move to top of working directory:" +msgstr "Nem lehet a munkakönyvtár tetejére lépni:" + +#: git-gui.sh:902 +msgid "Cannot use funny .git directory:" +msgstr "Nem használható vicces .git könyvtár:" + +#: git-gui.sh:907 +msgid "No working directory" +msgstr "Nincs munkakönyvtár" + +#: git-gui.sh:1054 +msgid "Refreshing file status..." +msgstr "A fájlok státuszának frissÃtése..." + +#: git-gui.sh:1119 +msgid "Scanning for modified files ..." +msgstr "MódosÃtott fájlok keresése ..." + +#: git-gui.sh:1294 lib/browser.tcl:245 +msgid "Ready." +msgstr "Kész." + +#: git-gui.sh:1560 +msgid "Unmodified" +msgstr "Nem módosÃtott" + +#: git-gui.sh:1562 +msgid "Modified, not staged" +msgstr "MódosÃtott, de nem kiválasztott" + +#: git-gui.sh:1563 git-gui.sh:1568 +msgid "Staged for commit" +msgstr "Kiválasztva commitolásra" + +#: git-gui.sh:1564 git-gui.sh:1569 +msgid "Portions staged for commit" +msgstr "Részek kiválasztva commitolásra" + +#: git-gui.sh:1565 git-gui.sh:1570 +msgid "Staged for commit, missing" +msgstr "Kiválasztva commitolásra, hiányzó" + +#: git-gui.sh:1567 +msgid "Untracked, not staged" +msgstr "Nem követett, nem kiválasztott" + +#: git-gui.sh:1572 +msgid "Missing" +msgstr "Hiányzó" + +#: git-gui.sh:1573 +msgid "Staged for removal" +msgstr "Kiválasztva eltávolÃtásra" + +#: git-gui.sh:1574 +msgid "Staged for removal, still present" +msgstr "Kiválasztva eltávolÃtásra, jelenleg is elérhetÅ‘" + +#: git-gui.sh:1576 git-gui.sh:1577 git-gui.sh:1578 git-gui.sh:1579 +msgid "Requires merge resolution" +msgstr "Merge feloldás szükséges" + +#: git-gui.sh:1614 +msgid "Starting gitk... please wait..." +msgstr "A gitk indÃtása... várjunk..." + +#: git-gui.sh:1623 +#, tcl-format +msgid "" +"Unable to start gitk:\n" +"\n" +"%s does not exist" +msgstr "" +"A gitk indÃtása sikertelen:\n" +"\n" +"A(z) %s nem létezik" + +#: git-gui.sh:1823 lib/choose_repository.tcl:35 +msgid "Repository" +msgstr "Repó" + +#: git-gui.sh:1824 +msgid "Edit" +msgstr "Szerkesztés" + +#: git-gui.sh:1826 lib/choose_rev.tcl:560 +msgid "Branch" +msgstr "Branch" + +#: git-gui.sh:1829 lib/choose_rev.tcl:547 +msgid "Commit@@noun" +msgstr "Commit@@fÅ‘név" + +#: git-gui.sh:1832 lib/merge.tcl:121 lib/merge.tcl:150 lib/merge.tcl:168 +msgid "Merge" +msgstr "Merge" + +#: git-gui.sh:1833 lib/choose_rev.tcl:556 +msgid "Remote" +msgstr "Távoli" + +#: git-gui.sh:1842 +msgid "Browse Current Branch's Files" +msgstr "A jelenlegi branch fájljainak böngészése" + +#: git-gui.sh:1846 +msgid "Browse Branch Files..." +msgstr "A branch fájljainak böngészése..." + +#: git-gui.sh:1851 +msgid "Visualize Current Branch's History" +msgstr "A jelenlegi branch történetének vizualizálása" + +#: git-gui.sh:1855 +msgid "Visualize All Branch History" +msgstr "Az összes branch történetének vizualizálása" + +#: git-gui.sh:1862 +#, tcl-format +msgid "Browse %s's Files" +msgstr "A(z) %s branch fájljainak böngészése" + +#: git-gui.sh:1864 +#, tcl-format +msgid "Visualize %s's History" +msgstr "A(z) %s branch történetének vizualizálása" + +#: git-gui.sh:1869 lib/database.tcl:27 lib/database.tcl:67 +msgid "Database Statistics" +msgstr "Adatbázis statisztikák" + +#: git-gui.sh:1872 lib/database.tcl:34 +msgid "Compress Database" +msgstr "Adatbázis tömörÃtése" + +#: git-gui.sh:1875 +msgid "Verify Database" +msgstr "Adatbázis ellenÅ‘rzése" + +#: git-gui.sh:1882 git-gui.sh:1886 git-gui.sh:1890 lib/shortcut.tcl:7 +#: lib/shortcut.tcl:39 lib/shortcut.tcl:71 +msgid "Create Desktop Icon" +msgstr "Asztal ikon létrehozása" + +#: git-gui.sh:1895 lib/choose_repository.tcl:176 lib/choose_repository.tcl:184 +msgid "Quit" +msgstr "Kilépés" + +#: git-gui.sh:1902 +msgid "Undo" +msgstr "Visszavonás" + +#: git-gui.sh:1905 +msgid "Redo" +msgstr "Mégis" + +#: git-gui.sh:1909 git-gui.sh:2403 +msgid "Cut" +msgstr "Kivágás" + +#: git-gui.sh:1912 git-gui.sh:2406 git-gui.sh:2477 git-gui.sh:2549 +#: lib/console.tcl:67 +msgid "Copy" +msgstr "Másolás" + +#: git-gui.sh:1915 git-gui.sh:2409 +msgid "Paste" +msgstr "Beillesztés" + +#: git-gui.sh:1918 git-gui.sh:2412 lib/branch_delete.tcl:26 +#: lib/remote_branch_delete.tcl:38 +msgid "Delete" +msgstr "Törlés" + +#: git-gui.sh:1922 git-gui.sh:2416 git-gui.sh:2553 lib/console.tcl:69 +msgid "Select All" +msgstr "Mindent kiválaszt" + +#: git-gui.sh:1931 +msgid "Create..." +msgstr "Létrehozás..." + +#: git-gui.sh:1937 +msgid "Checkout..." +msgstr "Checkout..." + +#: git-gui.sh:1943 +msgid "Rename..." +msgstr "Ãtnevezés..." + +#: git-gui.sh:1948 git-gui.sh:2048 +msgid "Delete..." +msgstr "Törlés..." + +#: git-gui.sh:1953 +msgid "Reset..." +msgstr "VisszaállÃtás..." + +#: git-gui.sh:1965 git-gui.sh:2350 +msgid "New Commit" +msgstr "Új commit" + +#: git-gui.sh:1973 git-gui.sh:2357 +msgid "Amend Last Commit" +msgstr "Utolsó commit javÃtása" + +#: git-gui.sh:1982 git-gui.sh:2317 lib/remote_branch_delete.tcl:99 +msgid "Rescan" +msgstr "Keresés újra" + +#: git-gui.sh:1988 +msgid "Stage To Commit" +msgstr "Kiválasztás commitolásra" + +#: git-gui.sh:1994 +msgid "Stage Changed Files To Commit" +msgstr "MódosÃtott fájlok kiválasztása commitolásra" + +#: git-gui.sh:2000 +msgid "Unstage From Commit" +msgstr "Commitba való kiválasztás visszavonása" + +#: git-gui.sh:2005 lib/index.tcl:393 +msgid "Revert Changes" +msgstr "Változtatások visszaállÃtása" + +#: git-gui.sh:2012 git-gui.sh:2329 git-gui.sh:2427 +msgid "Sign Off" +msgstr "AláÃr" + +#: git-gui.sh:2016 git-gui.sh:2333 +msgid "Commit@@verb" +msgstr "Commit@@ige" + +#: git-gui.sh:2027 +msgid "Local Merge..." +msgstr "Helyi merge..." + +#: git-gui.sh:2032 +msgid "Abort Merge..." +msgstr "Merge megszakÃtása..." + +#: git-gui.sh:2044 +msgid "Push..." +msgstr "Push..." + +#: git-gui.sh:2055 lib/choose_repository.tcl:40 +msgid "Apple" +msgstr "Apple" + +#: git-gui.sh:2058 git-gui.sh:2080 lib/about.tcl:13 +#: lib/choose_repository.tcl:43 lib/choose_repository.tcl:49 +#, tcl-format +msgid "About %s" +msgstr "Névjegy: %s" + +#: git-gui.sh:2062 +msgid "Preferences..." +msgstr "BeállÃtások..." + +#: git-gui.sh:2070 git-gui.sh:2595 +msgid "Options..." +msgstr "Opciók..." + +#: git-gui.sh:2076 lib/choose_repository.tcl:46 +msgid "Help" +msgstr "SegÃtség" + +#: git-gui.sh:2117 +msgid "Online Documentation" +msgstr "Online dokumentáció" + +#: git-gui.sh:2201 +#, tcl-format +msgid "fatal: cannot stat path %s: No such file or directory" +msgstr "végzetes hiba: nem érhetÅ‘ el a(z) %s útvonal: Nincs ilyen fájl vagy könyvtár" + +#: git-gui.sh:2234 +msgid "Current Branch:" +msgstr "Jelenlegi branch:" + +#: git-gui.sh:2255 +msgid "Staged Changes (Will Commit)" +msgstr "Kiválasztott változtatások (commitolva lesz)" + +#: git-gui.sh:2274 +msgid "Unstaged Changes" +msgstr "Kiválasztatlan változtatások" + +#: git-gui.sh:2323 +msgid "Stage Changed" +msgstr "Változtatások kiválasztása" + +#: git-gui.sh:2339 lib/transport.tcl:93 lib/transport.tcl:182 +msgid "Push" +msgstr "Push" + +#: git-gui.sh:2369 +msgid "Initial Commit Message:" +msgstr "Kezdeti commit üzenet:" + +#: git-gui.sh:2370 +msgid "Amended Commit Message:" +msgstr "JavÃtó commit üzenet:" + +#: git-gui.sh:2371 +msgid "Amended Initial Commit Message:" +msgstr "Kezdeti javÃtó commit üzenet:" + +#: git-gui.sh:2372 +msgid "Amended Merge Commit Message:" +msgstr "JavÃtó merge commit üzenet:" + +#: git-gui.sh:2373 +msgid "Merge Commit Message:" +msgstr "Merge commit üzenet:" + +#: git-gui.sh:2374 +msgid "Commit Message:" +msgstr "Commit üzenet:" + +#: git-gui.sh:2419 git-gui.sh:2557 lib/console.tcl:71 +msgid "Copy All" +msgstr "Összes másolása" + +#: git-gui.sh:2443 lib/blame.tcl:104 +msgid "File:" +msgstr "Fájl:" + +#: git-gui.sh:2545 +msgid "Refresh" +msgstr "FrissÃtés" + +#: git-gui.sh:2566 +msgid "Apply/Reverse Hunk" +msgstr "Hunk alkalmazása/visszaállÃtása" + +#: git-gui.sh:2572 +msgid "Decrease Font Size" +msgstr "Font méret csökkentése" + +#: git-gui.sh:2576 +msgid "Increase Font Size" +msgstr "Fönt méret növelése" + +#: git-gui.sh:2581 +msgid "Show Less Context" +msgstr "Kevesebb környezet mutatása" + +#: git-gui.sh:2588 +msgid "Show More Context" +msgstr "Több környezet mutatása" + +#: git-gui.sh:2602 +msgid "Unstage Hunk From Commit" +msgstr "Hunk törlése commitból" + +#: git-gui.sh:2604 +msgid "Stage Hunk For Commit" +msgstr "Hunk kiválasztása commitba" + +#: git-gui.sh:2623 +msgid "Initializing..." +msgstr "Inicializálás..." + +#: git-gui.sh:2718 +#, tcl-format +msgid "" +"Possible environment issues exist.\n" +"\n" +"The following environment variables are probably\n" +"going to be ignored by any Git subprocess run\n" +"by %s:\n" +"\n" +msgstr "" +"Lehetséges, hogy környezeti problémák vannak.\n" +"\n" +"A következÅ‘ környezeti változók valószÃnűleg\n" +"figyelmen kÃvül lesznek hagyva a(z) %s által\n" +"indÃtott folyamatok által:\n" +"\n" + +#: git-gui.sh:2748 +msgid "" +"\n" +"This is due to a known issue with the\n" +"Tcl binary distributed by Cygwin." +msgstr "" +"\n" +"Ez a Cygwin által terjesztett Tcl binárisban\n" +"lévÅ‘ ismert hiba miatt van." + +#: git-gui.sh:2753 +#, tcl-format +msgid "" +"\n" +"\n" +"A good replacement for %s\n" +"is placing values for the user.name and\n" +"user.email settings into your personal\n" +"~/.gitconfig file.\n" +msgstr "" +"\n" +"\n" +"Egy jó helyettesÃtés a(z) %s számára\n" +"a user.name és user.email beállÃtások\n" +"elhelyezése a személyes\n" +"~/.gitconfig fájlba.\n" + +#: lib/about.tcl:25 +msgid "git-gui - a graphical user interface for Git." +msgstr "git-gui - egy grafikus felület a Githez." + +#: lib/blame.tcl:77 +msgid "File Viewer" +msgstr "Fájl nézÅ‘" + +#: lib/blame.tcl:81 +msgid "Commit:" +msgstr "Commit:" + +#: lib/blame.tcl:249 +msgid "Copy Commit" +msgstr "Commit másolása" + +#: lib/blame.tcl:369 +#, tcl-format +msgid "Reading %s..." +msgstr "A(z) %s olvasása..." + +#: lib/blame.tcl:473 +msgid "Loading copy/move tracking annotations..." +msgstr "A másolást/átnevezést követÅ‘ annotációk betöltése..." + +#: lib/blame.tcl:493 +msgid "lines annotated" +msgstr "sor annotálva" + +#: lib/blame.tcl:674 +msgid "Loading original location annotations..." +msgstr "Az eredeti hely annotációk betöltése..." + +#: lib/blame.tcl:677 +msgid "Annotation complete." +msgstr "Az annotáció kész." + +#: lib/blame.tcl:731 +msgid "Loading annotation..." +msgstr "Az annotáció betöltése..." + +#: lib/blame.tcl:787 +msgid "Author:" +msgstr "SzerzÅ‘:" + +#: lib/blame.tcl:791 +msgid "Committer:" +msgstr "Commiter:" + +#: lib/blame.tcl:796 +msgid "Original File:" +msgstr "Eredeti fájl:" + +#: lib/blame.tcl:910 +msgid "Originally By:" +msgstr "Eredeti szerzÅ‘:" + +#: lib/blame.tcl:916 +msgid "In File:" +msgstr "Ebben a fájlban:" + +#: lib/blame.tcl:921 +msgid "Copied Or Moved Here By:" +msgstr "Ide másolta vagy helyezte:" + +#: lib/branch_checkout.tcl:14 lib/branch_checkout.tcl:19 +msgid "Checkout Branch" +msgstr "Branch checkoutolása" + +#: lib/branch_checkout.tcl:23 +msgid "Checkout" +msgstr "Checkout" + +#: lib/branch_checkout.tcl:27 lib/branch_create.tcl:35 +#: lib/branch_delete.tcl:32 lib/branch_rename.tcl:30 lib/browser.tcl:281 +#: lib/checkout_op.tcl:522 lib/choose_font.tcl:43 lib/merge.tcl:172 +#: lib/option.tcl:90 lib/remote_branch_delete.tcl:42 lib/transport.tcl:97 +msgid "Cancel" +msgstr "Mégsem" + +#: lib/branch_checkout.tcl:32 lib/browser.tcl:286 +msgid "Revision" +msgstr "RevÃzió" + +#: lib/branch_checkout.tcl:36 lib/branch_create.tcl:69 lib/option.tcl:202 +msgid "Options" +msgstr "Opciók" + +#: lib/branch_checkout.tcl:39 lib/branch_create.tcl:92 +msgid "Fetch Tracking Branch" +msgstr "KövetÅ‘ branch letöltése" + +#: lib/branch_checkout.tcl:44 +msgid "Detach From Local Branch" +msgstr "Helyi branch leválasztása" + +#: lib/branch_create.tcl:22 +msgid "Create Branch" +msgstr "Branch létrehozása" + +#: lib/branch_create.tcl:27 +msgid "Create New Branch" +msgstr "Új branch létrehozása" + +#: lib/branch_create.tcl:31 lib/choose_repository.tcl:375 +msgid "Create" +msgstr "Létrehozás" + +#: lib/branch_create.tcl:40 +msgid "Branch Name" +msgstr "Branch neve" + +#: lib/branch_create.tcl:43 +msgid "Name:" +msgstr "Név:" + +#: lib/branch_create.tcl:58 +msgid "Match Tracking Branch Name" +msgstr "EgyeztetendÅ‘ követési branch név" + +#: lib/branch_create.tcl:66 +msgid "Starting Revision" +msgstr "A következÅ‘ revÃziótól" + +#: lib/branch_create.tcl:72 +msgid "Update Existing Branch:" +msgstr "LétezÅ‘ branch frissÃtése" + +#: lib/branch_create.tcl:75 +msgid "No" +msgstr "Nem" + +#: lib/branch_create.tcl:80 +msgid "Fast Forward Only" +msgstr "Csak fast forward" + +#: lib/branch_create.tcl:85 lib/checkout_op.tcl:514 +msgid "Reset" +msgstr "VisszaállÃtás" + +#: lib/branch_create.tcl:97 +msgid "Checkout After Creation" +msgstr "Checkout létrehozás után" + +#: lib/branch_create.tcl:131 +msgid "Please select a tracking branch." +msgstr "Válasszunk ki egy követÅ‘ branchet." + +#: lib/branch_create.tcl:140 +#, tcl-format +msgid "Tracking branch %s is not a branch in the remote repository." +msgstr "A(z) %s követÅ‘ branch nem branch a távoli repóban." + +#: lib/branch_create.tcl:153 lib/branch_rename.tcl:86 +msgid "Please supply a branch name." +msgstr "Adjunk megy egy branch nevet." + +#: lib/branch_create.tcl:164 lib/branch_rename.tcl:106 +#, tcl-format +msgid "'%s' is not an acceptable branch name." +msgstr "A(z) '%s' nem egy elfogadható branch név." + +#: lib/branch_delete.tcl:15 +msgid "Delete Branch" +msgstr "Branch törlése" + +#: lib/branch_delete.tcl:20 +msgid "Delete Local Branch" +msgstr "Helyi branch törlése" + +#: lib/branch_delete.tcl:37 +msgid "Local Branches" +msgstr "Helyi branchek" + +#: lib/branch_delete.tcl:52 +msgid "Delete Only If Merged Into" +msgstr "Csak már merge-ölt törlése" + +#: lib/branch_delete.tcl:54 +msgid "Always (Do not perform merge test.)" +msgstr "Mindig (Ne legyen merge teszt.)" + +#: lib/branch_delete.tcl:103 +#, tcl-format +msgid "The following branches are not completely merged into %s:" +msgstr "A következÅ‘ branchek nem teljesen lettek merge-ölve ebbe: %s:" + +#: lib/branch_delete.tcl:115 +msgid "" +"Recovering deleted branches is difficult. \n" +"\n" +" Delete the selected branches?" +msgstr "" +"A törölt branchek visszaállÃtása bonyolult. \n" +"\n" +" Biztosan törli a kiválasztott brancheket?" + +#: lib/branch_delete.tcl:141 +#, tcl-format +msgid "" +"Failed to delete branches:\n" +"%s" +msgstr "" +"Nem sikerült törölni a következÅ‘ brancheket:\n" +"%s" + +#: lib/branch_rename.tcl:14 lib/branch_rename.tcl:22 +msgid "Rename Branch" +msgstr "Branch átnevezése" + +#: lib/branch_rename.tcl:26 +msgid "Rename" +msgstr "Ãtnevezés" + +#: lib/branch_rename.tcl:36 +msgid "Branch:" +msgstr "Branch:" + +#: lib/branch_rename.tcl:39 +msgid "New Name:" +msgstr "Új név:" + +#: lib/branch_rename.tcl:75 +msgid "Please select a branch to rename." +msgstr "Válasszunk ki egy átnevezendÅ‘ branchet." + +#: lib/branch_rename.tcl:96 lib/checkout_op.tcl:179 +#, tcl-format +msgid "Branch '%s' already exists." +msgstr "A(z) '%s' branch már létezik." + +#: lib/branch_rename.tcl:117 +#, tcl-format +msgid "Failed to rename '%s'." +msgstr "Nem sikerült átnevezni: '%s'." + +#: lib/browser.tcl:17 +msgid "Starting..." +msgstr "IndÃtás..." + +#: lib/browser.tcl:26 +msgid "File Browser" +msgstr "Fájl böngészÅ‘" + +#: lib/browser.tcl:125 lib/browser.tcl:142 +#, tcl-format +msgid "Loading %s..." +msgstr "A(z) %s betöltése..." + +#: lib/browser.tcl:186 +msgid "[Up To Parent]" +msgstr "[Fel a szülÅ‘höz]" + +#: lib/browser.tcl:266 lib/browser.tcl:272 +msgid "Browse Branch Files" +msgstr "A branch fájljainak böngészése" + +#: lib/browser.tcl:277 lib/choose_repository.tcl:391 +#: lib/choose_repository.tcl:482 lib/choose_repository.tcl:492 +#: lib/choose_repository.tcl:989 +msgid "Browse" +msgstr "Böngészés" + +#: lib/checkout_op.tcl:79 +#, tcl-format +msgid "Fetching %s from %s" +msgstr "A(z) %s letöltése innen: %s" + +#: lib/checkout_op.tcl:127 +#, tcl-format +msgid "fatal: Cannot resolve %s" +msgstr "végzetes: Nem lehet feloldani a következÅ‘t: %s" + +#: lib/checkout_op.tcl:140 lib/console.tcl:79 lib/database.tcl:31 +msgid "Close" +msgstr "Bezárás" + +#: lib/checkout_op.tcl:169 +#, tcl-format +msgid "Branch '%s' does not exist." +msgstr "A(z) '%s' branch nem létezik." + +#: lib/checkout_op.tcl:206 +#, tcl-format +msgid "" +"Branch '%s' already exists.\n" +"\n" +"It cannot fast-forward to %s.\n" +"A merge is required." +msgstr "" +"A(z) '%s' branch már létezik.\n" +"\n" +"Nem lehet fast-forwardolni a következÅ‘höz: %s.\n" +"Egy merge szükséges." + +#: lib/checkout_op.tcl:220 +#, tcl-format +msgid "Merge strategy '%s' not supported." +msgstr "A(z) '%s' merge strategy nem támogatott." + +#: lib/checkout_op.tcl:239 +#, tcl-format +msgid "Failed to update '%s'." +msgstr "Nem sikerült frissÃteni a következÅ‘t: '%s'." + +#: lib/checkout_op.tcl:251 +msgid "Staging area (index) is already locked." +msgstr "A kiválasztási terület (index) már zárolva van." + +#: lib/checkout_op.tcl:266 +msgid "" +"Last scanned state does not match repository state.\n" +"\n" +"Another Git program has modified this repository since the last scan. A " +"rescan must be performed before the current branch can be changed.\n" +"\n" +"The rescan will be automatically started now.\n" +msgstr "" +"Az utolsó keresési állapot nem egyezik meg a repó állpotával.\n" +"\n" +"Egy másik Git program módosÃtotta ezt a repót az utolsó keresés óta. Egy " +"újrakeresés mindenképpen szükséges mielÅ‘tt a jelenlegi branchet módosÃtani " +"lehetne.\n" +"\n" +"Az újrakeresés most automatikusan el fog indulni.\n" + +#: lib/checkout_op.tcl:322 +#, tcl-format +msgid "Updating working directory to '%s'..." +msgstr "A munkkönyvtár frissiÃtése a következÅ‘re: '%s'..." + +#: lib/checkout_op.tcl:353 +#, tcl-format +msgid "Aborted checkout of '%s' (file level merging is required)." +msgstr "A(z) '%s' checkoutja megszakÃtva (fájlszintű merge-ölés szükséges)." + +#: lib/checkout_op.tcl:354 +msgid "File level merge required." +msgstr "Fájlszintű merge-ölés szükséges." + +#: lib/checkout_op.tcl:358 +#, tcl-format +msgid "Staying on branch '%s'." +msgstr "Jelenleg a(z) '%s' branchen." + +#: lib/checkout_op.tcl:429 +msgid "" +"You are no longer on a local branch.\n" +"\n" +"If you wanted to be on a branch, create one now starting from 'This Detached " +"Checkout'." +msgstr "" +"Már nem egy helyi branchen vagyunk.\n" +"\n" +"Ha egy branchen szeretnénk lenni, hozzunk létre egyet az 'Ez a leválasztott " +"checkout'-ból." + +#: lib/checkout_op.tcl:446 +#, tcl-format +msgid "Checked out '%s'." +msgstr "'%s' kifejtve." + +#: lib/checkout_op.tcl:478 +#, tcl-format +msgid "Resetting '%s' to '%s' will lose the following commits:" +msgstr "" +"A(z) '%s' -> '%s' visszaállÃtás a következÅ‘ commitok elvesztését jelenti:" + +#: lib/checkout_op.tcl:500 +msgid "Recovering lost commits may not be easy." +msgstr "Az elveszett commitok helyreállÃtása nem biztos, hogy egyszerű." + +#: lib/checkout_op.tcl:505 +#, tcl-format +msgid "Reset '%s'?" +msgstr "VisszaállÃtjuk a következÅ‘t: '%s'?" + +#: lib/checkout_op.tcl:510 lib/merge.tcl:164 +msgid "Visualize" +msgstr "Vizualizálás" + +#: lib/checkout_op.tcl:578 +#, tcl-format +msgid "" +"Failed to set current branch.\n" +"\n" +"This working directory is only partially switched. We successfully updated " +"your files, but failed to update an internal Git file.\n" +"\n" +"This should not have occurred. %s will now close and give up." +msgstr "" +"Nem sikerült beállÃtani a jelenlegi branchet.\n" +"\n" +"A munkakönyvtár csak részben váltott át. A fájlok sikeresen frissÃtve " +"lettek, de nem sikerült frissÃteni egy belsÅ‘ Git fájlt.\n" +"\n" +"Ennek nem szabad megtörténnie. A(z) %s most kilép és feladja." + +#: lib/choose_font.tcl:39 +msgid "Select" +msgstr "Kiválaszt" + +#: lib/choose_font.tcl:53 +msgid "Font Family" +msgstr "Font család" + +#: lib/choose_font.tcl:73 +msgid "Font Size" +msgstr "Font méret" + +#: lib/choose_font.tcl:90 +msgid "Font Example" +msgstr "Font példa" + +#: lib/choose_font.tcl:101 +msgid "" +"This is example text.\n" +"If you like this text, it can be your font." +msgstr "" +"Ez egy példa szöveg.\n" +"Ha ez megfelel, ez lehet a betűtÃpus." + +#: lib/choose_repository.tcl:27 +msgid "Git Gui" +msgstr "Git Gui" + +#: lib/choose_repository.tcl:80 lib/choose_repository.tcl:380 +msgid "Create New Repository" +msgstr "Új repó létrehozása" + +#: lib/choose_repository.tcl:86 +msgid "New..." +msgstr "Új..." + +#: lib/choose_repository.tcl:93 lib/choose_repository.tcl:468 +msgid "Clone Existing Repository" +msgstr "LétezÅ‘ repó másolása" + +#: lib/choose_repository.tcl:99 +msgid "Clone..." +msgstr "Másolás..." + +#: lib/choose_repository.tcl:106 lib/choose_repository.tcl:978 +msgid "Open Existing Repository" +msgstr "LétezÅ‘ könyvtár megnyitása" + +#: lib/choose_repository.tcl:112 +msgid "Open..." +msgstr "Meggyitás..." + +#: lib/choose_repository.tcl:125 +msgid "Recent Repositories" +msgstr "Legutóbbi repók" + +#: lib/choose_repository.tcl:131 +msgid "Open Recent Repository:" +msgstr "Legutóbbi repók megnyitása:" + +#: lib/choose_repository.tcl:294 +#, tcl-format +msgid "Location %s already exists." +msgstr "A(z) '%s' hely már létezik." + +#: lib/choose_repository.tcl:300 lib/choose_repository.tcl:307 +#: lib/choose_repository.tcl:314 +#, tcl-format +msgid "Failed to create repository %s:" +msgstr "Nem sikerült letrehozni a(z) %s repót:" + +#: lib/choose_repository.tcl:385 lib/choose_repository.tcl:486 +msgid "Directory:" +msgstr "Könyvtár:" + +#: lib/choose_repository.tcl:415 lib/choose_repository.tcl:544 +#: lib/choose_repository.tcl:1013 +msgid "Git Repository" +msgstr "Git repó" + +#: lib/choose_repository.tcl:430 lib/choose_repository.tcl:437 +#, tcl-format +msgid "Directory %s already exists." +msgstr "A(z) '%s' könyvtár már létezik." + +#: lib/choose_repository.tcl:442 +#, tcl-format +msgid "File %s already exists." +msgstr "A(z) '%s' fájl már létezik." + +#: lib/choose_repository.tcl:463 +msgid "Clone" +msgstr "Bezárás" + +#: lib/choose_repository.tcl:476 +msgid "URL:" +msgstr "URL:" + +#: lib/choose_repository.tcl:496 +msgid "Clone Type:" +msgstr "Másolás tÃpusa:" + +#: lib/choose_repository.tcl:502 +msgid "Standard (Fast, Semi-Redundant, Hardlinks)" +msgstr "Ãltalános (Gyors, félig-redundáns, hardlinkek)" + +#: lib/choose_repository.tcl:508 +msgid "Full Copy (Slower, Redundant Backup)" +msgstr "Teljes másolás (Lassabb, redundáns biztonsági mentés)" + +#: lib/choose_repository.tcl:514 +msgid "Shared (Fastest, Not Recommended, No Backup)" +msgstr "Megosztott (Leggyorsabb, nem ajánlott, nincs mentés)" + +#: lib/choose_repository.tcl:550 lib/choose_repository.tcl:597 +#: lib/choose_repository.tcl:738 lib/choose_repository.tcl:808 +#: lib/choose_repository.tcl:1019 lib/choose_repository.tcl:1027 +#, tcl-format +msgid "Not a Git repository: %s" +msgstr "Nem Git repó: %s" + +#: lib/choose_repository.tcl:586 +msgid "Standard only available for local repository." +msgstr "A standard csak helyi repókra érhetÅ‘ el." + +#: lib/choose_repository.tcl:590 +msgid "Shared only available for local repository." +msgstr "A megosztott csak helyi repókra érhetÅ‘ el." + +#: lib/choose_repository.tcl:617 +msgid "Failed to configure origin" +msgstr "Nem sikerült beállÃtani az origint" + +#: lib/choose_repository.tcl:629 +msgid "Counting objects" +msgstr "Objektumok számolása" + +#: lib/choose_repository.tcl:630 +msgid "buckets" +msgstr "vödrök" + +#: lib/choose_repository.tcl:654 +#, tcl-format +msgid "Unable to copy objects/info/alternates: %s" +msgstr "Nem sikerült másolni az objects/info/alternates-t: %s" + +#: lib/choose_repository.tcl:690 +#, tcl-format +msgid "Nothing to clone from %s." +msgstr "Semmi másolni való nincs innen: %s" + +#: lib/choose_repository.tcl:692 lib/choose_repository.tcl:906 +#: lib/choose_repository.tcl:918 +msgid "The 'master' branch has not been initialized." +msgstr "A 'master' branch nincs inicializálva." + +#: lib/choose_repository.tcl:705 +msgid "Hardlinks are unavailable. Falling back to copying." +msgstr "Nem érhetÅ‘ek el hardlinkek. Másolás használata." + +#: lib/choose_repository.tcl:717 +#, tcl-format +msgid "Cloning from %s" +msgstr "Másolás innen: %s" + +#: lib/choose_repository.tcl:748 +msgid "Copying objects" +msgstr "Objektumok másolása" + +#: lib/choose_repository.tcl:749 +msgid "KiB" +msgstr "KiB" + +#: lib/choose_repository.tcl:773 +#, tcl-format +msgid "Unable to copy object: %s" +msgstr "Nem sikerült másolni az objektumot: %s" + +#: lib/choose_repository.tcl:783 +msgid "Linking objects" +msgstr "Objektumok összefűzése" + +#: lib/choose_repository.tcl:784 +msgid "objects" +msgstr "objektum" + +#: lib/choose_repository.tcl:792 +#, tcl-format +msgid "Unable to hardlink object: %s" +msgstr "Nem sikerült hardlinkelni az objektumot: %s" + +#: lib/choose_repository.tcl:847 +msgid "Cannot fetch branches and objects. See console output for details." +msgstr "Nem sikerült letölteni a branch-eket és az objektumokat. BÅ‘vebben a konzolos kimenetben." + +#: lib/choose_repository.tcl:858 +msgid "Cannot fetch tags. See console output for details." +msgstr "Nem sikerült letölteni a tageket. BÅ‘vebben a konzolos kimenetben." + +#: lib/choose_repository.tcl:882 +msgid "Cannot determine HEAD. See console output for details." +msgstr "Nem sikerült megállapÃtani a HEAD-et. BÅ‘vebben a konzolos kimenetben." + +#: lib/choose_repository.tcl:891 +#, tcl-format +msgid "Unable to cleanup %s" +msgstr "Nem sikerült tiszÃtani: %s." + +#: lib/choose_repository.tcl:897 +msgid "Clone failed." +msgstr "A másolás nem sikerült." + +#: lib/choose_repository.tcl:904 +msgid "No default branch obtained." +msgstr "Nincs alapértelmezett branch." + +#: lib/choose_repository.tcl:915 +#, tcl-format +msgid "Cannot resolve %s as a commit." +msgstr "Nem sikerült felöldani a(z) %s objektumot commitként." + +#: lib/choose_repository.tcl:927 +msgid "Creating working directory" +msgstr "Munkakönyvtár létrehozása" + +#: lib/choose_repository.tcl:928 lib/index.tcl:65 lib/index.tcl:127 +#: lib/index.tcl:193 +msgid "files" +msgstr "fájl" + +#: lib/choose_repository.tcl:957 +msgid "Initial file checkout failed." +msgstr "A kezdeti fájl-kibontás sikertelen." + +#: lib/choose_repository.tcl:973 +msgid "Open" +msgstr "Megnyitás" + +#: lib/choose_repository.tcl:983 +msgid "Repository:" +msgstr "Repó:" + +#: lib/choose_repository.tcl:1033 +#, tcl-format +msgid "Failed to open repository %s:" +msgstr "Nem sikerült megnyitni a(z) %s repót:" + +#: lib/choose_rev.tcl:53 +msgid "This Detached Checkout" +msgstr "Ez a leválasztott checkout" + +#: lib/choose_rev.tcl:60 +msgid "Revision Expression:" +msgstr "RevÃzió kifejezés:" + +#: lib/choose_rev.tcl:74 +msgid "Local Branch" +msgstr "Helyi branch" + +#: lib/choose_rev.tcl:79 +msgid "Tracking Branch" +msgstr "KövetÅ‘ branch" + +#: lib/choose_rev.tcl:84 lib/choose_rev.tcl:537 +msgid "Tag" +msgstr "Tag" + +#: lib/choose_rev.tcl:317 +#, tcl-format +msgid "Invalid revision: %s" +msgstr "Érvénytelen revÃzió: %s" + +#: lib/choose_rev.tcl:338 +msgid "No revision selected." +msgstr "Nincs kiválasztva revÃzió." + +#: lib/choose_rev.tcl:346 +msgid "Revision expression is empty." +msgstr "A revÃzió kifejezés üres." + +#: lib/choose_rev.tcl:530 +msgid "Updated" +msgstr "FrissÃtve" + +#: lib/choose_rev.tcl:558 +msgid "URL" +msgstr "URL" + +#: lib/commit.tcl:9 +msgid "" +"There is nothing to amend.\n" +"\n" +"You are about to create the initial commit. There is no commit before this " +"to amend.\n" +msgstr "" +"Nincs semmi javÃtanivaló.\n" +"\n" +"Az elsÅ‘ commit létrehozása elÅ‘tt nincs semmilyen commit amit javitani " +"lehetne.\n" + +#: lib/commit.tcl:18 +msgid "" +"Cannot amend while merging.\n" +"\n" +"You are currently in the middle of a merge that has not been fully " +"completed. You cannot amend the prior commit unless you first abort the " +"current merge activity.\n" +msgstr "" +"Nem lehet javÃtani merge alatt.\n" +"\n" +"A jelenlegi merge még nem teljesen fejezÅ‘dött be. Csak akkor javÃthat egy " +"elÅ‘bbi commitot, hogyha megszakÃtja a jelenlegi merge folyamatot.\n" + +#: lib/commit.tcl:49 +msgid "Error loading commit data for amend:" +msgstr "Hiba a javÃtandó commit adat betöltése közben:" + +#: lib/commit.tcl:76 +msgid "Unable to obtain your identity:" +msgstr "Nem sikerült megállapÃtani az azonosÃtót:" + +#: lib/commit.tcl:81 +msgid "Invalid GIT_COMMITTER_IDENT:" +msgstr "Érvénytelen GIT_COMMITTER_IDENT:" + +#: lib/commit.tcl:133 +msgid "" +"Last scanned state does not match repository state.\n" +"\n" +"Another Git program has modified this repository since the last scan. A " +"rescan must be performed before another commit can be created.\n" +"\n" +"The rescan will be automatically started now.\n" +msgstr "" +"Az utolsó keresési állapot nem egyezik meg a repó állapotával.\n" +"\n" +"Egy másik Git program módosÃtotta ezt a repót az utolsó keresés óta. Egy " +"újrakeresés mindenképpen szükséges mielÅ‘tt a jelenlegi branchet módosÃtani " +"lehetne.\n" +"\n" +"Az újrakeresés most automatikusan el fog indulni.\n" + +#: lib/commit.tcl:154 +#, tcl-format +msgid "" +"Unmerged files cannot be committed.\n" +"\n" +"File %s has merge conflicts. You must resolve them and stage the file " +"before committing.\n" +msgstr "" +"Nem commitolhatunk fájlokat merge elÅ‘tt.\n" +"\n" +"A(z) %s fájlban ütközések vannak. Egyszer azokat ki kell javÃtani, majd " +"hozzá ki kell választani a fájlt mielÅ‘tt commitolni lehetne.\n" + +#: lib/commit.tcl:162 +#, tcl-format +msgid "" +"Unknown file state %s detected.\n" +"\n" +"File %s cannot be committed by this program.\n" +msgstr "" +"Ismeretlen fájl tÃpus %s érzékelve.\n" +"\n" +"A(z) %s fájlt nem tudja ez a program commitolni.\n" + +#: lib/commit.tcl:170 +msgid "" +"No changes to commit.\n" +"\n" +"You must stage at least 1 file before you can commit.\n" +msgstr "" +"Nincs commitolandó változtatás.\n" +"\n" +"Legalább egy fájl ki kell választani, hogy commitolni lehessen.\n" + +#: lib/commit.tcl:183 +msgid "" +"Please supply a commit message.\n" +"\n" +"A good commit message has the following format:\n" +"\n" +"- First line: Describe in one sentence what you did.\n" +"- Second line: Blank\n" +"- Remaining lines: Describe why this change is good.\n" +msgstr "" +"Adjunk megy egy commit üzenetet.\n" +"\n" +"Egy jó commit üzenetnek a következÅ‘ a formátuma:\n" +"\n" +"- ElsÅ‘ sor: Egy mondatban leÃrja, hogy mit csináltunk.\n" +"- Második sor: Ãœres\n" +"- A többi sor: LeÃrja, hogy miért jó ez a változtatás.\n" + +#: lib/commit.tcl:257 +msgid "write-tree failed:" +msgstr "a write-tree sikertelen:" + +#: lib/commit.tcl:275 +#, tcl-format +msgid "Commit %s appears to be corrupt" +msgstr "A(z) %s commit sérültnek tűnik" + +#: lib/commit.tcl:279 +msgid "" +"No changes to commit.\n" +"\n" +"No files were modified by this commit and it was not a merge commit.\n" +"\n" +"A rescan will be automatically started now.\n" +msgstr "" +"Nincs commitolandó változtatás.\n" +"\n" +"Egyetlen fájlt se módosÃtott ez a commit és merge commit se volt.\n" +"\n" +"Az újrakeresés most automatikusan el fog indulni.\n" + +#: lib/commit.tcl:286 +msgid "No changes to commit." +msgstr "Nincs commitolandó változtatás." + +#: lib/commit.tcl:303 +#, tcl-format +msgid "warning: Tcl does not support encoding '%s'." +msgstr "figyelmeztetés: a Tcl nem támogatja a(z) '%s' kódolást." + +#: lib/commit.tcl:317 +msgid "commit-tree failed:" +msgstr "a commit-tree sikertelen:" + +#: lib/commit.tcl:339 +msgid "update-ref failed:" +msgstr "az update-ref sikertelen:" + +#: lib/commit.tcl:430 +#, tcl-format +msgid "Created commit %s: %s" +msgstr "Létrejött a %s commit: %s" + +#: lib/console.tcl:57 +msgid "Working... please wait..." +msgstr "Munka folyamatban.. Várjunk..." + +#: lib/console.tcl:183 +msgid "Success" +msgstr "Siker" + +#: lib/console.tcl:196 +msgid "Error: Command Failed" +msgstr "Hiba: a parancs sikertelen" + +#: lib/database.tcl:43 +msgid "Number of loose objects" +msgstr "Elvesztett objektumok száma" + +#: lib/database.tcl:44 +msgid "Disk space used by loose objects" +msgstr "Elveszett objektumok által elfoglalt lemezterület" + +#: lib/database.tcl:45 +msgid "Number of packed objects" +msgstr "Csomagolt objektumok számra" + +#: lib/database.tcl:46 +msgid "Number of packs" +msgstr "Csomagok száma" + +#: lib/database.tcl:47 +msgid "Disk space used by packed objects" +msgstr "A csomagolt objektumok által használt lemezterület" + +#: lib/database.tcl:48 +msgid "Packed objects waiting for pruning" +msgstr "EltávolÃtásra váró csomagolt objektumok számra" + +#: lib/database.tcl:49 +msgid "Garbage files" +msgstr "Hulladék fájlok" + +#: lib/database.tcl:72 +msgid "Compressing the object database" +msgstr "Az objektum adatbázis tömörÃtése" + +#: lib/database.tcl:83 +msgid "Verifying the object database with fsck-objects" +msgstr "Az objektum adatbázis ellenÅ‘rzése az fsck-objects használatával" + +#: lib/database.tcl:108 +#, tcl-format +msgid "" +"This repository currently has approximately %i loose objects.\n" +"\n" +"To maintain optimal performance it is strongly recommended that you compress " +"the database when more than %i loose objects exist.\n" +"\n" +"Compress the database now?" +msgstr "" +"Ennek a repónak jelenleg %i különálló objektuma van.\n" +"\n" +"Az optimális teljesÃtményhez erÅ‘sen ajánlott az adatbázis tömörÃtése, ha " +"több mint %i objektum létezik.\n" +"\n" +"Lehet most tömörÃteni az adatbázist?" + +#: lib/date.tcl:25 +#, tcl-format +msgid "Invalid date from Git: %s" +msgstr "Érvénytelen dátum a Git-tÅ‘l: %s" + +#: lib/diff.tcl:42 +#, tcl-format +msgid "" +"No differences detected.\n" +"\n" +"%s has no changes.\n" +"\n" +"The modification date of this file was updated by another application, but " +"the content within the file was not changed.\n" +"\n" +"A rescan will be automatically started to find other files which may have " +"the same state." +msgstr "" +"Nincsenek változások.\n" +"\n" +"A(z) %s módosÃtatlan.\n" +"\n" +"A fájl módosÃtási dátumát frissÃtette egy másik alkalmazás, de a fájl " +"tartalma változatlan.\n" +"\n" +"Egy újrakeresés fog indulni a hasonló állapotú fájlok megtalálása érdekében." + +#: lib/diff.tcl:81 +#, tcl-format +msgid "Loading diff of %s..." +msgstr "A(z) %s diff-jének betöltése..." + +#: lib/diff.tcl:114 lib/diff.tcl:184 +#, tcl-format +msgid "Unable to display %s" +msgstr "Nem lehet megjelenÃteni a következÅ‘t: %s" + +#: lib/diff.tcl:115 +msgid "Error loading file:" +msgstr "Hiba a fájl betöltése közben:" + +#: lib/diff.tcl:122 +msgid "Git Repository (subproject)" +msgstr "Git repó (alprojekt)" + +#: lib/diff.tcl:134 +msgid "* Binary file (not showing content)." +msgstr "* Bináris fájl (tartalom elrejtése)." + +#: lib/diff.tcl:185 +msgid "Error loading diff:" +msgstr "Hiba a diff betöltése közben:" + +#: lib/diff.tcl:302 +msgid "Failed to unstage selected hunk." +msgstr "Nem visszavonni a hunk kiválasztását." + +#: lib/diff.tcl:309 +msgid "Failed to stage selected hunk." +msgstr "Nem sikerült kiválasztani a hunkot." + +#: lib/error.tcl:12 lib/error.tcl:102 +msgid "error" +msgstr "hiba" + +#: lib/error.tcl:28 +msgid "warning" +msgstr "figyelmeztetés" + +#: lib/error.tcl:81 +msgid "You must correct the above errors before committing." +msgstr "Ki kell javÃtanunk a fenti hibákat commit elÅ‘tt." + +#: lib/index.tcl:6 +msgid "Unable to unlock the index." +msgstr "Nem sikerült az index zárolásának feloldása." + +#: lib/index.tcl:15 +msgid "Index Error" +msgstr "Index hiba" + +#: lib/index.tcl:21 +msgid "" +"Updating the Git index failed. A rescan will be automatically started to " +"resynchronize git-gui." +msgstr "" +"A Git index frissÃtése sikertelen volt. Egy újraolvasás automatikusan elindult, hogy " +"a git-gui újra szinkonban legyen." + +#: lib/index.tcl:27 +msgid "Continue" +msgstr "Folytatás" + +#: lib/index.tcl:31 +msgid "Unlock Index" +msgstr "Index zárolásának feloldása" + +#: lib/index.tcl:282 +#, tcl-format +msgid "Unstaging %s from commit" +msgstr "A(z) %s commitba való kiválasztásának visszavonása" + +#: lib/index.tcl:326 +#, tcl-format +msgid "Adding %s" +msgstr "A(z) %s hozzáadása..." + +#: lib/index.tcl:381 +#, tcl-format +msgid "Revert changes in file %s?" +msgstr "VisszaállÃtja a változtatásokat a(z) %s fájlban?" + +#: lib/index.tcl:383 +#, tcl-format +msgid "Revert changes in these %i files?" +msgstr "VisszaállÃtja a változtatásokat ebben e %i fájlban?" + +#: lib/index.tcl:389 +msgid "Any unstaged changes will be permanently lost by the revert." +msgstr "" +"Minden nem kiválasztott változtatás el fog veszni ezáltal a visszaállÃtás " +"által." + +#: lib/index.tcl:392 +msgid "Do Nothing" +msgstr "Ne csináljunk semmit" + +#: lib/merge.tcl:13 +msgid "" +"Cannot merge while amending.\n" +"\n" +"You must finish amending this commit before starting any type of merge.\n" +msgstr "" +"JavÃtás közben nem lehetséges a merge.\n" +"\n" +"Egyszer be kell fejezni ennek a commitnak a javÃtását, majd kezdÅ‘dhet egy " +"merge.\n" + +#: lib/merge.tcl:27 +msgid "" +"Last scanned state does not match repository state.\n" +"\n" +"Another Git program has modified this repository since the last scan. A " +"rescan must be performed before a merge can be performed.\n" +"\n" +"The rescan will be automatically started now.\n" +msgstr "" +"Az utolsó keresési állapot nem egyezik meg a repó állapotával.\n" +"\n" +"Egy másik Git program módosÃtotta ezt a repót az utolsó keresés óta. Egy " +"újrakeresés mindenképpen szükséges mielÅ‘tt a jelenlegi branchet módosÃtani " +"lehetne.\n" +"\n" +"Az újrakeresés most automatikusan el fog indulni.\n" + +#: lib/merge.tcl:44 +#, tcl-format +msgid "" +"You are in the middle of a conflicted merge.\n" +"\n" +"File %s has merge conflicts.\n" +"\n" +"You must resolve them, stage the file, and commit to complete the current " +"merge. Only then can you begin another merge.\n" +msgstr "" +"Jelenleg egy ütközés feloldása közben vagyunk.\n" +"\n" +"A(z) %s fájlban ütközések vannak.\n" +"\n" +"Fel kell oldanunk Å‘ket, kiválasztani a fájlt, és commitolni hogy befejezzük " +"a jelenlegi merge-t. Csak ezután kezdhetünk el egy újabbat.\n" + +#: lib/merge.tcl:54 +#, tcl-format +msgid "" +"You are in the middle of a change.\n" +"\n" +"File %s is modified.\n" +"\n" +"You should complete the current commit before starting a merge. Doing so " +"will help you abort a failed merge, should the need arise.\n" +msgstr "" +"Jelenleg egy változtatás közben vagyunk.\n" +"\n" +"A(z) %s fájl megváltozott.\n" +"\n" +"ElÅ‘ször be kell fejeznünk a jelenlegi commitot, hogy elkezdhessünk egy merge-" +"t. Ez segÃteni fog, hogy félbeszakÃthassunk egy merge-t.\n" + +#: lib/merge.tcl:106 +#, tcl-format +msgid "%s of %s" +msgstr "%s / %s" + +#: lib/merge.tcl:119 +#, tcl-format +msgid "Merging %s and %s" +msgstr "A(z) %s és a(z) %s merge-ölése" + +#: lib/merge.tcl:131 +msgid "Merge completed successfully." +msgstr "A merge sikeresen befejezÅ‘dött." + +#: lib/merge.tcl:133 +msgid "Merge failed. Conflict resolution is required." +msgstr "A merge sikertelen. Fel kell oldanunk az ütközéseket." + +#: lib/merge.tcl:158 +#, tcl-format +msgid "Merge Into %s" +msgstr "Merge-ölés a következÅ‘be: %s" + +#: lib/merge.tcl:177 +msgid "Revision To Merge" +msgstr "Merge-ölni szándékozott revÃzió" + +#: lib/merge.tcl:212 +msgid "" +"Cannot abort while amending.\n" +"\n" +"You must finish amending this commit.\n" +msgstr "" +"A commit javÃtás közben megszakÃtva.\n" +"\n" +"Be kell fejeznünk ennek a commitnak a javÃtását.\n" + +#: lib/merge.tcl:222 +msgid "" +"Abort merge?\n" +"\n" +"Aborting the current merge will cause *ALL* uncommitted changes to be lost.\n" +"\n" +"Continue with aborting the current merge?" +msgstr "" +"MegszakÃtjuk a merge-t?\n" +"\n" +"A jelenlegi merge megszakÃtása *MINDEN* nem commitolt változtatás " +"elvesztését jelenti.\n" +"\n" +"Folytatjuk a jelenlegi merge megszakÃtását?" + +#: lib/merge.tcl:228 +msgid "" +"Reset changes?\n" +"\n" +"Resetting the changes will cause *ALL* uncommitted changes to be lost.\n" +"\n" +"Continue with resetting the current changes?" +msgstr "" +"Visszavonjuk a módosÃtásokat?\n" +"\n" +"A módosÃtások visszavonása *MINDEN* nem commitolt változtatás elvesztését " +"jelenti.\n" +"\n" +"Folytatjuk a jelenlegi módosÃtások visszavonását?" + +#: lib/merge.tcl:239 +msgid "Aborting" +msgstr "FélbeszakÃtás" + +#: lib/merge.tcl:266 +msgid "Abort failed." +msgstr "A félbeszakÃtás nem sikerült." + +#: lib/merge.tcl:268 +msgid "Abort completed. Ready." +msgstr "A megkeszakÃtás befejezÅ‘dött. Kész." + +#: lib/option.tcl:82 +msgid "Restore Defaults" +msgstr "Alapértelmezés visszaállÃtása" + +#: lib/option.tcl:86 +msgid "Save" +msgstr "Mentés" + +#: lib/option.tcl:96 +#, tcl-format +msgid "%s Repository" +msgstr "%s Repó" + +#: lib/option.tcl:97 +msgid "Global (All Repositories)" +msgstr "Globális (minden repó)" + +#: lib/option.tcl:103 +msgid "User Name" +msgstr "Felhasználónév" + +#: lib/option.tcl:104 +msgid "Email Address" +msgstr "Email cÃm" + +#: lib/option.tcl:106 +msgid "Summarize Merge Commits" +msgstr "A merge commitok összegzése" + +#: lib/option.tcl:107 +msgid "Merge Verbosity" +msgstr "Merge beszédesség" + +#: lib/option.tcl:108 +msgid "Show Diffstat After Merge" +msgstr "Diffstat mutatása merge után" + +#: lib/option.tcl:110 +msgid "Trust File Modification Timestamps" +msgstr "A fájl módosÃtási dátumok megbÃzhatóak" + +#: lib/option.tcl:111 +msgid "Prune Tracking Branches During Fetch" +msgstr "A követÅ‘ branchek eltávolÃtása letöltés alatt" + +#: lib/option.tcl:112 +msgid "Match Tracking Branches" +msgstr "A követÅ‘ branchek egyeztetése" + +#: lib/option.tcl:113 +msgid "Number of Diff Context Lines" +msgstr "A diff környezeti sorok száma" + +#: lib/option.tcl:114 +msgid "New Branch Name Template" +msgstr "Új branch név sablon" + +#: lib/option.tcl:176 +msgid "Change Font" +msgstr "BetűtÃpus megváltoztatása" + +#: lib/option.tcl:180 +#, tcl-format +msgid "Choose %s" +msgstr "%s választása" + +#: lib/option.tcl:186 +msgid "pt." +msgstr "pt." + +#: lib/option.tcl:200 +msgid "Preferences" +msgstr "BeállÃtások" + +#: lib/option.tcl:235 +msgid "Failed to completely save options:" +msgstr "Nem sikerült teljesen elmenteni a beállÃtásokat:" + +#: lib/remote_branch_delete.tcl:29 lib/remote_branch_delete.tcl:34 +msgid "Delete Remote Branch" +msgstr "Távoli branch törlése" + +#: lib/remote_branch_delete.tcl:47 +msgid "From Repository" +msgstr "Forrás repó" + +#: lib/remote_branch_delete.tcl:50 lib/transport.tcl:123 +msgid "Remote:" +msgstr "Távoli:" + +#: lib/remote_branch_delete.tcl:66 lib/transport.tcl:138 +msgid "Arbitrary URL:" +msgstr "TetszÅ‘leges URL:" + +#: lib/remote_branch_delete.tcl:84 +msgid "Branches" +msgstr "Branchek" + +#: lib/remote_branch_delete.tcl:109 +msgid "Delete Only If" +msgstr "Törlés csak akkor ha" + +#: lib/remote_branch_delete.tcl:111 +msgid "Merged Into:" +msgstr "Merge-ölt a következÅ‘be:" + +#: lib/remote_branch_delete.tcl:119 +msgid "Always (Do not perform merge checks)" +msgstr "Mindig (Ne végezzen merge vizsgálatokat)" + +#: lib/remote_branch_delete.tcl:152 +msgid "A branch is required for 'Merged Into'." +msgstr "Egy branch szükséges a 'Merge-ölt a következÅ‘be'-hez." + +#: lib/remote_branch_delete.tcl:184 +#, tcl-format +msgid "" +"The following branches are not completely merged into %s:\n" +"\n" +" - %s" +msgstr "" +"A következÅ‘ branchek nem teljesen lettek merge-ölve ebbe: %s:" +"\n" +" - %s" + +#: lib/remote_branch_delete.tcl:189 +#, tcl-format +msgid "" +"One or more of the merge tests failed because you have not fetched the " +"necessary commits. Try fetching from %s first." +msgstr "" +"Egy vagy több merge teszt hibát jelzett, mivel nem töltöttük le a megfelelÅ‘ " +"commitokat. Próbáljunk meg letölteni a következÅ‘bÅ‘l: %s elÅ‘ször." + +#: lib/remote_branch_delete.tcl:207 +msgid "Please select one or more branches to delete." +msgstr "Válasszunk ki egy vagy több branchet törlésre." + +#: lib/remote_branch_delete.tcl:216 +msgid "" +"Recovering deleted branches is difficult.\n" +"\n" +"Delete the selected branches?" +msgstr "" +"A törölt branchek visszaállÃtása nehéz.\n" +"\n" +"Töröljük a kiválasztott brancheket?" + +#: lib/remote_branch_delete.tcl:226 +#, tcl-format +msgid "Deleting branches from %s" +msgstr "Brancek törlése innen: %s" + +#: lib/remote_branch_delete.tcl:286 +msgid "No repository selected." +msgstr "Nincs kiválasztott repó." + +#: lib/remote_branch_delete.tcl:291 +#, tcl-format +msgid "Scanning %s..." +msgstr "Keresés itt: %s..." + +#: lib/remote.tcl:165 +msgid "Prune from" +msgstr "Törlés innen" + +# tcl-format +#: lib/remote.tcl:170 +msgid "Fetch from" +msgstr "Letöltés innen" + +#: lib/remote.tcl:213 +msgid "Push to" +msgstr "Push ide" + +#: lib/shortcut.tcl:20 lib/shortcut.tcl:61 +msgid "Cannot write shortcut:" +msgstr "Nem sikerült Ãrni a gyorsbillentyűt:" + +#: lib/shortcut.tcl:136 +msgid "Cannot write icon:" +msgstr "Nem sikerült Ãrni az ikont:" + +#: lib/status_bar.tcl:83 +#, tcl-format +msgid "%s ... %*i of %*i %s (%3i%%)" +msgstr "%s ... %*i / %*i %s (%3i%%)" + +#: lib/transport.tcl:6 +#, tcl-format +msgid "fetch %s" +msgstr "a(z) %s letöltése" + +#: lib/transport.tcl:7 +#, tcl-format +msgid "Fetching new changes from %s" +msgstr "Új változások letöltése innen: %s" + +#: lib/transport.tcl:18 +#, tcl-format +msgid "remote prune %s" +msgstr "a(z) %s távoli törlése" + +#: lib/transport.tcl:19 +#, tcl-format +msgid "Pruning tracking branches deleted from %s" +msgstr "A %s repóból törölt követÅ‘ branchek törlése" + +#: lib/transport.tcl:25 lib/transport.tcl:71 +#, tcl-format +msgid "push %s" +msgstr "%s push-olása" + +#: lib/transport.tcl:26 +#, tcl-format +msgid "Pushing changes to %s" +msgstr "Változások pusholása ide: %s" + +#: lib/transport.tcl:72 +#, tcl-format +msgid "Pushing %s %s to %s" +msgstr "Pusholás: %s %s, ide: %s" + +#: lib/transport.tcl:89 +msgid "Push Branches" +msgstr "Branchek pusholása" + +#: lib/transport.tcl:103 +msgid "Source Branches" +msgstr "Forrás branchek" + +#: lib/transport.tcl:120 +msgid "Destination Repository" +msgstr "Cél repó" + +#: lib/transport.tcl:158 +msgid "Transfer Options" +msgstr "Ãtviteli opciók" + +#: lib/transport.tcl:160 +msgid "Force overwrite existing branch (may discard changes)" +msgstr "LétezÅ‘ branch felülÃrásának erÅ‘ltetése (lehet, hogy el fog dobni változtatásokat)" + +#: lib/transport.tcl:164 +msgid "Use thin pack (for slow network connections)" +msgstr "Vékony csomagok használata (lassú hálózati kapcsolatok számára)" + +#: lib/transport.tcl:168 +msgid "Include tags" +msgstr "Tageket is" + +#~ msgid "Cannot find the git directory:" +#~ msgstr "Nem található a git könyvtár:" + +#~ msgid "Unstaged Changes (Will Not Be Committed)" +#~ msgstr "Nem kiválasztott változtatások (nem lesz commitolva)" + +#~ msgid "Push to %s..." +#~ msgstr "Pusholás ide: %s..." + +#~ msgid "Add To Commit" +#~ msgstr "Hozzáadás a commithoz" + +#~ msgid "Add Existing To Commit" +#~ msgstr "Hozzáadás létezÅ‘ commithoz" + +#~ msgid "Running miga..." +#~ msgstr "A miga futtatása..." + +#~ msgid "Add Existing" +#~ msgstr "LétezÅ‘ hozzáadása" + +#~ msgid "" +#~ "Abort commit?\n" +#~ "\n" +#~ "Aborting the current commit will cause *ALL* uncommitted changes to be " +#~ "lost.\n" +#~ "\n" +#~ "Continue with aborting the current commit?" +#~ msgstr "" +#~ "MegszakÃtjuk a commitot?\n" +#~ "\n" +#~ "A jelenlegi commit megszakÃtása *MINDEN* nem commitolt változtatás " +#~ "elvesztését jelenti.\n" +#~ "\n" +#~ "Folytatjuk a jelenlegi commit megszakÃtását?" + +#~ msgid "Aborting... please wait..." +#~ msgstr "MegszakÃtás... várjunk..." diff --git a/git-gui/po/it.po b/git-gui/po/it.po new file mode 100644 index 0000000000..33a8399175 --- /dev/null +++ b/git-gui/po/it.po @@ -0,0 +1,1913 @@ +# Translation of git-gui to Italian +# Copyright (C) 2007 Shawn Pearce +# This file is distributed under the same license as the git-gui package. +# Paolo Ciarrocchi <paolo.ciarrocchi@gmail.com>, 2007 +# Michele Ballabio <barra_cuda@katamail.com>, 2007. +# +# +msgid "" +msgstr "" +"Project-Id-Version: git-gui\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2007-11-09 11:18+0100\n" +"PO-Revision-Date: 2007-11-01 21:05+0100\n" +"Last-Translator: Michele Ballabio <barra_cuda@katamail.com>\n" +"Language-Team: Italian <tp@lists.linux.it>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: git-gui.sh:41 git-gui.sh:604 git-gui.sh:618 git-gui.sh:631 git-gui.sh:714 +#: git-gui.sh:733 +msgid "git-gui: fatal error" +msgstr "git-gui: errore grave" + +#: git-gui.sh:565 +#, tcl-format +msgid "Invalid font specified in %s:" +msgstr "Caratteri non validi specificati in %s:" + +#: git-gui.sh:590 +msgid "Main Font" +msgstr "Caratteri principali" + +#: git-gui.sh:591 +msgid "Diff/Console Font" +msgstr "Caratteri per confronti e terminale" + +#: git-gui.sh:605 +msgid "Cannot find git in PATH." +msgstr "Impossibile trovare git nel PATH" + +#: git-gui.sh:632 +msgid "Cannot parse Git version string:" +msgstr "Impossibile determinare la versione di Git:" + +#: git-gui.sh:650 +#, tcl-format +msgid "" +"Git version cannot be determined.\n" +"\n" +"%s claims it is version '%s'.\n" +"\n" +"%s requires at least Git 1.5.0 or later.\n" +"\n" +"Assume '%s' is version 1.5.0?\n" +msgstr "" +"La versione di Git non può essere determinata.\n" +"\n" +"%s riporta che la versione è '%s'.\n" +"\n" +"%s richiede almeno Git 1.5.0 o superiore.\n" +"\n" +"Assumere che '%s' sia alla versione 1.5.0?\n" + +#: git-gui.sh:888 +msgid "Git directory not found:" +msgstr "Non trovo la directory di git: " + +#: git-gui.sh:895 +msgid "Cannot move to top of working directory:" +msgstr "Impossibile spostarsi sulla directory principale del progetto:" + +#: git-gui.sh:902 +msgid "Cannot use funny .git directory:" +msgstr "Impossibile usare una .git directory strana:" + +#: git-gui.sh:907 +msgid "No working directory" +msgstr "Nessuna directory di lavoro" + +#: git-gui.sh:1054 +msgid "Refreshing file status..." +msgstr "Controllo dello stato dei file in corso..." + +#: git-gui.sh:1119 +msgid "Scanning for modified files ..." +msgstr "Ricerca di file modificati in corso..." + +#: git-gui.sh:1294 lib/browser.tcl:245 +msgid "Ready." +msgstr "Pronto." + +#: git-gui.sh:1560 +msgid "Unmodified" +msgstr "Non modificato" + +#: git-gui.sh:1562 +msgid "Modified, not staged" +msgstr "Modificato, non preparato per una nuova revisione" + +#: git-gui.sh:1563 git-gui.sh:1568 +msgid "Staged for commit" +msgstr "Preparato per una nuova revisione" + +#: git-gui.sh:1564 git-gui.sh:1569 +msgid "Portions staged for commit" +msgstr "Parti preparate per una nuova revisione" + +#: git-gui.sh:1565 git-gui.sh:1570 +msgid "Staged for commit, missing" +msgstr "Preparato per una nuova revisione, mancante" + +#: git-gui.sh:1567 +msgid "Untracked, not staged" +msgstr "Non tracciato, non preparato per una nuova revisione" + +#: git-gui.sh:1572 +msgid "Missing" +msgstr "Mancante" + +#: git-gui.sh:1573 +msgid "Staged for removal" +msgstr "Preparato per la rimozione" + +#: git-gui.sh:1574 +msgid "Staged for removal, still present" +msgstr "Preparato alla rimozione, ancora presente" + +#: git-gui.sh:1576 git-gui.sh:1577 git-gui.sh:1578 git-gui.sh:1579 +msgid "Requires merge resolution" +msgstr "Richiede risoluzione dei conflitti" + +#: git-gui.sh:1614 +msgid "Starting gitk... please wait..." +msgstr "Avvio di gitk... attendere..." + +#: git-gui.sh:1623 +#, tcl-format +msgid "" +"Unable to start gitk:\n" +"\n" +"%s does not exist" +msgstr "" +"Impossibile avviare gitk:\n" +"\n" +"%s non esiste" + +#: git-gui.sh:1823 lib/choose_repository.tcl:35 +msgid "Repository" +msgstr "Archivio" + +#: git-gui.sh:1824 +msgid "Edit" +msgstr "Modifica" + +#: git-gui.sh:1826 lib/choose_rev.tcl:560 +msgid "Branch" +msgstr "Ramo" + +#: git-gui.sh:1829 lib/choose_rev.tcl:547 +msgid "Commit@@noun" +msgstr "Revisione" + +#: git-gui.sh:1832 lib/merge.tcl:121 lib/merge.tcl:150 lib/merge.tcl:168 +msgid "Merge" +msgstr "Fusione (Merge)" + +#: git-gui.sh:1833 lib/choose_rev.tcl:556 +msgid "Remote" +msgstr "Remoto" + +#: git-gui.sh:1842 +msgid "Browse Current Branch's Files" +msgstr "Esplora i file del ramo attuale" + +#: git-gui.sh:1846 +msgid "Browse Branch Files..." +msgstr "Esplora i file del ramo..." + +#: git-gui.sh:1851 +msgid "Visualize Current Branch's History" +msgstr "Visualizza la cronologia del ramo attuale" + +#: git-gui.sh:1855 +msgid "Visualize All Branch History" +msgstr "Visualizza la cronologia di tutti i rami" + +#: git-gui.sh:1862 +#, tcl-format +msgid "Browse %s's Files" +msgstr "Esplora i file di %s" + +#: git-gui.sh:1864 +#, tcl-format +msgid "Visualize %s's History" +msgstr "Visualizza la cronologia di %s" + +#: git-gui.sh:1869 lib/database.tcl:27 lib/database.tcl:67 +msgid "Database Statistics" +msgstr "Statistiche dell'archivio" + +#: git-gui.sh:1872 lib/database.tcl:34 +msgid "Compress Database" +msgstr "Comprimi l'archivio" + +#: git-gui.sh:1875 +msgid "Verify Database" +msgstr "Verifica l'archivio" + +#: git-gui.sh:1882 git-gui.sh:1886 git-gui.sh:1890 lib/shortcut.tcl:7 +#: lib/shortcut.tcl:39 lib/shortcut.tcl:71 +msgid "Create Desktop Icon" +msgstr "Crea icona desktop" + +#: git-gui.sh:1895 lib/choose_repository.tcl:176 lib/choose_repository.tcl:184 +msgid "Quit" +msgstr "Esci" + +#: git-gui.sh:1902 +msgid "Undo" +msgstr "Annulla" + +#: git-gui.sh:1905 +msgid "Redo" +msgstr "Ripeti" + +#: git-gui.sh:1909 git-gui.sh:2403 +msgid "Cut" +msgstr "Taglia" + +#: git-gui.sh:1912 git-gui.sh:2406 git-gui.sh:2477 git-gui.sh:2549 +#: lib/console.tcl:67 +msgid "Copy" +msgstr "Copia" + +#: git-gui.sh:1915 git-gui.sh:2409 +msgid "Paste" +msgstr "Incolla" + +#: git-gui.sh:1918 git-gui.sh:2412 lib/branch_delete.tcl:26 +#: lib/remote_branch_delete.tcl:38 +msgid "Delete" +msgstr "Elimina" + +#: git-gui.sh:1922 git-gui.sh:2416 git-gui.sh:2553 lib/console.tcl:69 +msgid "Select All" +msgstr "Seleziona tutto" + +#: git-gui.sh:1931 +msgid "Create..." +msgstr "Crea..." + +#: git-gui.sh:1937 +msgid "Checkout..." +msgstr "Attiva..." + +#: git-gui.sh:1943 +msgid "Rename..." +msgstr "Rinomina" + +#: git-gui.sh:1948 git-gui.sh:2048 +msgid "Delete..." +msgstr "Elimina..." + +#: git-gui.sh:1953 +msgid "Reset..." +msgstr "Ripristina..." + +#: git-gui.sh:1965 git-gui.sh:2350 +msgid "New Commit" +msgstr "Nuova revisione" + +#: git-gui.sh:1973 git-gui.sh:2357 +msgid "Amend Last Commit" +msgstr "Correggi l'ultima revisione" + +#: git-gui.sh:1982 git-gui.sh:2317 lib/remote_branch_delete.tcl:99 +msgid "Rescan" +msgstr "Analizza nuovamente" + +#: git-gui.sh:1988 +msgid "Stage To Commit" +msgstr "Prepara per una nuova revisione" + +#: git-gui.sh:1994 +msgid "Stage Changed Files To Commit" +msgstr "Prepara i file modificati per una nuova revisione" + +#: git-gui.sh:2000 +msgid "Unstage From Commit" +msgstr "Annulla preparazione" + +#: git-gui.sh:2005 lib/index.tcl:393 +msgid "Revert Changes" +msgstr "Annulla modifiche" + +#: git-gui.sh:2012 git-gui.sh:2329 git-gui.sh:2427 +msgid "Sign Off" +msgstr "Sign Off" + +#: git-gui.sh:2016 git-gui.sh:2333 +msgid "Commit@@verb" +msgstr "Nuova revisione" + +#: git-gui.sh:2027 +msgid "Local Merge..." +msgstr "Fusione locale..." + +#: git-gui.sh:2032 +msgid "Abort Merge..." +msgstr "Interrompi fusione..." + +#: git-gui.sh:2044 +msgid "Push..." +msgstr "Propaga..." + +#: git-gui.sh:2055 lib/choose_repository.tcl:40 +msgid "Apple" +msgstr "Apple" + +#: git-gui.sh:2058 git-gui.sh:2080 lib/about.tcl:13 +#: lib/choose_repository.tcl:43 lib/choose_repository.tcl:49 +#, tcl-format +msgid "About %s" +msgstr "Informazioni su %s" + +#: git-gui.sh:2062 +msgid "Preferences..." +msgstr "Preferenze..." + +#: git-gui.sh:2070 git-gui.sh:2595 +msgid "Options..." +msgstr "Opzioni..." + +#: git-gui.sh:2076 lib/choose_repository.tcl:46 +msgid "Help" +msgstr "Aiuto" + +#: git-gui.sh:2117 +msgid "Online Documentation" +msgstr "Documentazione sul web" + +#: git-gui.sh:2201 +#, tcl-format +msgid "fatal: cannot stat path %s: No such file or directory" +msgstr "" +"errore grave: impossibile effettuare lo stat del path %s: file o directory " +"non trovata" + +#: git-gui.sh:2234 +msgid "Current Branch:" +msgstr "Ramo attuale:" + +#: git-gui.sh:2255 +msgid "Staged Changes (Will Commit)" +msgstr "Modifiche preparate (saranno nella nuova revisione)" + +#: git-gui.sh:2274 +msgid "Unstaged Changes" +msgstr "Modifiche non preparate" + +#: git-gui.sh:2323 +msgid "Stage Changed" +msgstr "Prepara modificati" + +#: git-gui.sh:2339 lib/transport.tcl:93 lib/transport.tcl:182 +msgid "Push" +msgstr "Propaga (Push)" + +#: git-gui.sh:2369 +msgid "Initial Commit Message:" +msgstr "Messaggio di revisione iniziale:" + +#: git-gui.sh:2370 +msgid "Amended Commit Message:" +msgstr "Messaggio di revisione corretto:" + +#: git-gui.sh:2371 +msgid "Amended Initial Commit Message:" +msgstr "Messaggio iniziale di revisione corretto:" + +#: git-gui.sh:2372 +msgid "Amended Merge Commit Message:" +msgstr "Messaggio di fusione corretto:" + +#: git-gui.sh:2373 +msgid "Merge Commit Message:" +msgstr "Messaggio di fusione:" + +#: git-gui.sh:2374 +msgid "Commit Message:" +msgstr "Messaggio di revisione:" + +#: git-gui.sh:2419 git-gui.sh:2557 lib/console.tcl:71 +msgid "Copy All" +msgstr "Copia tutto" + +#: git-gui.sh:2443 lib/blame.tcl:104 +msgid "File:" +msgstr "File:" + +#: git-gui.sh:2545 +msgid "Refresh" +msgstr "Rinfresca" + +#: git-gui.sh:2566 +msgid "Apply/Reverse Hunk" +msgstr "Applica/Inverti sezione" + +#: git-gui.sh:2572 +msgid "Decrease Font Size" +msgstr "Diminuisci dimensione caratteri" + +#: git-gui.sh:2576 +msgid "Increase Font Size" +msgstr "Aumenta dimensione caratteri" + +#: git-gui.sh:2581 +msgid "Show Less Context" +msgstr "Mostra meno contesto" + +#: git-gui.sh:2588 +msgid "Show More Context" +msgstr "Mostra più contesto" + +#: git-gui.sh:2602 +msgid "Unstage Hunk From Commit" +msgstr "Sezione non preparata per una nuova revisione" + +#: git-gui.sh:2604 +msgid "Stage Hunk For Commit" +msgstr "Prepara sezione per una nuova revisione" + +#: git-gui.sh:2623 +msgid "Initializing..." +msgstr "Inizializzazione..." + +#: git-gui.sh:2718 +#, tcl-format +msgid "" +"Possible environment issues exist.\n" +"\n" +"The following environment variables are probably\n" +"going to be ignored by any Git subprocess run\n" +"by %s:\n" +"\n" +msgstr "" +"Possibili problemi con le variabili d'ambiente.\n" +"\n" +"Le seguenti variabili d'ambiente saranno probabilmente\n" +"ignorate da tutti i sottoprocessi di Git avviati\n" +"da %s:\n" +"\n" + +#: git-gui.sh:2748 +msgid "" +"\n" +"This is due to a known issue with the\n" +"Tcl binary distributed by Cygwin." +msgstr "" +"\n" +"Ciò è dovuto a un problema conosciuto\n" +"causato dall'eseguibile Tcl distribuito da Cygwin." + +#: git-gui.sh:2753 +#, tcl-format +msgid "" +"\n" +"\n" +"A good replacement for %s\n" +"is placing values for the user.name and\n" +"user.email settings into your personal\n" +"~/.gitconfig file.\n" +msgstr "" +"\n" +"\n" +"Una buona alternativa a %s\n" +"consiste nell'assegnare valori alle variabili di configurazione\n" +"user.name e user.email nel tuo file ~/.gitconfig personale.\n" + +#: lib/about.tcl:25 +msgid "git-gui - a graphical user interface for Git." +msgstr "git-gui - un'interfaccia grafica per Git." + +#: lib/blame.tcl:77 +msgid "File Viewer" +msgstr "Mostra file" + +#: lib/blame.tcl:81 +msgid "Commit:" +msgstr "Revisione:" + +#: lib/blame.tcl:249 +msgid "Copy Commit" +msgstr "Copia revisione" + +#: lib/blame.tcl:369 +#, tcl-format +msgid "Reading %s..." +msgstr "Lettura di %s..." + +#: lib/blame.tcl:473 +msgid "Loading copy/move tracking annotations..." +msgstr "Caricamento annotazioni per copie/spostamenti..." + +#: lib/blame.tcl:493 +msgid "lines annotated" +msgstr "linee annotate" + +#: lib/blame.tcl:674 +msgid "Loading original location annotations..." +msgstr "Caricamento annotazioni per posizione originaria..." + +#: lib/blame.tcl:677 +msgid "Annotation complete." +msgstr "Annotazione completata." + +#: lib/blame.tcl:731 +msgid "Loading annotation..." +msgstr "Caricamento annotazioni..." + +#: lib/blame.tcl:787 +msgid "Author:" +msgstr "Autore:" + +#: lib/blame.tcl:791 +msgid "Committer:" +msgstr "Revisione creata da:" + +#: lib/blame.tcl:796 +msgid "Original File:" +msgstr "File originario:" + +#: lib/blame.tcl:910 +msgid "Originally By:" +msgstr "In origine da:" + +#: lib/blame.tcl:916 +msgid "In File:" +msgstr "Nel file:" + +#: lib/blame.tcl:921 +msgid "Copied Or Moved Here By:" +msgstr "Copiato o spostato qui da:" + +#: lib/branch_checkout.tcl:14 lib/branch_checkout.tcl:19 +msgid "Checkout Branch" +msgstr "Attiva ramo" + +#: lib/branch_checkout.tcl:23 +msgid "Checkout" +msgstr "Attiva" + +#: lib/branch_checkout.tcl:27 lib/branch_create.tcl:35 +#: lib/branch_delete.tcl:32 lib/branch_rename.tcl:30 lib/browser.tcl:281 +#: lib/checkout_op.tcl:522 lib/choose_font.tcl:43 lib/merge.tcl:172 +#: lib/option.tcl:90 lib/remote_branch_delete.tcl:42 lib/transport.tcl:97 +msgid "Cancel" +msgstr "Annulla" + +#: lib/branch_checkout.tcl:32 lib/browser.tcl:286 +msgid "Revision" +msgstr "Revisione" + +#: lib/branch_checkout.tcl:36 lib/branch_create.tcl:69 lib/option.tcl:202 +msgid "Options" +msgstr "Opzioni" + +#: lib/branch_checkout.tcl:39 lib/branch_create.tcl:92 +msgid "Fetch Tracking Branch" +msgstr "Recupera duplicato locale di ramo remoto" + +#: lib/branch_checkout.tcl:44 +msgid "Detach From Local Branch" +msgstr "Stacca da ramo locale" + +#: lib/branch_create.tcl:22 +msgid "Create Branch" +msgstr "Crea ramo" + +#: lib/branch_create.tcl:27 +msgid "Create New Branch" +msgstr "Crea nuovo ramo" + +#: lib/branch_create.tcl:31 lib/choose_repository.tcl:375 +msgid "Create" +msgstr "Crea" + +#: lib/branch_create.tcl:40 +msgid "Branch Name" +msgstr "Nome del ramo" + +#: lib/branch_create.tcl:43 +msgid "Name:" +msgstr "Nome:" + +#: lib/branch_create.tcl:58 +msgid "Match Tracking Branch Name" +msgstr "Appaia nome del duplicato locale di ramo remoto" + +#: lib/branch_create.tcl:66 +msgid "Starting Revision" +msgstr "Revisione iniziale" + +#: lib/branch_create.tcl:72 +msgid "Update Existing Branch:" +msgstr "Aggiorna ramo esistente:" + +#: lib/branch_create.tcl:75 +msgid "No" +msgstr "No" + +#: lib/branch_create.tcl:80 +msgid "Fast Forward Only" +msgstr "Solo fast forward" + +#: lib/branch_create.tcl:85 lib/checkout_op.tcl:514 +msgid "Reset" +msgstr "Ripristina" + +#: lib/branch_create.tcl:97 +msgid "Checkout After Creation" +msgstr "Attiva dopo la creazione" + +#: lib/branch_create.tcl:131 +msgid "Please select a tracking branch." +msgstr "Scegliere un duplicato locale di ramo remoto" + +#: lib/branch_create.tcl:140 +#, tcl-format +msgid "Tracking branch %s is not a branch in the remote repository." +msgstr "" +"Il duplicato locale del ramo remoto %s non è un ramo nell'archivio remoto." + +#: lib/branch_create.tcl:153 lib/branch_rename.tcl:86 +msgid "Please supply a branch name." +msgstr "Inserire un nome per il ramo." + +#: lib/branch_create.tcl:164 lib/branch_rename.tcl:106 +#, tcl-format +msgid "'%s' is not an acceptable branch name." +msgstr "'%s' non è utilizzabile come nome di ramo." + +#: lib/branch_delete.tcl:15 +msgid "Delete Branch" +msgstr "Elimina ramo" + +#: lib/branch_delete.tcl:20 +msgid "Delete Local Branch" +msgstr "Elimina ramo locale" + +#: lib/branch_delete.tcl:37 +msgid "Local Branches" +msgstr "Rami locali" + +#: lib/branch_delete.tcl:52 +msgid "Delete Only If Merged Into" +msgstr "Cancella solo se fuso con un altro ramo" + +#: lib/branch_delete.tcl:54 +msgid "Always (Do not perform merge test.)" +msgstr "Sempre (Non effettuare verifiche di fusione)." + +#: lib/branch_delete.tcl:103 +#, tcl-format +msgid "The following branches are not completely merged into %s:" +msgstr "I rami seguenti non sono stati fusi completamente in %s:" + +#: lib/branch_delete.tcl:115 +msgid "" +"Recovering deleted branches is difficult. \n" +"\n" +" Delete the selected branches?" +msgstr "" +"Ricomporre rami cancellati può essere complicato. \n" +"\n" +" Eliminare i rami selezionati?" + +#: lib/branch_delete.tcl:141 +#, tcl-format +msgid "" +"Failed to delete branches:\n" +"%s" +msgstr "" +"Impossibile cancellare i rami:\n" +"%s" + +#: lib/branch_rename.tcl:14 lib/branch_rename.tcl:22 +msgid "Rename Branch" +msgstr "Rinomina ramo" + +#: lib/branch_rename.tcl:26 +msgid "Rename" +msgstr "Rinomina" + +#: lib/branch_rename.tcl:36 +msgid "Branch:" +msgstr "Ramo:" + +#: lib/branch_rename.tcl:39 +msgid "New Name:" +msgstr "Nuovo Nome:" + +#: lib/branch_rename.tcl:75 +msgid "Please select a branch to rename." +msgstr "Scegliere un ramo da rinominare." + +#: lib/branch_rename.tcl:96 lib/checkout_op.tcl:179 +#, tcl-format +msgid "Branch '%s' already exists." +msgstr "Il ramo '%s' esiste già ." + +#: lib/branch_rename.tcl:117 +#, tcl-format +msgid "Failed to rename '%s'." +msgstr "Impossibile rinominare '%s'." + +#: lib/browser.tcl:17 +msgid "Starting..." +msgstr "Avvio in corso..." + +#: lib/browser.tcl:26 +msgid "File Browser" +msgstr "File browser" + +#: lib/browser.tcl:125 lib/browser.tcl:142 +#, tcl-format +msgid "Loading %s..." +msgstr "Caricamento %s..." + +#: lib/browser.tcl:186 +msgid "[Up To Parent]" +msgstr "[Directory superiore]" + +#: lib/browser.tcl:266 lib/browser.tcl:272 +msgid "Browse Branch Files" +msgstr "Esplora i file del ramo" + +#: lib/browser.tcl:277 lib/choose_repository.tcl:391 +#: lib/choose_repository.tcl:482 lib/choose_repository.tcl:492 +#: lib/choose_repository.tcl:989 +msgid "Browse" +msgstr "Sfoglia" + +#: lib/checkout_op.tcl:79 +#, tcl-format +msgid "Fetching %s from %s" +msgstr "Recupero %s da %s" + +#: lib/checkout_op.tcl:127 +#, tcl-format +msgid "fatal: Cannot resolve %s" +msgstr "errore grave: impossibile risolvere %s" + +#: lib/checkout_op.tcl:140 lib/console.tcl:79 lib/database.tcl:31 +msgid "Close" +msgstr "Chiudi" + +#: lib/checkout_op.tcl:169 +#, tcl-format +msgid "Branch '%s' does not exist." +msgstr "Il ramo '%s' non esiste." + +#: lib/checkout_op.tcl:206 +#, tcl-format +msgid "" +"Branch '%s' already exists.\n" +"\n" +"It cannot fast-forward to %s.\n" +"A merge is required." +msgstr "" +"Il ramo '%s' esiste già .\n" +"\n" +"Non può effettuare un 'fast-forward' a %s.\n" +"E' necessaria una fusione." + +#: lib/checkout_op.tcl:220 +#, tcl-format +msgid "Merge strategy '%s' not supported." +msgstr "La strategia di fusione '%s' non è supportata." + +#: lib/checkout_op.tcl:239 +#, tcl-format +msgid "Failed to update '%s'." +msgstr "Impossibile aggiornare '%s'." + +#: lib/checkout_op.tcl:251 +msgid "Staging area (index) is already locked." +msgstr "" +"L'area di preparazione per una nuova revisione (indice) è già bloccata." + +#: lib/checkout_op.tcl:266 +msgid "" +"Last scanned state does not match repository state.\n" +"\n" +"Another Git program has modified this repository since the last scan. A " +"rescan must be performed before the current branch can be changed.\n" +"\n" +"The rescan will be automatically started now.\n" +msgstr "" +"L'ultimo stato analizzato non corrisponde allo stato dell'archivio.\n" +"\n" +"Un altro programma Git ha modificato questo archivio dall'ultima analisi. " +"Bisogna effettuare una nuova analisi prima di poter cambiare il ramo " +"attuale.\n" +"\n" +"La nuova analisi comincerà ora.\n" + +#: lib/checkout_op.tcl:322 +#, tcl-format +msgid "Updating working directory to '%s'..." +msgstr "Aggiornamento della directory di lavoro a '%s' in corso..." + +#: lib/checkout_op.tcl:353 +#, tcl-format +msgid "Aborted checkout of '%s' (file level merging is required)." +msgstr "Attivazione di '%s' fallita (richiesta una fusione a livello file)." + +#: lib/checkout_op.tcl:354 +msgid "File level merge required." +msgstr "E' richiesta una fusione a livello file." + +#: lib/checkout_op.tcl:358 +#, tcl-format +msgid "Staying on branch '%s'." +msgstr "Si rimarrà sul ramo '%s'." + +#: lib/checkout_op.tcl:429 +msgid "" +"You are no longer on a local branch.\n" +"\n" +"If you wanted to be on a branch, create one now starting from 'This Detached " +"Checkout'." +msgstr "" +"Non si è più su un ramo locale\n" +"\n" +"Se si vuole rimanere su un ramo, crearne uno ora a partire da 'Questa " +"revisione attiva staccata'." + +#: lib/checkout_op.tcl:446 +#, tcl-format +msgid "Checked out '%s'." +msgstr "Attivazione di '%s' completata." + +#: lib/checkout_op.tcl:478 +#, tcl-format +msgid "Resetting '%s' to '%s' will lose the following commits:" +msgstr "" +"Ripristinare '%s' a '%s' comporterà la perdita delle seguenti revisioni:" + +#: lib/checkout_op.tcl:500 +msgid "Recovering lost commits may not be easy." +msgstr "Ricomporre le revisioni perdute potrebbe non essere semplice." + +#: lib/checkout_op.tcl:505 +#, tcl-format +msgid "Reset '%s'?" +msgstr "Ripristinare '%s'?" + +#: lib/checkout_op.tcl:510 lib/merge.tcl:164 +msgid "Visualize" +msgstr "Visualizza" + +#: lib/checkout_op.tcl:578 +#, tcl-format +msgid "" +"Failed to set current branch.\n" +"\n" +"This working directory is only partially switched. We successfully updated " +"your files, but failed to update an internal Git file.\n" +"\n" +"This should not have occurred. %s will now close and give up." +msgstr "" +"Impossibile preparare il ramo attuale.\n" +"\n" +"Questa directory di lavoro è stata convertita solo parzialmente. I file sono " +"stati aggiornati correttamente, ma l'aggiornamento di un file di Git ha " +"prodotto degli errori.\n" +"\n" +"Questo non sarebbe dovuto succedere. %s ora terminerà senza altre azioni." + +#: lib/choose_font.tcl:39 +msgid "Select" +msgstr "Seleziona" + +#: lib/choose_font.tcl:53 +msgid "Font Family" +msgstr "Famiglia di caratteri" + +#: lib/choose_font.tcl:73 +msgid "Font Size" +msgstr "Dimensione caratteri" + +#: lib/choose_font.tcl:90 +msgid "Font Example" +msgstr "Esempio caratteri" + +#: lib/choose_font.tcl:101 +msgid "" +"This is example text.\n" +"If you like this text, it can be your font." +msgstr "" +"Questo è un testo d'esempio.\n" +"Se ti piace questo testo, può essere il carattere giusto." + +#: lib/choose_repository.tcl:27 +msgid "Git Gui" +msgstr "Git Gui" + +#: lib/choose_repository.tcl:80 lib/choose_repository.tcl:380 +msgid "Create New Repository" +msgstr "Crea nuovo archivio" + +#: lib/choose_repository.tcl:86 +msgid "New..." +msgstr "Nuovo..." + +#: lib/choose_repository.tcl:93 lib/choose_repository.tcl:468 +msgid "Clone Existing Repository" +msgstr "Clona archivio esistente" + +#: lib/choose_repository.tcl:99 +msgid "Clone..." +msgstr "Clona..." + +#: lib/choose_repository.tcl:106 lib/choose_repository.tcl:978 +msgid "Open Existing Repository" +msgstr "Apri archivio esistente" + +#: lib/choose_repository.tcl:112 +msgid "Open..." +msgstr "Apri..." + +#: lib/choose_repository.tcl:125 +msgid "Recent Repositories" +msgstr "Archivi recenti" + +#: lib/choose_repository.tcl:131 +msgid "Open Recent Repository:" +msgstr "Apri archivio recente:" + +#: lib/choose_repository.tcl:294 +#, tcl-format +msgid "Location %s already exists." +msgstr "La posizione %s esiste già ." + +#: lib/choose_repository.tcl:300 lib/choose_repository.tcl:307 +#: lib/choose_repository.tcl:314 +#, tcl-format +msgid "Failed to create repository %s:" +msgstr "Impossibile creare l'archivio %s:" + +#: lib/choose_repository.tcl:385 lib/choose_repository.tcl:486 +msgid "Directory:" +msgstr "Directory:" + +#: lib/choose_repository.tcl:415 lib/choose_repository.tcl:544 +#: lib/choose_repository.tcl:1013 +msgid "Git Repository" +msgstr "Archivio Git" + +#: lib/choose_repository.tcl:430 lib/choose_repository.tcl:437 +#, tcl-format +msgid "Directory %s already exists." +msgstr "La directory %s esiste già ." + +#: lib/choose_repository.tcl:442 +#, tcl-format +msgid "File %s already exists." +msgstr "Il file %s esiste già ." + +#: lib/choose_repository.tcl:463 +msgid "Clone" +msgstr "Clona" + +#: lib/choose_repository.tcl:476 +msgid "URL:" +msgstr "URL:" + +#: lib/choose_repository.tcl:496 +msgid "Clone Type:" +msgstr "Tipo di clone:" + +#: lib/choose_repository.tcl:502 +msgid "Standard (Fast, Semi-Redundant, Hardlinks)" +msgstr "Standard (veloce, semi-ridondante, con hardlink)" + +#: lib/choose_repository.tcl:508 +msgid "Full Copy (Slower, Redundant Backup)" +msgstr "Copia completa (più lento, backup ridondante)" + +#: lib/choose_repository.tcl:514 +msgid "Shared (Fastest, Not Recommended, No Backup)" +msgstr "Shared (il più veloce, non raccomandato, nessun backup)" + +#: lib/choose_repository.tcl:550 lib/choose_repository.tcl:597 +#: lib/choose_repository.tcl:738 lib/choose_repository.tcl:808 +#: lib/choose_repository.tcl:1019 lib/choose_repository.tcl:1027 +#, tcl-format +msgid "Not a Git repository: %s" +msgstr "%s non è un archivio Git." + +#: lib/choose_repository.tcl:586 +msgid "Standard only available for local repository." +msgstr "Standard è disponibile solo per archivi locali." + +#: lib/choose_repository.tcl:590 +msgid "Shared only available for local repository." +msgstr "Shared è disponibile solo per archivi locali." + +#: lib/choose_repository.tcl:617 +msgid "Failed to configure origin" +msgstr "Impossibile configurare origin" + +#: lib/choose_repository.tcl:629 +msgid "Counting objects" +msgstr "Calcolo oggetti" + +#: lib/choose_repository.tcl:630 +msgid "buckets" +msgstr "" + +#: lib/choose_repository.tcl:654 +#, tcl-format +msgid "Unable to copy objects/info/alternates: %s" +msgstr "Impossibile copiare oggetti/info/alternate: %s" + +#: lib/choose_repository.tcl:690 +#, tcl-format +msgid "Nothing to clone from %s." +msgstr "Niente da clonare da %s." + +#: lib/choose_repository.tcl:692 lib/choose_repository.tcl:906 +#: lib/choose_repository.tcl:918 +msgid "The 'master' branch has not been initialized." +msgstr "Il ramo 'master' non è stato inizializzato." + +#: lib/choose_repository.tcl:705 +msgid "Hardlinks are unavailable. Falling back to copying." +msgstr "Impossibile utilizzare gli hardlink. Si ricorrerà alla copia." + +#: lib/choose_repository.tcl:717 +#, tcl-format +msgid "Cloning from %s" +msgstr "Clonazione da %s" + +#: lib/choose_repository.tcl:748 +msgid "Copying objects" +msgstr "Copia degli oggetti" + +#: lib/choose_repository.tcl:749 +msgid "KiB" +msgstr "KiB" + +#: lib/choose_repository.tcl:773 +#, tcl-format +msgid "Unable to copy object: %s" +msgstr "Impossibile copiare oggetto: %s" + +#: lib/choose_repository.tcl:783 +msgid "Linking objects" +msgstr "Collegamento oggetti" + +#: lib/choose_repository.tcl:784 +msgid "objects" +msgstr "oggetti" + +#: lib/choose_repository.tcl:792 +#, tcl-format +msgid "Unable to hardlink object: %s" +msgstr "Hardlink impossibile sull'oggetto: %s" + +#: lib/choose_repository.tcl:847 +msgid "Cannot fetch branches and objects. See console output for details." +msgstr "" +"Impossibile recuperare rami e oggetti. Controllare i dettagli forniti dalla " +"console." + +#: lib/choose_repository.tcl:858 +msgid "Cannot fetch tags. See console output for details." +msgstr "" +"Impossibile recuperare le etichette. Controllare i dettagli forniti dalla " +"console." + +#: lib/choose_repository.tcl:882 +msgid "Cannot determine HEAD. See console output for details." +msgstr "" +"Impossibile determinare HEAD. Controllare i dettagli forniti dalla console." + +#: lib/choose_repository.tcl:891 +#, tcl-format +msgid "Unable to cleanup %s" +msgstr "Impossibile ripulire %s" + +#: lib/choose_repository.tcl:897 +msgid "Clone failed." +msgstr "Clonazione non riuscita." + +#: lib/choose_repository.tcl:904 +msgid "No default branch obtained." +msgstr "Non è stato trovato un ramo predefinito." + +#: lib/choose_repository.tcl:915 +#, tcl-format +msgid "Cannot resolve %s as a commit." +msgstr "Impossibile risolvere %s come una revisione." + +#: lib/choose_repository.tcl:927 +msgid "Creating working directory" +msgstr "Creazione directory di lavoro" + +#: lib/choose_repository.tcl:928 lib/index.tcl:65 lib/index.tcl:127 +#: lib/index.tcl:193 +msgid "files" +msgstr "file" + +#: lib/choose_repository.tcl:957 +msgid "Initial file checkout failed." +msgstr "Attivazione iniziale non riuscita." + +#: lib/choose_repository.tcl:973 +msgid "Open" +msgstr "Apri" + +#: lib/choose_repository.tcl:983 +msgid "Repository:" +msgstr "Archivio:" + +#: lib/choose_repository.tcl:1033 +#, tcl-format +msgid "Failed to open repository %s:" +msgstr "Impossibile accedere all'archivio %s:" + +#: lib/choose_rev.tcl:53 +msgid "This Detached Checkout" +msgstr "Questa revisione attiva staccata" + +#: lib/choose_rev.tcl:60 +msgid "Revision Expression:" +msgstr "Espressione di revisione:" + +#: lib/choose_rev.tcl:74 +msgid "Local Branch" +msgstr "Ramo locale" + +#: lib/choose_rev.tcl:79 +msgid "Tracking Branch" +msgstr "Duplicato locale di ramo remoto" + +#: lib/choose_rev.tcl:84 lib/choose_rev.tcl:537 +msgid "Tag" +msgstr "Etichetta" + +#: lib/choose_rev.tcl:317 +#, tcl-format +msgid "Invalid revision: %s" +msgstr "Revisione non valida: %s" + +#: lib/choose_rev.tcl:338 +msgid "No revision selected." +msgstr "Nessuna revisione selezionata." + +#: lib/choose_rev.tcl:346 +msgid "Revision expression is empty." +msgstr "L'espressione di revisione è vuota." + +#: lib/choose_rev.tcl:530 +msgid "Updated" +msgstr "Aggiornato" + +#: lib/choose_rev.tcl:558 +msgid "URL" +msgstr "URL" + +#: lib/commit.tcl:9 +msgid "" +"There is nothing to amend.\n" +"\n" +"You are about to create the initial commit. There is no commit before this " +"to amend.\n" +msgstr "" +"Non c'è niente da correggere.\n" +"\n" +"Stai per creare la revisione iniziale. Non esiste una revisione precedente " +"da correggere.\n" + +#: lib/commit.tcl:18 +msgid "" +"Cannot amend while merging.\n" +"\n" +"You are currently in the middle of a merge that has not been fully " +"completed. You cannot amend the prior commit unless you first abort the " +"current merge activity.\n" +msgstr "" +"Non è possibile effettuare una correzione durante una fusione.\n" +"\n" +"In questo momento si sta effettuando una fusione che non è stata del tutto " +"completata. Non puoi correggere la revisione precedente a meno che prima tu " +"non interrompa l'operazione di fusione in corso.\n" + +#: lib/commit.tcl:49 +msgid "Error loading commit data for amend:" +msgstr "Errore durante il caricamento dei dati della revisione da correggere:" + +#: lib/commit.tcl:76 +msgid "Unable to obtain your identity:" +msgstr "Impossibile ottenere la tua identità :" + +#: lib/commit.tcl:81 +msgid "Invalid GIT_COMMITTER_IDENT:" +msgstr "GIT_COMMITTER_IDENT non valida:" + +#: lib/commit.tcl:133 +msgid "" +"Last scanned state does not match repository state.\n" +"\n" +"Another Git program has modified this repository since the last scan. A " +"rescan must be performed before another commit can be created.\n" +"\n" +"The rescan will be automatically started now.\n" +msgstr "" +"L'ultimo stato analizzato non corrisponde allo stato dell'archivio.\n" +"\n" +"Un altro programma Git ha modificato questo archivio dall'ultima analisi. " +"Bisogna effettuare una nuova analisi prima di poter creare una nuova " +"revisione.\n" +"\n" +"La nuova analisi comincerà ora.\n" + +#: lib/commit.tcl:154 +#, tcl-format +msgid "" +"Unmerged files cannot be committed.\n" +"\n" +"File %s has merge conflicts. You must resolve them and stage the file " +"before committing.\n" +msgstr "" +"Non è possibile creare una revisione con file non sottoposti a fusione.\n" +"\n" +"Il file %s presenta dei conflitti. Devi risolverli e preparare il file per " +"creare una nuova revisione prima di effettuare questa azione.\n" + +#: lib/commit.tcl:162 +#, tcl-format +msgid "" +"Unknown file state %s detected.\n" +"\n" +"File %s cannot be committed by this program.\n" +msgstr "" +"Stato di file %s sconosciuto.\n" +"\n" +"Questo programma non può creare una revisione contenente il file %s.\n" + +#: lib/commit.tcl:170 +msgid "" +"No changes to commit.\n" +"\n" +"You must stage at least 1 file before you can commit.\n" +msgstr "" +"Nessuna modifica per la nuova revisione.\n" +"\n" +"Devi preparare per una nuova revisione almeno 1 file prima di effettuare " +"questa operazione.\n" + +#: lib/commit.tcl:183 +msgid "" +"Please supply a commit message.\n" +"\n" +"A good commit message has the following format:\n" +"\n" +"- First line: Describe in one sentance what you did.\n" +"- Second line: Blank\n" +"- Remaining lines: Describe why this change is good.\n" +msgstr "" +"Bisogna fornire un messaggio di revisione.\n" +"\n" +"Un buon messaggio di revisione ha il seguente formato:\n" +"\n" +"- Prima linea: descrivi in una frase ciò che hai fatto.\n" +"- Seconda linea: vuota.\n" +"- Terza linea: spiega a cosa serve la tua modifica.\n" + +#: lib/commit.tcl:257 +msgid "write-tree failed:" +msgstr "write-tree non riuscito:" + +#: lib/commit.tcl:275 +#, tcl-format +msgid "Commit %s appears to be corrupt" +msgstr "La revisione %s sembra essere danneggiata" + +#: lib/commit.tcl:279 +msgid "" +"No changes to commit.\n" +"\n" +"No files were modified by this commit and it was not a merge commit.\n" +"\n" +"A rescan will be automatically started now.\n" +msgstr "" +"Nessuna modifica per la nuova revisione.\n" +"\n" +"Questa revisione non modifica alcun file e non effettua alcuna fusione.\n" +"\n" +"Si procederà subito ad una nuova analisi.\n" + +#: lib/commit.tcl:286 +msgid "No changes to commit." +msgstr "Nessuna modifica per la nuova revisione." + +#: lib/commit.tcl:303 +#, tcl-format +msgid "warning: Tcl does not support encoding '%s'." +msgstr "attenzione: Tcl non supporta la codifica '%s'." + +#: lib/commit.tcl:317 +msgid "commit-tree failed:" +msgstr "commit-tree non riuscito:" + +#: lib/commit.tcl:339 +msgid "update-ref failed:" +msgstr "update-ref non riuscito:" + +#: lib/commit.tcl:430 +#, tcl-format +msgid "Created commit %s: %s" +msgstr "Creata revisione %s: %s" + +#: lib/console.tcl:57 +msgid "Working... please wait..." +msgstr "Elaborazione in corso... attendere..." + +#: lib/console.tcl:183 +msgid "Success" +msgstr "Successo" + +#: lib/console.tcl:196 +msgid "Error: Command Failed" +msgstr "Errore: comando non riuscito" + +#: lib/database.tcl:43 +msgid "Number of loose objects" +msgstr "Numero di oggetti slegati" + +#: lib/database.tcl:44 +msgid "Disk space used by loose objects" +msgstr "Spazio su disco utilizzato da oggetti slegati" + +#: lib/database.tcl:45 +msgid "Number of packed objects" +msgstr "Numero di oggetti impacchettati" + +#: lib/database.tcl:46 +msgid "Number of packs" +msgstr "Numero di pacchetti" + +#: lib/database.tcl:47 +msgid "Disk space used by packed objects" +msgstr "Spazio su disco utilizzato da oggetti impacchettati" + +#: lib/database.tcl:48 +msgid "Packed objects waiting for pruning" +msgstr "Oggetti impacchettati che attendono la potatura" + +#: lib/database.tcl:49 +msgid "Garbage files" +msgstr "File inutili" + +#: lib/database.tcl:72 +msgid "Compressing the object database" +msgstr "Compressione dell'archivio in corso" + +#: lib/database.tcl:83 +msgid "Verifying the object database with fsck-objects" +msgstr "Verifica dell'archivio con fsck-objects in corso" + +#: lib/database.tcl:108 +#, tcl-format +msgid "" +"This repository currently has approximately %i loose objects.\n" +"\n" +"To maintain optimal performance it is strongly recommended that you compress " +"the database when more than %i loose objects exist.\n" +"\n" +"Compress the database now?" +msgstr "" +"Questo archivio attualmente ha circa %i oggetti slegati.\n" +"\n" +"Per mantenere buone prestazioni si raccomanda di comprimere l'archivio " +"quando sono presenti più di %i oggetti slegati.\n" +"\n" +"Comprimere l'archivio ora?" + +#: lib/date.tcl:25 +#, tcl-format +msgid "Invalid date from Git: %s" +msgstr "Git ha restituito una data non valida: %s" + +#: lib/diff.tcl:42 +#, tcl-format +msgid "" +"No differences detected.\n" +"\n" +"%s has no changes.\n" +"\n" +"The modification date of this file was updated by another application, but " +"the content within the file was not changed.\n" +"\n" +"A rescan will be automatically started to find other files which may have " +"the same state." +msgstr "" +"Non sono state trovate differenze.\n" +"\n" +"%s non ha modifiche.\n" +"\n" +"La data di modifica di questo file è stata cambiata da un'altra " +"applicazione, ma il contenuto del file è rimasto invariato.\n" +"\n" +"Si procederà automaticamente ad una nuova analisi per trovare altri file che " +"potrebbero avere lo stesso stato." + +#: lib/diff.tcl:81 +#, tcl-format +msgid "Loading diff of %s..." +msgstr "Caricamento delle differenze di %s..." + +#: lib/diff.tcl:114 lib/diff.tcl:184 +#, tcl-format +msgid "Unable to display %s" +msgstr "Impossibile visualizzare %s" + +#: lib/diff.tcl:115 +msgid "Error loading file:" +msgstr "Errore nel caricamento del file:" + +#: lib/diff.tcl:122 +msgid "Git Repository (subproject)" +msgstr "Archivio Git (sottoprogetto)" + +#: lib/diff.tcl:134 +msgid "* Binary file (not showing content)." +msgstr "* File binario (il contenuto non sarà mostrato)." + +#: lib/diff.tcl:185 +msgid "Error loading diff:" +msgstr "Errore nel caricamento delle differenze:" + +#: lib/diff.tcl:302 +msgid "Failed to unstage selected hunk." +msgstr "Impossibile rimuovere la sezione scelta dalla nuova revisione." + +#: lib/diff.tcl:309 +msgid "Failed to stage selected hunk." +msgstr "Impossibile preparare la sezione scelta per una nuova revisione." + +#: lib/error.tcl:12 lib/error.tcl:102 +msgid "error" +msgstr "errore" + +#: lib/error.tcl:28 +msgid "warning" +msgstr "attenzione" + +#: lib/error.tcl:81 +msgid "You must correct the above errors before committing." +msgstr "" +"Bisogna correggere gli errori suddetti prima di creare una nuova revisione." + +#: lib/index.tcl:6 +msgid "Unable to unlock the index." +msgstr "Impossibile sbloccare l'accesso all'indice" + +#: lib/index.tcl:15 +msgid "Index Error" +msgstr "Errore nell'indice" + +#: lib/index.tcl:21 +msgid "" +"Updating the Git index failed. A rescan will be automatically started to " +"resynchronize git-gui." +msgstr "" +"Impossibile aggiornare l'indice. Ora sarà avviata una nuova analisi che " +"aggiornerà git-gui." + +#: lib/index.tcl:27 +msgid "Continue" +msgstr "Continua" + +#: lib/index.tcl:31 +msgid "Unlock Index" +msgstr "Sblocca l'accesso all'indice" + +#: lib/index.tcl:282 +#, tcl-format +msgid "Unstaging %s from commit" +msgstr "%s non farà parte della prossima revisione" + +#: lib/index.tcl:326 +#, tcl-format +msgid "Adding %s" +msgstr "Aggiunta di %s in corso" + +#: lib/index.tcl:381 +#, tcl-format +msgid "Revert changes in file %s?" +msgstr "Annullare le modifiche nel file %s?" + +#: lib/index.tcl:383 +#, tcl-format +msgid "Revert changes in these %i files?" +msgstr "Annullare le modifiche in questi %i file?" + +#: lib/index.tcl:389 +msgid "Any unstaged changes will be permanently lost by the revert." +msgstr "" +"Tutte le modifiche non preparate per una nuova revisione saranno perse per " +"sempre." + +#: lib/index.tcl:392 +msgid "Do Nothing" +msgstr "Non fare niente" + +#: lib/merge.tcl:13 +msgid "" +"Cannot merge while amending.\n" +"\n" +"You must finish amending this commit before starting any type of merge.\n" +msgstr "" +"Non posso effettuare fusioni durante una correzione.\n" +"\n" +"Bisogna finire di correggere questa revisione prima di iniziare una " +"qualunque fusione.\n" + +#: lib/merge.tcl:27 +msgid "" +"Last scanned state does not match repository state.\n" +"\n" +"Another Git program has modified this repository since the last scan. A " +"rescan must be performed before a merge can be performed.\n" +"\n" +"The rescan will be automatically started now.\n" +msgstr "" +"L'ultimo stato analizzato non corrisponde allo stato dell'archivio.\n" +"\n" +"Un altro programma Git ha modificato questo archivio dall'ultima analisi." +"Bisogna effettuare una nuova analisi prima di poter effettuare una fusione.\n" +"\n" +"La nuova analisi comincerà ora.\n" + +#: lib/merge.tcl:44 +#, tcl-format +msgid "" +"You are in the middle of a conflicted merge.\n" +"\n" +"File %s has merge conflicts.\n" +"\n" +"You must resolve them, stage the file, and commit to complete the current " +"merge. Only then can you begin another merge.\n" +msgstr "" +"Sei nel mezzo di una fusione con conflitti.\n" +"\n" +"Il file %s ha dei conflitti.\n" +"\n" +"Bisogna risolvere i conflitti, preparare il file per una nuova revisione ed " +"infine crearla per completare la fusione attuale. Solo a questo punto potrai " +"iniziare un'altra fusione.\n" + +#: lib/merge.tcl:54 +#, tcl-format +msgid "" +"You are in the middle of a change.\n" +"\n" +"File %s is modified.\n" +"\n" +"You should complete the current commit before starting a merge. Doing so " +"will help you abort a failed merge, should the need arise.\n" +msgstr "" +"Sei nel mezzo di una modifica.\n" +"\n" +"Il file %s è stato modificato.\n" +"\n" +"Bisogna completare la creazione della revisione attuale prima di iniziare " +"una fusione. In questo modo sarà più facile interrompere una fusione non " +"riuscita, nel caso ce ne fosse bisogno.\n" + +#: lib/merge.tcl:106 +#, tcl-format +msgid "%s of %s" +msgstr "%s di %s" + +#: lib/merge.tcl:119 +#, tcl-format +msgid "Merging %s and %s" +msgstr "Fusione di %s e %s in corso" + +#: lib/merge.tcl:131 +msgid "Merge completed successfully." +msgstr "Fusione completata con successo." + +#: lib/merge.tcl:133 +msgid "Merge failed. Conflict resolution is required." +msgstr "Fusione non riuscita. Bisogna risolvere i conflitti." + +#: lib/merge.tcl:158 +#, tcl-format +msgid "Merge Into %s" +msgstr "Fusione in %s" + +#: lib/merge.tcl:177 +msgid "Revision To Merge" +msgstr "Revisione da fondere" + +#: lib/merge.tcl:212 +msgid "" +"Cannot abort while amending.\n" +"\n" +"You must finish amending this commit.\n" +msgstr "" +"Interruzione impossibile durante una correzione.\n" +"\n" +"Bisogna finire di correggere questa revisione.\n" + +#: lib/merge.tcl:222 +msgid "" +"Abort merge?\n" +"\n" +"Aborting the current merge will cause *ALL* uncommitted changes to be lost.\n" +"\n" +"Continue with aborting the current merge?" +msgstr "" +"Interrompere fusione?\n" +"\n" +"L'interruzione della fusione attuale causerà la perdita di *TUTTE* le " +"modifiche non ancora presenti nell'archivio.\n" +"\n" +"Continuare con l'interruzione della fusione attuale?" + +#: lib/merge.tcl:228 +msgid "" +"Reset changes?\n" +"\n" +"Resetting the changes will cause *ALL* uncommitted changes to be lost.\n" +"\n" +"Continue with resetting the current changes?" +msgstr "" +"Ripristinare la revisione corrente e annullare le modifiche?\n" +"\n" +"L'annullamento delle modifiche causerà la perdita di *TUTTE* le modifiche " +"non ancora presenti nell'archivio.\n" +"\n" +"Continuare con l'annullamento delle modifiche attuali?" + +#: lib/merge.tcl:239 +msgid "Aborting" +msgstr "Interruzione in corso" + +#: lib/merge.tcl:266 +msgid "Abort failed." +msgstr "Interruzione non riuscita." + +#: lib/merge.tcl:268 +msgid "Abort completed. Ready." +msgstr "Interruzione completata. Pronto." + +#: lib/option.tcl:82 +msgid "Restore Defaults" +msgstr "Ripristina valori predefiniti" + +#: lib/option.tcl:86 +msgid "Save" +msgstr "Salva" + +#: lib/option.tcl:96 +#, tcl-format +msgid "%s Repository" +msgstr "Archivio di %s" + +#: lib/option.tcl:97 +msgid "Global (All Repositories)" +msgstr "Tutti gli archivi" + +#: lib/option.tcl:103 +msgid "User Name" +msgstr "Nome utente" + +#: lib/option.tcl:104 +msgid "Email Address" +msgstr "Indirizzo Email" + +#: lib/option.tcl:106 +msgid "Summarize Merge Commits" +msgstr "Riepilogo nelle revisioni di fusione" + +#: lib/option.tcl:107 +msgid "Merge Verbosity" +msgstr "Prolissità della fusione" + +#: lib/option.tcl:108 +msgid "Show Diffstat After Merge" +msgstr "Mostra statistiche delle differenze dopo la fusione" + +#: lib/option.tcl:110 +msgid "Trust File Modification Timestamps" +msgstr "Fidati delle date di modifica dei file" + +#: lib/option.tcl:111 +msgid "Prune Tracking Branches During Fetch" +msgstr "" +"Effettua potatura dei duplicati locali di rami remoti durante il recupero" + +#: lib/option.tcl:112 +msgid "Match Tracking Branches" +msgstr "Appaia duplicati locali di rami remoti" + +#: lib/option.tcl:113 +msgid "Number of Diff Context Lines" +msgstr "Numero di linee di contesto nelle differenze" + +#: lib/option.tcl:114 +msgid "New Branch Name Template" +msgstr "Modello per il nome di un nuovo ramo" + +#: lib/option.tcl:176 +msgid "Change Font" +msgstr "Cambia caratteri" + +#: lib/option.tcl:180 +#, tcl-format +msgid "Choose %s" +msgstr "Scegli %s" + +#: lib/option.tcl:186 +msgid "pt." +msgstr "pt." + +#: lib/option.tcl:200 +msgid "Preferences" +msgstr "Preferenze" + +#: lib/option.tcl:235 +msgid "Failed to completely save options:" +msgstr "Impossibile salvare completamente le opzioni:" + +#: lib/remote.tcl:165 +msgid "Prune from" +msgstr "Effettua potatura da" + +#: lib/remote.tcl:170 +msgid "Fetch from" +msgstr "Recupera da" + +#: lib/remote.tcl:213 +msgid "Push to" +msgstr "Propaga verso" + +#: lib/remote_branch_delete.tcl:29 lib/remote_branch_delete.tcl:34 +msgid "Delete Remote Branch" +msgstr "Cancella ramo remoto" + +#: lib/remote_branch_delete.tcl:47 +msgid "From Repository" +msgstr "Da archivio" + +#: lib/remote_branch_delete.tcl:50 lib/transport.tcl:123 +msgid "Remote:" +msgstr "Remoto:" + +#: lib/remote_branch_delete.tcl:66 lib/transport.tcl:138 +msgid "Arbitrary URL:" +msgstr "URL specifico:" + +#: lib/remote_branch_delete.tcl:84 +msgid "Branches" +msgstr "Rami" + +#: lib/remote_branch_delete.tcl:109 +msgid "Delete Only If" +msgstr "Elimina solo se" + +#: lib/remote_branch_delete.tcl:111 +msgid "Merged Into:" +msgstr "Fuso in:" + +#: lib/remote_branch_delete.tcl:119 +msgid "Always (Do not perform merge checks)" +msgstr "Sempre (non verificare le fusioni)" + +#: lib/remote_branch_delete.tcl:152 +msgid "A branch is required for 'Merged Into'." +msgstr "Si richiede un ramo per 'Fuso in'." + +#: lib/remote_branch_delete.tcl:184 +#, tcl-format +msgid "" +"The following branches are not completely merged into %s:\n" +"\n" +" - %s" +msgstr "" +"I rami seguenti non sono stati fusi completamente in %s:\n" +"\n" +" - %s" + +#: lib/remote_branch_delete.tcl:189 +#, tcl-format +msgid "" +"One or more of the merge tests failed because you have not fetched the " +"necessary commits. Try fetching from %s first." +msgstr "" +"Impossibile verificare una o più fusioni: mancano le revisioni necessarie. " +"Prova prima a recuperarle da %s." + +#: lib/remote_branch_delete.tcl:207 +msgid "Please select one or more branches to delete." +msgstr "Scegliere uno o più rami da cancellare." + +#: lib/remote_branch_delete.tcl:216 +msgid "" +"Recovering deleted branches is difficult.\n" +"\n" +"Delete the selected branches?" +msgstr "" +"Ricomporre rami cancellati è difficile.\n" +"\n" +"Cancellare i rami selezionati?" + +#: lib/remote_branch_delete.tcl:226 +#, tcl-format +msgid "Deleting branches from %s" +msgstr "Cancellazione rami da %s" + +#: lib/remote_branch_delete.tcl:286 +msgid "No repository selected." +msgstr "Nessun archivio selezionato." + +#: lib/remote_branch_delete.tcl:291 +#, tcl-format +msgid "Scanning %s..." +msgstr "Analisi in corso %s..." + +#: lib/shortcut.tcl:20 lib/shortcut.tcl:61 +msgid "Cannot write shortcut:" +msgstr "Impossibile scrivere shortcut:" + +#: lib/shortcut.tcl:136 +msgid "Cannot write icon:" +msgstr "Impossibile scrivere icona:" + +#: lib/status_bar.tcl:83 +#, tcl-format +msgid "%s ... %*i of %*i %s (%3i%%)" +msgstr "%1$s ... %6$s: %2$*i di %4$*i (%7$3i%%)" + +#: lib/transport.tcl:6 +#, tcl-format +msgid "fetch %s" +msgstr "recupera da %s" + +#: lib/transport.tcl:7 +#, tcl-format +msgid "Fetching new changes from %s" +msgstr "Recupero nuove modifiche da %s" + +#: lib/transport.tcl:18 +#, tcl-format +msgid "remote prune %s" +msgstr "potatura remota di %s" + +#: lib/transport.tcl:19 +#, tcl-format +msgid "Pruning tracking branches deleted from %s" +msgstr "Effettua potatura dei duplicati locali di rami remoti cancellati da %s" + +#: lib/transport.tcl:25 lib/transport.tcl:71 +#, tcl-format +msgid "push %s" +msgstr "propaga verso %s" + +#: lib/transport.tcl:26 +#, tcl-format +msgid "Pushing changes to %s" +msgstr "Propagazione modifiche a %s" + +#: lib/transport.tcl:72 +#, tcl-format +msgid "Pushing %s %s to %s" +msgstr "Propagazione %s %s a %s" + +#: lib/transport.tcl:89 +msgid "Push Branches" +msgstr "Propaga rami" + +#: lib/transport.tcl:103 +msgid "Source Branches" +msgstr "Rami di origine" + +#: lib/transport.tcl:120 +msgid "Destination Repository" +msgstr "Archivio di destinazione" + +#: lib/transport.tcl:158 +msgid "Transfer Options" +msgstr "Opzioni di trasferimento" + +#: lib/transport.tcl:160 +msgid "Force overwrite existing branch (may discard changes)" +msgstr "Sovrascrivi ramo esistente (alcune modifiche potrebbero essere perse)" + +#: lib/transport.tcl:164 +msgid "Use thin pack (for slow network connections)" +msgstr "Utilizza 'thin pack' (per connessioni lente)" + +#: lib/transport.tcl:168 +msgid "Include tags" +msgstr "Includi etichette" diff --git a/git-gui/po/ja.po b/git-gui/po/ja.po new file mode 100644 index 0000000000..e2cf5bdc06 --- /dev/null +++ b/git-gui/po/ja.po @@ -0,0 +1,1883 @@ +# Translation of git-gui to Japanese +# Copyright (C) 2007 Shawn Pearce +# This file is distributed under the same license as the git-gui package. +# ã—らã„ã— ãªãªã“ <nanako3@bluebottle.com>, 2007. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: git-gui\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2007-11-24 10:36+0100\n" +"PO-Revision-Date: 2007-12-05 06:12+0900\n" +"Last-Translator: ã—らã„ã— ãªãªã“ <nanako3@bluebottle.com>\n" +"Language-Team: Japanese\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: git-gui.sh:41 git-gui.sh:604 git-gui.sh:618 git-gui.sh:631 git-gui.sh:714 +#: git-gui.sh:733 +msgid "git-gui: fatal error" +msgstr "git-gui: 致命的ãªã‚¨ãƒ©ãƒ¼" + +#: git-gui.sh:565 +#, tcl-format +msgid "Invalid font specified in %s:" +msgstr "%s ã«ç„¡åŠ¹ãªãƒ•ã‚©ãƒ³ãƒˆãŒæŒ‡å®šã•ã‚Œã¦ã„ã¾ã™:" + +#: git-gui.sh:590 +msgid "Main Font" +msgstr "主フォント" + +#: git-gui.sh:591 +msgid "Diff/Console Font" +msgstr "diff/コンソール・フォント" + +#: git-gui.sh:605 +msgid "Cannot find git in PATH." +msgstr "PATH ä¸ã« git ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“" + +#: git-gui.sh:632 +msgid "Cannot parse Git version string:" +msgstr "Git ãƒãƒ¼ã‚¸ãƒ§ãƒ³åãŒç†è§£ã§ãã¾ã›ã‚“:" + +#: git-gui.sh:650 +#, tcl-format +msgid "" +"Git version cannot be determined.\n" +"\n" +"%s claims it is version '%s'.\n" +"\n" +"%s requires at least Git 1.5.0 or later.\n" +"\n" +"Assume '%s' is version 1.5.0?\n" +msgstr "" +"Git ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ãŒç¢ºèªã§ãã¾ã›ã‚“。\n" +"\n" +"%s ã¯ãƒãƒ¼ã‚¸ãƒ§ãƒ³ '%s' ã¨ã®ã“ã¨ã§ã™ã€‚\n" +"\n" +"%s ã¯æœ€ä½Žã§ã‚‚ 1.5.0 ã‹ãれ以é™ã® Git ãŒå¿…è¦ã§ã™\n" +"\n" +"'%s' ã¯ãƒãƒ¼ã‚¸ãƒ§ãƒ³ 1.5.0 ã¨æ€ã£ã¦è‰¯ã„ã§ã™ã‹ï¼Ÿ\n" + +#: git-gui.sh:888 +msgid "Git directory not found:" +msgstr "Git ディレクトリãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“:" + +#: git-gui.sh:895 +msgid "Cannot move to top of working directory:" +msgstr "作æ¥ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã®æœ€ä¸Šä½ã«ç§»å‹•ã§ãã¾ã›ã‚“" + +#: git-gui.sh:902 +msgid "Cannot use funny .git directory:" +msgstr "変㪠.git ディレクトリã¯ä½¿ãˆã¾ã›ã‚“" + +#: git-gui.sh:907 +msgid "No working directory" +msgstr "作æ¥ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªãŒã‚ã‚Šã¾ã›ã‚“" + +#: git-gui.sh:1054 +msgid "Refreshing file status..." +msgstr "ファイル状態を更新ã—ã¦ã„ã¾ã™â€¦" + +#: git-gui.sh:1119 +msgid "Scanning for modified files ..." +msgstr "変更ã•ã‚ŒãŸãƒ•ã‚¡ã‚¤ãƒ«ã‚’スã‚ャンã—ã¦ã„ã¾ã™â€¦" + +#: git-gui.sh:1294 lib/browser.tcl:245 +msgid "Ready." +msgstr "準備完了" + +#: git-gui.sh:1560 +msgid "Unmodified" +msgstr "変更無ã—" + +#: git-gui.sh:1562 +msgid "Modified, not staged" +msgstr "変更ã‚ã‚Šã€ã‚³ãƒŸãƒƒãƒˆæœªäºˆå®š" + +#: git-gui.sh:1563 git-gui.sh:1568 +msgid "Staged for commit" +msgstr "コミット予定済" + +#: git-gui.sh:1564 git-gui.sh:1569 +msgid "Portions staged for commit" +msgstr "部分的ã«ã‚³ãƒŸãƒƒãƒˆäºˆå®šæ¸ˆ" + +#: git-gui.sh:1565 git-gui.sh:1570 +msgid "Staged for commit, missing" +msgstr "コミット予定済ã€ãƒ•ã‚¡ã‚¤ãƒ«ç„¡ã—" + +#: git-gui.sh:1567 +msgid "Untracked, not staged" +msgstr "管ç†å¤–ã€ã‚³ãƒŸãƒƒãƒˆæœªäºˆå®š" + +#: git-gui.sh:1572 +msgid "Missing" +msgstr "ファイル無ã—" + +#: git-gui.sh:1573 +msgid "Staged for removal" +msgstr "削除予定済" + +#: git-gui.sh:1574 +msgid "Staged for removal, still present" +msgstr "削除予定済ã€ãƒ•ã‚¡ã‚¤ãƒ«æœªå‰Šé™¤" + +#: git-gui.sh:1576 git-gui.sh:1577 git-gui.sh:1578 git-gui.sh:1579 +msgid "Requires merge resolution" +msgstr "è¦ãƒžãƒ¼ã‚¸è§£æ±º" + +#: git-gui.sh:1614 +msgid "Starting gitk... please wait..." +msgstr "gitk ã‚’èµ·å‹•ä¸â€¦ãŠå¾…ã¡ä¸‹ã•ã„…" + +#: git-gui.sh:1623 +#, tcl-format +msgid "" +"Unable to start gitk:\n" +"\n" +"%s does not exist" +msgstr "" +"gitk ã‚’èµ·å‹•ã§ãã¾ã›ã‚“:\n" +"\n" +"%s ãŒã‚ã‚Šã¾ã›ã‚“" + +#: git-gui.sh:1823 lib/choose_repository.tcl:35 +msgid "Repository" +msgstr "リãƒã‚¸ãƒˆãƒª" + +#: git-gui.sh:1824 +msgid "Edit" +msgstr "編集" + +#: git-gui.sh:1826 lib/choose_rev.tcl:560 +msgid "Branch" +msgstr "ブランãƒ" + +#: git-gui.sh:1829 lib/choose_rev.tcl:547 +msgid "Commit@@noun" +msgstr "コミット" + +#: git-gui.sh:1832 lib/merge.tcl:121 lib/merge.tcl:150 lib/merge.tcl:168 +msgid "Merge" +msgstr "マージ" + +#: git-gui.sh:1833 lib/choose_rev.tcl:556 +msgid "Remote" +msgstr "リモート" + +#: git-gui.sh:1842 +msgid "Browse Current Branch's Files" +msgstr "ç¾åœ¨ã®ãƒ–ランãƒã®ãƒ•ã‚¡ã‚¤ãƒ«ã‚’見る" + +#: git-gui.sh:1846 +msgid "Browse Branch Files..." +msgstr "ブランãƒã®ãƒ•ã‚¡ã‚¤ãƒ«ã‚’見る…" + +#: git-gui.sh:1851 +msgid "Visualize Current Branch's History" +msgstr "ç¾åœ¨ã®ãƒ–ランãƒã®å±¥æ´ã‚’見る" + +#: git-gui.sh:1855 +msgid "Visualize All Branch History" +msgstr "å…¨ã¦ã®ãƒ–ランãƒã®å±¥æ´ã‚’見る" + +#: git-gui.sh:1862 +#, tcl-format +msgid "Browse %s's Files" +msgstr "ブランム%s ã®ãƒ•ã‚¡ã‚¤ãƒ«ã‚’見る" + +#: git-gui.sh:1864 +#, tcl-format +msgid "Visualize %s's History" +msgstr "ブランム%s ã®å±¥æ´ã‚’見る" + +#: git-gui.sh:1869 lib/database.tcl:27 lib/database.tcl:67 +msgid "Database Statistics" +msgstr "データベース統計" + +#: git-gui.sh:1872 lib/database.tcl:34 +msgid "Compress Database" +msgstr "データベース圧縮" + +#: git-gui.sh:1875 +msgid "Verify Database" +msgstr "データベース検証" + +#: git-gui.sh:1882 git-gui.sh:1886 git-gui.sh:1890 lib/shortcut.tcl:7 +#: lib/shortcut.tcl:39 lib/shortcut.tcl:71 +msgid "Create Desktop Icon" +msgstr "デスクトップ・アイコンを作る" + +#: git-gui.sh:1895 lib/choose_repository.tcl:176 lib/choose_repository.tcl:184 +msgid "Quit" +msgstr "終了" + +#: git-gui.sh:1902 +msgid "Undo" +msgstr "å…ƒã«æˆ»ã™" + +#: git-gui.sh:1905 +msgid "Redo" +msgstr "ã‚„ã‚Šç›´ã—" + +#: git-gui.sh:1909 git-gui.sh:2403 +msgid "Cut" +msgstr "切りå–ã‚Š" + +#: git-gui.sh:1912 git-gui.sh:2406 git-gui.sh:2477 git-gui.sh:2549 +#: lib/console.tcl:67 +msgid "Copy" +msgstr "コピー" + +#: git-gui.sh:1915 git-gui.sh:2409 +msgid "Paste" +msgstr "貼り付ã‘" + +#: git-gui.sh:1918 git-gui.sh:2412 lib/branch_delete.tcl:26 +#: lib/remote_branch_delete.tcl:38 +msgid "Delete" +msgstr "削除" + +#: git-gui.sh:1922 git-gui.sh:2416 git-gui.sh:2553 lib/console.tcl:69 +msgid "Select All" +msgstr "å…¨ã¦é¸æŠž" + +#: git-gui.sh:1931 +msgid "Create..." +msgstr "作æˆâ€¦" + +#: git-gui.sh:1937 +msgid "Checkout..." +msgstr "ãƒã‚§ãƒƒã‚¯ã‚¢ã‚¦ãƒˆ" + +#: git-gui.sh:1943 +msgid "Rename..." +msgstr "åå‰å¤‰æ›´â€¦" + +#: git-gui.sh:1948 git-gui.sh:2048 +msgid "Delete..." +msgstr "削除…" + +#: git-gui.sh:1953 +msgid "Reset..." +msgstr "リセット…" + +#: git-gui.sh:1965 git-gui.sh:2350 +msgid "New Commit" +msgstr "æ–°è¦ã‚³ãƒŸãƒƒãƒˆ" + +#: git-gui.sh:1973 git-gui.sh:2357 +msgid "Amend Last Commit" +msgstr "最新コミットを訂æ£" + +#: git-gui.sh:1982 git-gui.sh:2317 lib/remote_branch_delete.tcl:99 +msgid "Rescan" +msgstr "å†ã‚¹ã‚ャン" + +#: git-gui.sh:1988 +msgid "Stage To Commit" +msgstr "コミット予定ã™ã‚‹" + +#: git-gui.sh:1994 +msgid "Stage Changed Files To Commit" +msgstr "変更ã•ã‚ŒãŸãƒ•ã‚¡ã‚¤ãƒ«ã‚’コミット予定" + +#: git-gui.sh:2000 +msgid "Unstage From Commit" +msgstr "コミットã‹ã‚‰é™ã‚ã™" + +#: git-gui.sh:2005 lib/index.tcl:393 +msgid "Revert Changes" +msgstr "変更を元ã«æˆ»ã™" + +#: git-gui.sh:2012 git-gui.sh:2329 git-gui.sh:2427 +msgid "Sign Off" +msgstr "ç½²å" + +#: git-gui.sh:2016 git-gui.sh:2333 +msgid "Commit@@verb" +msgstr "コミット" + +#: git-gui.sh:2027 +msgid "Local Merge..." +msgstr "ãƒãƒ¼ã‚«ãƒ«ãƒ»ãƒžãƒ¼ã‚¸â€¦" + +#: git-gui.sh:2032 +msgid "Abort Merge..." +msgstr "マージä¸æ¢â€¦" + +#: git-gui.sh:2044 +msgid "Push..." +msgstr "プッシュ…" + +#: git-gui.sh:2055 lib/choose_repository.tcl:40 +msgid "Apple" +msgstr "ã‚Šã‚“ã”" + +#: git-gui.sh:2058 git-gui.sh:2080 lib/about.tcl:13 +#: lib/choose_repository.tcl:43 lib/choose_repository.tcl:49 +#, tcl-format +msgid "About %s" +msgstr "%s ã«ã¤ã„ã¦" + +#: git-gui.sh:2062 +msgid "Preferences..." +msgstr "è¨å®šâ€¦" + +#: git-gui.sh:2070 git-gui.sh:2595 +msgid "Options..." +msgstr "オプション…" + +#: git-gui.sh:2076 lib/choose_repository.tcl:46 +msgid "Help" +msgstr "ヘルプ" + +#: git-gui.sh:2117 +msgid "Online Documentation" +msgstr "オンライン・ドã‚ュメント" + +#: git-gui.sh:2201 +#, tcl-format +msgid "fatal: cannot stat path %s: No such file or directory" +msgstr "" +"致命的: パス %s ㌠stat ã§ãã¾ã›ã‚“。ãã®ã‚ˆã†ãªãƒ•ã‚¡ã‚¤ãƒ«ã‚„ディレクトリã¯ã‚ã‚Šã¾" +"ã›ã‚“" + +#: git-gui.sh:2234 +msgid "Current Branch:" +msgstr "ç¾åœ¨ã®ãƒ–ランãƒ" + +#: git-gui.sh:2255 +msgid "Staged Changes (Will Commit)" +msgstr "ステージングã•ã‚ŒãŸï¼ˆã‚³ãƒŸãƒƒãƒˆäºˆå®šæ¸ˆã®ï¼‰å¤‰æ›´" + +#: git-gui.sh:2274 +msgid "Unstaged Changes" +msgstr "コミット予定ã«å…¥ã£ã¦ã„ãªã„変更" + +#: git-gui.sh:2323 +msgid "Stage Changed" +msgstr "変更をコミット予定ã«å…¥ã‚Œã‚‹" + +#: git-gui.sh:2339 lib/transport.tcl:93 lib/transport.tcl:182 +msgid "Push" +msgstr "プッシュ" + +#: git-gui.sh:2369 +msgid "Initial Commit Message:" +msgstr "最åˆã®ã‚³ãƒŸãƒƒãƒˆãƒ¡ãƒƒã‚»ãƒ¼ã‚¸:" + +#: git-gui.sh:2370 +msgid "Amended Commit Message:" +msgstr "訂æ£ã—ãŸã‚³ãƒŸãƒƒãƒˆãƒ¡ãƒƒã‚»ãƒ¼ã‚¸:" + +#: git-gui.sh:2371 +msgid "Amended Initial Commit Message:" +msgstr "訂æ£ã—ãŸæœ€åˆã®ã‚³ãƒŸãƒƒãƒˆãƒ¡ãƒƒã‚»ãƒ¼ã‚¸:" + +#: git-gui.sh:2372 +msgid "Amended Merge Commit Message:" +msgstr "訂æ£ã—ãŸãƒžãƒ¼ã‚¸ã‚³ãƒŸãƒƒãƒˆãƒ¡ãƒƒã‚»ãƒ¼ã‚¸:" + +#: git-gui.sh:2373 +msgid "Merge Commit Message:" +msgstr "マージコミットメッセージ:" + +#: git-gui.sh:2374 +msgid "Commit Message:" +msgstr "コミットメッセージ:" + +#: git-gui.sh:2419 git-gui.sh:2557 lib/console.tcl:71 +msgid "Copy All" +msgstr "å…¨ã¦ã‚³ãƒ”ー" + +#: git-gui.sh:2443 lib/blame.tcl:104 +msgid "File:" +msgstr "ファイル:" + +#: git-gui.sh:2545 +msgid "Refresh" +msgstr "å†èªã¿è¾¼ã¿" + +#: git-gui.sh:2566 +msgid "Apply/Reverse Hunk" +msgstr "パッãƒã‚’é©ç”¨/å–り消ã™" + +#: git-gui.sh:2572 +msgid "Decrease Font Size" +msgstr "フォントをå°ã•ã" + +#: git-gui.sh:2576 +msgid "Increase Font Size" +msgstr "フォントを大ãã" + +#: git-gui.sh:2581 +msgid "Show Less Context" +msgstr "文脈を少ãªã" + +#: git-gui.sh:2588 +msgid "Show More Context" +msgstr "文脈を多ã" + +#: git-gui.sh:2602 +msgid "Unstage Hunk From Commit" +msgstr "パッãƒã‚’コミット予定ã‹ã‚‰å¤–ã™" + +#: git-gui.sh:2604 +msgid "Stage Hunk For Commit" +msgstr "パッãƒã‚’コミット予定ã«åŠ ãˆã‚‹" + +#: git-gui.sh:2623 +msgid "Initializing..." +msgstr "åˆæœŸåŒ–ã—ã¦ã„ã¾ã™â€¦" + +#: git-gui.sh:2718 +#, tcl-format +msgid "" +"Possible environment issues exist.\n" +"\n" +"The following environment variables are probably\n" +"going to be ignored by any Git subprocess run\n" +"by %s:\n" +"\n" +msgstr "" +"環境ã«å•é¡ŒãŒã‚ã‚‹å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™\n" +"\n" +"以下ã®ç’°å¢ƒå¤‰æ•°ã¯ %s ãŒèµ·å‹•ã™ã‚‹ Git サブプãƒã‚»ã‚¹ã«ã‚ˆã£ã¦ç„¡è¦–ã•ã‚Œã‚‹ã§ã—ょã†:\n" +"\n" + +#: git-gui.sh:2748 +msgid "" +"\n" +"This is due to a known issue with the\n" +"Tcl binary distributed by Cygwin." +msgstr "" +"\n" +"ã“れ㯠Cygwin ã§é…布ã•ã‚Œã¦ã„ã‚‹ Tcl ãƒã‚¤ãƒŠãƒªã«\n" +"é–¢ã—ã¦ã®æ—¢çŸ¥ã®å•é¡Œã«ã‚ˆã‚Šã¾ã™" + +#: git-gui.sh:2753 +#, tcl-format +msgid "" +"\n" +"\n" +"A good replacement for %s\n" +"is placing values for the user.name and\n" +"user.email settings into your personal\n" +"~/.gitconfig file.\n" +msgstr "" +"\n" +"\n" +"個人的㪠~/.gitconfig ファイル内㧠user.name 㨠user.email ã®å€¤ã‚’è¨å®š\n" +"ã™ã‚‹ã®ãŒã€%s ã®è‰¯ã„代用ã¨ãªã‚Šã¾ã™\n" + +#: lib/about.tcl:25 +msgid "git-gui - a graphical user interface for Git." +msgstr "Git ã®ã‚°ãƒ©ãƒ•ã‚£ã‚«ãƒ«UI git-gui" + +#: lib/blame.tcl:77 +msgid "File Viewer" +msgstr "ファイルピューワ" + +#: lib/blame.tcl:81 +msgid "Commit:" +msgstr "コミット:" + +#: lib/blame.tcl:249 +msgid "Copy Commit" +msgstr "コミットをコピー" + +#: lib/blame.tcl:369 +#, tcl-format +msgid "Reading %s..." +msgstr "%s ã‚’èªã‚“ã§ã„ã¾ã™â€¦" + +#: lib/blame.tcl:473 +msgid "Loading copy/move tracking annotations..." +msgstr "コピー・移動追跡データをèªã‚“ã§ã„ã¾ã™â€¦" + +#: lib/blame.tcl:493 +msgid "lines annotated" +msgstr "行を注釈ã—ã¾ã—ãŸ" + +#: lib/blame.tcl:674 +msgid "Loading original location annotations..." +msgstr "å…ƒä½ç½®è¡Œã®æ³¨é‡ˆãƒ‡ãƒ¼ã‚¿ã‚’èªã‚“ã§ã„ã¾ã™â€¦" + +#: lib/blame.tcl:677 +msgid "Annotation complete." +msgstr "注釈完了ã—ã¾ã—ãŸ" + +#: lib/blame.tcl:731 +msgid "Loading annotation..." +msgstr "注釈をèªã¿è¾¼ã‚“ã§ã„ã¾ã™â€¦" + +#: lib/blame.tcl:787 +msgid "Author:" +msgstr "作者:" + +#: lib/blame.tcl:791 +msgid "Committer:" +msgstr "コミット者:" + +#: lib/blame.tcl:796 +msgid "Original File:" +msgstr "元ファイル" + +#: lib/blame.tcl:910 +msgid "Originally By:" +msgstr "原作者:" + +#: lib/blame.tcl:916 +msgid "In File:" +msgstr "ファイル:" + +#: lib/blame.tcl:921 +msgid "Copied Or Moved Here By:" +msgstr "複写・移動者:" + +#: lib/branch_checkout.tcl:14 lib/branch_checkout.tcl:19 +msgid "Checkout Branch" +msgstr "ブランãƒã‚’ãƒã‚§ãƒƒã‚¯ã‚¢ã‚¦ãƒˆ" + +#: lib/branch_checkout.tcl:23 +msgid "Checkout" +msgstr "ãƒã‚§ãƒƒã‚¯ã‚¢ã‚¦ãƒˆ" + +#: lib/branch_checkout.tcl:27 lib/branch_create.tcl:35 +#: lib/branch_delete.tcl:32 lib/branch_rename.tcl:30 lib/browser.tcl:281 +#: lib/checkout_op.tcl:522 lib/choose_font.tcl:43 lib/merge.tcl:172 +#: lib/option.tcl:90 lib/remote_branch_delete.tcl:42 lib/transport.tcl:97 +msgid "Cancel" +msgstr "ä¸æ¢" + +#: lib/branch_checkout.tcl:32 lib/browser.tcl:286 +msgid "Revision" +msgstr "リビジョン" + +#: lib/branch_checkout.tcl:36 lib/branch_create.tcl:69 lib/option.tcl:202 +msgid "Options" +msgstr "オプション" + +#: lib/branch_checkout.tcl:39 lib/branch_create.tcl:92 +msgid "Fetch Tracking Branch" +msgstr "トラッã‚ング・ブランãƒã‚’フェッãƒ" + +#: lib/branch_checkout.tcl:44 +msgid "Detach From Local Branch" +msgstr "ãƒãƒ¼ã‚«ãƒ«ãƒ»ãƒ–ランãƒã‹ã‚‰å‰Šé™¤" + +#: lib/branch_create.tcl:22 +msgid "Create Branch" +msgstr "ブランãƒã‚’作æˆ" + +#: lib/branch_create.tcl:27 +msgid "Create New Branch" +msgstr "ブランãƒã‚’æ–°è¦ä½œæˆ" + +#: lib/branch_create.tcl:31 lib/choose_repository.tcl:375 +msgid "Create" +msgstr "作æˆ" + +#: lib/branch_create.tcl:40 +msgid "Branch Name" +msgstr "ブランãƒå" + +#: lib/branch_create.tcl:43 +msgid "Name:" +msgstr "åå‰:" + +#: lib/branch_create.tcl:58 +msgid "Match Tracking Branch Name" +msgstr "トラッã‚ング・ブランãƒåã‚’åˆã‚ã›ã‚‹" + +#: lib/branch_create.tcl:66 +msgid "Starting Revision" +msgstr "åˆæœŸãƒªãƒ“ジョン" + +#: lib/branch_create.tcl:72 +msgid "Update Existing Branch:" +msgstr "æ—¢å˜ã®ãƒ–ランãƒã‚’æ›´æ–°:" + +#: lib/branch_create.tcl:75 +msgid "No" +msgstr "ã„ã„ãˆ" + +#: lib/branch_create.tcl:80 +msgid "Fast Forward Only" +msgstr "æ—©é€ã‚Šã®ã¿" + +#: lib/branch_create.tcl:85 lib/checkout_op.tcl:514 +msgid "Reset" +msgstr "リセット" + +#: lib/branch_create.tcl:97 +msgid "Checkout After Creation" +msgstr "作æˆã—ã¦ã™ããƒã‚§ãƒƒã‚¯ã‚¢ã‚¦ãƒˆ" + +#: lib/branch_create.tcl:131 +msgid "Please select a tracking branch." +msgstr "トラッã‚ング・ブランãƒã‚’é¸æŠžã—ã¦ä¸‹ã•ã„。" + +#: lib/branch_create.tcl:140 +#, tcl-format +msgid "Tracking branch %s is not a branch in the remote repository." +msgstr "トラッã‚ング・ブランム%s ã¯é 隔リãƒã‚¸ãƒˆãƒªã®ãƒ–ランãƒã§ã¯ã‚ã‚Šã¾ã›ã‚“。" + +#: lib/branch_create.tcl:153 lib/branch_rename.tcl:86 +msgid "Please supply a branch name." +msgstr "ブランãƒåを指定ã—ã¦ä¸‹ã•ã„。" + +#: lib/branch_create.tcl:164 lib/branch_rename.tcl:106 +#, tcl-format +msgid "'%s' is not an acceptable branch name." +msgstr "'%s' ã¯ãƒ–ランãƒåã«ä½¿ãˆã¾ã›ã‚“。" + +#: lib/branch_delete.tcl:15 +msgid "Delete Branch" +msgstr "ブランãƒå‰Šé™¤" + +#: lib/branch_delete.tcl:20 +msgid "Delete Local Branch" +msgstr "ãƒãƒ¼ã‚«ãƒ«ãƒ»ãƒ–ランãƒã‚’削除" + +#: lib/branch_delete.tcl:37 +msgid "Local Branches" +msgstr "ãƒãƒ¼ã‚«ãƒ«ãƒ»ãƒ–ランãƒ" + +#: lib/branch_delete.tcl:52 +msgid "Delete Only If Merged Into" +msgstr "マージ済ã¿ã®æ™‚ã®ã¿å‰Šé™¤" + +#: lib/branch_delete.tcl:54 +msgid "Always (Do not perform merge test.)" +msgstr "ç„¡æ¡ä»¶(マージテストã—ãªã„)" + +#: lib/branch_delete.tcl:103 +#, tcl-format +msgid "The following branches are not completely merged into %s:" +msgstr "以下ã®ãƒ–ランãƒã¯ %s ã«å®Œå…¨ã«ãƒžãƒ¼ã‚¸ã•ã‚Œã¦ã„ã¾ã›ã‚“:" + +#: lib/branch_delete.tcl:115 +msgid "" +"Recovering deleted branches is difficult. \n" +"\n" +" Delete the selected branches?" +msgstr "" +"ブランãƒã‚’削除ã™ã‚‹ã¨å…ƒã«æˆ»ã™ã®ã¯å›°é›£ã§ã™ã€‚ \n" +"\n" +" é¸æŠžã—ãŸãƒ–ランãƒã‚’削除ã—ã¾ã™ã‹ï¼Ÿ" + +#: lib/branch_delete.tcl:141 +#, tcl-format +msgid "" +"Failed to delete branches:\n" +"%s" +msgstr "" +"以下ã®ãƒ–ランãƒã‚’削除ã§ãã¾ã›ã‚“:\n" +"%s" + +#: lib/branch_rename.tcl:14 lib/branch_rename.tcl:22 +msgid "Rename Branch" +msgstr "ブランãƒã®åå‰å¤‰æ›´" + +#: lib/branch_rename.tcl:26 +msgid "Rename" +msgstr "åå‰å¤‰æ›´" + +#: lib/branch_rename.tcl:36 +msgid "Branch:" +msgstr "ブランãƒ:" + +#: lib/branch_rename.tcl:39 +msgid "New Name:" +msgstr "æ–°ã—ã„åå‰:" + +#: lib/branch_rename.tcl:75 +msgid "Please select a branch to rename." +msgstr "åå‰ã‚’変更ã™ã‚‹ãƒ–ランãƒã‚’é¸ã‚“ã§ä¸‹ã•ã„。" + +#: lib/branch_rename.tcl:96 lib/checkout_op.tcl:179 +#, tcl-format +msgid "Branch '%s' already exists." +msgstr "'%s'ã¨ã„ã†ãƒ–ランãƒã¯æ—¢ã«å˜åœ¨ã—ã¾ã™ã€‚" + +#: lib/branch_rename.tcl:117 +#, tcl-format +msgid "Failed to rename '%s'." +msgstr "'%s'ã®åå‰å¤‰æ›´ã«å¤±æ•—ã—ã¾ã—ãŸã€‚" + +#: lib/browser.tcl:17 +msgid "Starting..." +msgstr "èµ·å‹•ä¸â€¦" + +#: lib/browser.tcl:26 +msgid "File Browser" +msgstr "ファイル・ブラウザ" + +#: lib/browser.tcl:125 lib/browser.tcl:142 +#, tcl-format +msgid "Loading %s..." +msgstr "%s ã‚’ãƒãƒ¼ãƒ‰ä¸â€¦" + +#: lib/browser.tcl:186 +msgid "[Up To Parent]" +msgstr "[上ä½ãƒ•ã‚©ãƒ«ãƒ€ã¸]" + +#: lib/browser.tcl:266 lib/browser.tcl:272 +msgid "Browse Branch Files" +msgstr "ç¾åœ¨ã®ãƒ–ランãƒã®ãƒ•ã‚¡ã‚¤ãƒ«ã‚’見る" + +#: lib/browser.tcl:277 lib/choose_repository.tcl:391 +#: lib/choose_repository.tcl:482 lib/choose_repository.tcl:492 +#: lib/choose_repository.tcl:989 +msgid "Browse" +msgstr "ブラウズ" + +#: lib/checkout_op.tcl:79 +#, tcl-format +msgid "Fetching %s from %s" +msgstr "%s ã‹ã‚‰ %s をフェッãƒã—ã¦ã„ã¾ã™" + +#: lib/checkout_op.tcl:127 +#, tcl-format +msgid "fatal: Cannot resolve %s" +msgstr "致命的エラー: %s を解決ã§ãã¾ã›ã‚“" + +#: lib/checkout_op.tcl:140 lib/console.tcl:79 lib/database.tcl:31 +msgid "Close" +msgstr "é–‰ã˜ã‚‹" + +#: lib/checkout_op.tcl:169 +#, tcl-format +msgid "Branch '%s' does not exist." +msgstr "ブランãƒ'%s'ã¯å˜åœ¨ã—ã¾ã›ã‚“。" + +#: lib/checkout_op.tcl:206 +#, tcl-format +msgid "" +"Branch '%s' already exists.\n" +"\n" +"It cannot fast-forward to %s.\n" +"A merge is required." +msgstr "" +"ブランム'%s' ã¯æ—¢ã«å˜åœ¨ã—ã¾ã™ã€‚\n" +"\n" +"%s ã«æ—©é€ã‚Šã§ãã¾ã›ã‚“。\n" +"マージãŒå¿…è¦ã§ã™ã€‚" + +#: lib/checkout_op.tcl:220 +#, tcl-format +msgid "Merge strategy '%s' not supported." +msgstr "'%s' マージ戦略ã¯ã‚µãƒãƒ¼ãƒˆã•ã‚Œã¦ã„ã¾ã›ã‚“。" + +#: lib/checkout_op.tcl:239 +#, tcl-format +msgid "Failed to update '%s'." +msgstr "'%s' ã®æ›´æ–°ã«å¤±æ•—ã—ã¾ã—ãŸã€‚" + +#: lib/checkout_op.tcl:251 +msgid "Staging area (index) is already locked." +msgstr "インデックスã¯æ—¢ã«ãƒãƒƒã‚¯ã•ã‚Œã¦ã„ã¾ã™ã€‚" + +#: lib/checkout_op.tcl:266 +msgid "" +"Last scanned state does not match repository state.\n" +"\n" +"Another Git program has modified this repository since the last scan. A " +"rescan must be performed before the current branch can be changed.\n" +"\n" +"The rescan will be automatically started now.\n" +msgstr "" +"最後ã«ã‚¹ã‚ャンã—ãŸçŠ¶æ…‹ã¯ãƒªãƒã‚¸ãƒˆãƒªã®çŠ¶æ…‹ã¨åˆè‡´ã—ã¾ã›ã‚“。\n" +"\n" +"最後ã«ã‚¹ã‚ャンã—ã¦ä»¥å¾Œã€åˆ¥ã® Git プãƒã‚°ãƒ©ãƒ ãŒãƒªãƒã‚¸ãƒˆãƒªã‚’変更ã—ã¦ã„ã¾ã™ã€‚ç¾åœ¨" +"ã®ãƒ–ランãƒã‚’変更ã™ã‚‹å‰ã«ã€å†ã‚¹ã‚ャンãŒå¿…è¦ã§ã™ã€‚\n" +"\n" +"自動的ã«å†ã‚¹ã‚ャンを開始ã—ã¾ã™ã€‚\n" + +#: lib/checkout_op.tcl:322 +#, tcl-format +msgid "Updating working directory to '%s'..." +msgstr "作æ¥ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã‚’ '%s' ã«æ›´æ–°ã—ã¦ã„ã¾ã™â€¦" + +#: lib/checkout_op.tcl:353 +#, tcl-format +msgid "Aborted checkout of '%s' (file level merging is required)." +msgstr "'%s' ã®ãƒã‚§ãƒƒã‚¯ã‚¢ã‚¦ãƒˆã‚’ä¸æ¢ã—ã¾ã—ãŸï¼ˆãƒ•ã‚¡ã‚¤ãƒ«æ¯Žã®ãƒžãƒ¼ã‚¸ãŒå¿…è¦ã§ã™ï¼‰ã€‚" + +#: lib/checkout_op.tcl:354 +msgid "File level merge required." +msgstr "ファイル毎ã®ãƒžãƒ¼ã‚¸ãŒå¿…è¦ã§ã™ã€‚" + +#: lib/checkout_op.tcl:358 +#, tcl-format +msgid "Staying on branch '%s'." +msgstr "ブランム'%s' ã«æ»žã¾ã‚Šã¾ã™ã€‚" + +#: lib/checkout_op.tcl:429 +msgid "" +"You are no longer on a local branch.\n" +"\n" +"If you wanted to be on a branch, create one now starting from 'This Detached " +"Checkout'." +msgstr "" +"ãƒãƒ¼ã‚«ãƒ«ãƒ»ãƒ–ランãƒã‹ã‚‰é›¢ã‚Œã¾ã™ã€‚\n" +"\n" +"ブランãƒä¸Šã«æ»žã¾ã‚ŠãŸã„ã¨ãã¯ã€ã“ã®ã€Œåˆ†é›¢ã•ã‚ŒãŸãƒã‚§ãƒƒã‚¯ã‚¢ã‚¦ãƒˆã€ã‹ã‚‰æ–°è¦ãƒ–ラン" +"ãƒã‚’開始ã—ã¦ãã ã•ã„。" + +#: lib/checkout_op.tcl:446 +#, tcl-format +msgid "Checked out '%s'." +msgstr "'%s' ã‚’ãƒã‚§ãƒƒã‚¯ã‚¢ã‚¦ãƒˆã—ã¾ã—ãŸ" + +#: lib/checkout_op.tcl:478 +#, tcl-format +msgid "Resetting '%s' to '%s' will lose the following commits:" +msgstr "'%s' ã‚’ '%s' ã«ãƒªã‚»ãƒƒãƒˆã™ã‚‹ã¨ã€ä»¥ä¸‹ã®ã‚³ãƒŸãƒƒãƒˆãŒå¤±ãªã‚ã‚Œã¾ã™:" + +#: lib/checkout_op.tcl:500 +msgid "Recovering lost commits may not be easy." +msgstr "失ãªã‚ã‚ŒãŸã‚³ãƒŸãƒƒãƒˆã‚’回復ã™ã‚‹ã®ã¯ç°¡å˜ã§ã¯ã‚ã‚Šã¾ã›ã‚“。" + +#: lib/checkout_op.tcl:505 +#, tcl-format +msgid "Reset '%s'?" +msgstr "'%s' をリセットã—ã¾ã™ã‹ï¼Ÿ" + +#: lib/checkout_op.tcl:510 lib/merge.tcl:164 +msgid "Visualize" +msgstr "å¯è¦–化" + +#: lib/checkout_op.tcl:578 +#, tcl-format +msgid "" +"Failed to set current branch.\n" +"\n" +"This working directory is only partially switched. We successfully updated " +"your files, but failed to update an internal Git file.\n" +"\n" +"This should not have occurred. %s will now close and give up." +msgstr "" +"ç¾åœ¨ã®ãƒ–ランãƒã‚’è¨å®šã§ãã¾ã›ã‚“。\n" +"\n" +"作æ¥ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã¯éƒ¨åˆ†çš„ã«ã—ã‹åˆ‡ã‚Šæ›¿ã‚ã£ã¦ã„ã¾ã›ã‚“。ファイルã®æ›´æ–°ã«ã¯æˆåŠŸã—" +"ã¾ã—ãŸãŒã€ Git ã®å†…部データを更新ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚\n" +"èµ·ã“ã‚‹ã¯ãšã®ãªã„エラーã§ã™ã€‚ã‚ãらã‚㦠%s を終了ã—ã¾ã™ã€‚" + +#: lib/choose_font.tcl:39 +msgid "Select" +msgstr "é¸æŠž" + +#: lib/choose_font.tcl:53 +msgid "Font Family" +msgstr "フォント・ファミリー" + +#: lib/choose_font.tcl:73 +msgid "Font Size" +msgstr "フォントã®å¤§ãã•" + +#: lib/choose_font.tcl:90 +msgid "Font Example" +msgstr "フォント・サンプル" + +#: lib/choose_font.tcl:101 +msgid "" +"This is example text.\n" +"If you like this text, it can be your font." +msgstr "" +"ã“ã‚Œã¯ã‚µãƒ³ãƒ—ル文ã§ã™ã€‚\n" +"ã“ã®ãƒ•ã‚©ãƒ³ãƒˆãŒæ°—ã«å…¥ã‚Œã°ãŠä½¿ã„ã«ãªã‚Œã¾ã™ã€‚" + +#: lib/choose_repository.tcl:27 +msgid "Git Gui" +msgstr "Git GUI" + +#: lib/choose_repository.tcl:80 lib/choose_repository.tcl:380 +msgid "Create New Repository" +msgstr "æ–°ã—ã„リãƒã‚¸ãƒˆãƒªã‚’作る" + +#: lib/choose_repository.tcl:86 +msgid "New..." +msgstr "æ–°è¦â€¦" + +#: lib/choose_repository.tcl:93 lib/choose_repository.tcl:468 +msgid "Clone Existing Repository" +msgstr "æ—¢å˜ãƒªãƒã‚¸ãƒˆãƒªã‚’複製ã™ã‚‹" + +#: lib/choose_repository.tcl:99 +msgid "Clone..." +msgstr "複製…" + +#: lib/choose_repository.tcl:106 lib/choose_repository.tcl:978 +msgid "Open Existing Repository" +msgstr "æ—¢å˜ãƒªãƒã‚¸ãƒˆãƒªã‚’é–‹ã" + +#: lib/choose_repository.tcl:112 +msgid "Open..." +msgstr "é–‹ã…" + +#: lib/choose_repository.tcl:125 +msgid "Recent Repositories" +msgstr "最近使ã£ãŸãƒªãƒã‚¸ãƒˆãƒª" + +#: lib/choose_repository.tcl:131 +msgid "Open Recent Repository:" +msgstr "最近使ã£ãŸãƒªãƒã‚¸ãƒˆãƒªã‚’é–‹ã" + +#: lib/choose_repository.tcl:294 +#, tcl-format +msgid "Location %s already exists." +msgstr "'%s' ã¯æ—¢ã«å˜åœ¨ã—ã¾ã™ã€‚" + +#: lib/choose_repository.tcl:300 lib/choose_repository.tcl:307 +#: lib/choose_repository.tcl:314 +#, tcl-format +msgid "Failed to create repository %s:" +msgstr "リãƒã‚¸ãƒˆãƒª %s を作製ã§ãã¾ã›ã‚“:" + +#: lib/choose_repository.tcl:385 lib/choose_repository.tcl:486 +msgid "Directory:" +msgstr "ディレクトリ:" + +#: lib/choose_repository.tcl:415 lib/choose_repository.tcl:544 +#: lib/choose_repository.tcl:1013 +msgid "Git Repository" +msgstr "GIT リãƒã‚¸ãƒˆãƒª" + +#: lib/choose_repository.tcl:430 lib/choose_repository.tcl:437 +#, tcl-format +msgid "Directory %s already exists." +msgstr "ディレクトリ '%s' ã¯æ—¢ã«å˜åœ¨ã—ã¾ã™ã€‚" + +#: lib/choose_repository.tcl:442 +#, tcl-format +msgid "File %s already exists." +msgstr "ファイル '%s' ã¯æ—¢ã«å˜åœ¨ã—ã¾ã™ã€‚" + +#: lib/choose_repository.tcl:463 +msgid "Clone" +msgstr "複製" + +#: lib/choose_repository.tcl:476 +msgid "URL:" +msgstr "URL:" + +#: lib/choose_repository.tcl:496 +msgid "Clone Type:" +msgstr "複製方å¼:" + +#: lib/choose_repository.tcl:502 +msgid "Standard (Fast, Semi-Redundant, Hardlinks)" +msgstr "標準(高速・ä¸å†—長度・ãƒãƒ¼ãƒ‰ãƒªãƒ³ã‚¯)" + +#: lib/choose_repository.tcl:508 +msgid "Full Copy (Slower, Redundant Backup)" +msgstr "全複写(低速・冗長ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—)" + +#: lib/choose_repository.tcl:514 +msgid "Shared (Fastest, Not Recommended, No Backup)" +msgstr "共有(最高速・éžæŽ¨å¥¨ãƒ»ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—ç„¡ã—)" + +#: lib/choose_repository.tcl:550 lib/choose_repository.tcl:597 +#: lib/choose_repository.tcl:738 lib/choose_repository.tcl:808 +#: lib/choose_repository.tcl:1019 lib/choose_repository.tcl:1027 +#, tcl-format +msgid "Not a Git repository: %s" +msgstr "Git リãƒã‚¸ãƒˆãƒªã§ã¯ã‚ã‚Šã¾ã›ã‚“: %s" + +#: lib/choose_repository.tcl:586 +msgid "Standard only available for local repository." +msgstr "標準方å¼ã¯åŒä¸€è¨ˆç®—機上ã®ãƒªãƒã‚¸ãƒˆãƒªã«ã®ã¿ä½¿ãˆã¾ã™ã€‚" + +#: lib/choose_repository.tcl:590 +msgid "Shared only available for local repository." +msgstr "共有方å¼ã¯åŒä¸€è¨ˆç®—機上ã®ãƒªãƒã‚¸ãƒˆãƒªã«ã®ã¿ä½¿ãˆã¾ã™ã€‚" + +#: lib/choose_repository.tcl:617 +msgid "Failed to configure origin" +msgstr "origin ã‚’è¨å®šã§ãã¾ã›ã‚“ã§ã—ãŸ" + +#: lib/choose_repository.tcl:629 +msgid "Counting objects" +msgstr "オブジェクトを数ãˆã¦ã„ã¾ã™" + +#: lib/choose_repository.tcl:630 +msgid "buckets" +msgstr "ãƒã‚±ãƒ„" + +#: lib/choose_repository.tcl:654 +#, tcl-format +msgid "Unable to copy objects/info/alternates: %s" +msgstr "objects/info/alternates を複写ã§ãã¾ã›ã‚“: %s" + +#: lib/choose_repository.tcl:690 +#, tcl-format +msgid "Nothing to clone from %s." +msgstr "%s ã‹ã‚‰è¤‡è£½ã™ã‚‹å†…容ã¯ã‚ã‚Šã¾ã›ã‚“" + +#: lib/choose_repository.tcl:692 lib/choose_repository.tcl:906 +#: lib/choose_repository.tcl:918 +msgid "The 'master' branch has not been initialized." +msgstr "'master' ブランãƒãŒåˆæœŸåŒ–ã•ã‚Œã¦ã„ã¾ã›ã‚“" + +#: lib/choose_repository.tcl:705 +msgid "Hardlinks are unavailable. Falling back to copying." +msgstr "ãƒãƒ¼ãƒ‰ãƒªãƒ³ã‚¯ãŒä½œã‚Œãªã„ã®ã§ã€ã‚³ãƒ”ーã—ã¾ã™" + +#: lib/choose_repository.tcl:717 +#, tcl-format +msgid "Cloning from %s" +msgstr "%s ã‹ã‚‰è¤‡è£½ã—ã¦ã„ã¾ã™" + +#: lib/choose_repository.tcl:748 +msgid "Copying objects" +msgstr "オブジェクトを複写ã—ã¦ã„ã¾ã™" + +#: lib/choose_repository.tcl:749 +msgid "KiB" +msgstr "KiB" + +#: lib/choose_repository.tcl:773 +#, tcl-format +msgid "Unable to copy object: %s" +msgstr "オブジェクトを複写ã§ãã¾ã›ã‚“: %s" + +#: lib/choose_repository.tcl:783 +msgid "Linking objects" +msgstr "オブジェクトを連çµã—ã¦ã„ã¾ã™" + +#: lib/choose_repository.tcl:784 +msgid "objects" +msgstr "オブジェクト" + +#: lib/choose_repository.tcl:792 +#, tcl-format +msgid "Unable to hardlink object: %s" +msgstr "オブジェクトをãƒãƒ¼ãƒ‰ãƒªãƒ³ã‚¯ã§ãã¾ã›ã‚“: %s" + +#: lib/choose_repository.tcl:847 +msgid "Cannot fetch branches and objects. See console output for details." +msgstr "ブランãƒã‚„オブジェクトをå–å¾—ã§ãã¾ã›ã‚“。コンソール出力を見ã¦ä¸‹ã•ã„" + +#: lib/choose_repository.tcl:858 +msgid "Cannot fetch tags. See console output for details." +msgstr "ã‚¿ã‚°ã‚’å–å¾—ã§ãã¾ã›ã‚“。コンソール出力を見ã¦ä¸‹ã•ã„" + +#: lib/choose_repository.tcl:882 +msgid "Cannot determine HEAD. See console output for details." +msgstr "HEAD を確定ã§ãã¾ã›ã‚“。コンソール出力を見ã¦ä¸‹ã•ã„" + +#: lib/choose_repository.tcl:891 +#, tcl-format +msgid "Unable to cleanup %s" +msgstr "%s を掃除ã§ãã¾ã›ã‚“" + +#: lib/choose_repository.tcl:897 +msgid "Clone failed." +msgstr "複写ã«å¤±æ•—ã—ã¾ã—ãŸã€‚" + +#: lib/choose_repository.tcl:904 +msgid "No default branch obtained." +msgstr "デフォールト・ブランãƒãŒå–å¾—ã•ã‚Œã¾ã›ã‚“ã§ã—ãŸ" + +#: lib/choose_repository.tcl:915 +#, tcl-format +msgid "Cannot resolve %s as a commit." +msgstr "%s をコミットã¨ã—ã¦è§£é‡ˆã§ãã¾ã›ã‚“" + +#: lib/choose_repository.tcl:927 +msgid "Creating working directory" +msgstr "作æ¥ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã‚’作æˆã—ã¦ã„ã¾ã™" + +#: lib/choose_repository.tcl:928 lib/index.tcl:65 lib/index.tcl:127 +#: lib/index.tcl:193 +msgid "files" +msgstr "ファイル" + +#: lib/choose_repository.tcl:957 +msgid "Initial file checkout failed." +msgstr "åˆæœŸãƒã‚§ãƒƒã‚¯ã‚¢ã‚¦ãƒˆã«å¤±æ•—ã—ã¾ã—ãŸ" + +#: lib/choose_repository.tcl:973 +msgid "Open" +msgstr "é–‹ã" + +#: lib/choose_repository.tcl:983 +msgid "Repository:" +msgstr "リãƒã‚¸ãƒˆãƒª:" + +#: lib/choose_repository.tcl:1033 +#, tcl-format +msgid "Failed to open repository %s:" +msgstr "リãƒã‚¸ãƒˆãƒª %s ã‚’é–‹ã‘ã¾ã›ã‚“:" + +#: lib/choose_rev.tcl:53 +msgid "This Detached Checkout" +msgstr "分離ã•ã‚ŒãŸãƒã‚§ãƒƒã‚¯ã‚¢ã‚¦ãƒˆ" + +#: lib/choose_rev.tcl:60 +msgid "Revision Expression:" +msgstr "リビジョンå¼:" + +#: lib/choose_rev.tcl:74 +msgid "Local Branch" +msgstr "ãƒãƒ¼ã‚«ãƒ«ãƒ»ãƒ–ランãƒ" + +#: lib/choose_rev.tcl:79 +msgid "Tracking Branch" +msgstr "トラッã‚ング・ブランãƒ" + +#: lib/choose_rev.tcl:84 lib/choose_rev.tcl:537 +msgid "Tag" +msgstr "ã‚¿ã‚°" + +#: lib/choose_rev.tcl:317 +#, tcl-format +msgid "Invalid revision: %s" +msgstr "無効ãªãƒªãƒ“ジョン: %s" + +#: lib/choose_rev.tcl:338 +msgid "No revision selected." +msgstr "リビジョンãŒæœªé¸æŠžã§ã™ã€‚" + +#: lib/choose_rev.tcl:346 +msgid "Revision expression is empty." +msgstr "リビジョンå¼ãŒç©ºã§ã™ã€‚" + +#: lib/choose_rev.tcl:530 +msgid "Updated" +msgstr "æ›´æ–°ã—ã¾ã—ãŸ" + +#: lib/choose_rev.tcl:558 +msgid "URL" +msgstr "URL" + +#: lib/commit.tcl:9 +msgid "" +"There is nothing to amend.\n" +"\n" +"You are about to create the initial commit. There is no commit before this " +"to amend.\n" +msgstr "" +"訂æ£ã™ã‚‹ã‚³ãƒŸãƒƒãƒˆãŒãã‚‚ãã‚‚ã‚ã‚Šã¾ã›ã‚“。\n" +"\n" +"ã“ã‚Œã‹ã‚‰ä½œã‚‹ã®ã¯æœ€åˆã®ã‚³ãƒŸãƒƒãƒˆã§ã™ã€‚ãã®å‰ã«ã¯ã¾ã 訂æ£ã™ã‚‹ã‚ˆã†ãªã‚³ãƒŸãƒƒãƒˆã¯ã‚" +"ã‚Šã¾ã›ã‚“。\n" + +#: lib/commit.tcl:18 +msgid "" +"Cannot amend while merging.\n" +"\n" +"You are currently in the middle of a merge that has not been fully " +"completed. You cannot amend the prior commit unless you first abort the " +"current merge activity.\n" +msgstr "" +"マージä¸ã«ã‚³ãƒŸãƒƒãƒˆã®è¨‚æ£ã¯ã§ãã¾ã›ã‚“。\n" +"\n" +"ç¾åœ¨ã¯ã¾ã マージã®é€”ä¸ã§ã™ã€‚å…ˆã«ã“ã®ãƒžãƒ¼ã‚¸ã‚’ä¸æ¢ã—ãªã„ã¨ã€å‰ã®ã‚³ãƒŸãƒƒãƒˆã®è¨‚æ£" +"ã¯ã§ãã¾ã›ã‚“\n" + +#: lib/commit.tcl:49 +msgid "Error loading commit data for amend:" +msgstr "訂æ£ã™ã‚‹ã‚³ãƒŸãƒƒãƒˆã®ãƒ‡ãƒ¼ã‚¿ã‚’èªã‚ã¾ã›ã‚“:" + +#: lib/commit.tcl:76 +msgid "Unable to obtain your identity:" +msgstr "ユーザã®æ£ä½“を確èªã§ãã¾ã›ã‚“:" + +#: lib/commit.tcl:81 +msgid "Invalid GIT_COMMITTER_IDENT:" +msgstr "GIT_COMMITTER_IDENT ãŒç„¡åŠ¹ã§ã™:" + +#: lib/commit.tcl:133 +msgid "" +"Last scanned state does not match repository state.\n" +"\n" +"Another Git program has modified this repository since the last scan. A " +"rescan must be performed before another commit can be created.\n" +"\n" +"The rescan will be automatically started now.\n" +msgstr "" +"最後ã«ã‚¹ã‚ャンã—ãŸçŠ¶æ…‹ã¯ãƒªãƒã‚¸ãƒˆãƒªã®çŠ¶æ…‹ã¨åˆè‡´ã—ã¾ã›ã‚“。\n" +"\n" +"最後ã«ã‚¹ã‚ャンã—ã¦ä»¥å¾Œã€åˆ¥ã® Git プãƒã‚°ãƒ©ãƒ ãŒãƒªãƒã‚¸ãƒˆãƒªã‚’変更ã—ã¦ã„ã¾ã™ã€‚æ–°ã—" +"ãコミットã™ã‚‹å‰ã«ã€å†ã‚¹ã‚ャンãŒå¿…è¦ã§ã™ã€‚\n" +"\n" +"自動的ã«å†ã‚¹ã‚ャンを開始ã—ã¾ã™ã€‚\n" + +#: lib/commit.tcl:154 +#, tcl-format +msgid "" +"Unmerged files cannot be committed.\n" +"\n" +"File %s has merge conflicts. You must resolve them and stage the file " +"before committing.\n" +msgstr "" +"マージã—ã¦ã„ãªã„ファイルã¯ã‚³ãƒŸãƒƒãƒˆã§ãã¾ã›ã‚“。\n" +"\n" +"ファイル %s ã«ã¯ãƒžãƒ¼ã‚¸è¡çªãŒæ®‹ã£ã¦ã„ã¾ã™ã€‚ã¾ãšè§£æ±ºã—ã¦ã‚³ãƒŸãƒƒãƒˆäºˆå®šã«åŠ ãˆã‚‹å¿…" +"è¦ãŒã‚ã‚Šã¾ã™ã€‚\n" + +#: lib/commit.tcl:162 +#, tcl-format +msgid "" +"Unknown file state %s detected.\n" +"\n" +"File %s cannot be committed by this program.\n" +msgstr "" +"ä¸æ˜Žãªãƒ•ã‚¡ã‚¤ãƒ«çŠ¶æ…‹ %s ã§ã™ã€‚\n" +"\n" +"ファイル %s ã¯æœ¬ãƒ—ãƒã‚°ãƒ©ãƒ ã§ã¯ã‚³ãƒŸãƒƒãƒˆã§ãã¾ã›ã‚“。\n" + +#: lib/commit.tcl:170 +msgid "" +"No changes to commit.\n" +"\n" +"You must stage at least 1 file before you can commit.\n" +msgstr "" +"コミットã™ã‚‹å¤‰æ›´ãŒã‚ã‚Šã¾ã›ã‚“。\n" +"\n" +"最低一ã¤ã®å¤‰æ›´ã‚’コミット予定ã«åŠ ãˆã¦ã‹ã‚‰ã‚³ãƒŸãƒƒãƒˆã—ã¦ä¸‹ã•ã„。\n" + +#: lib/commit.tcl:183 +msgid "" +"Please supply a commit message.\n" +"\n" +"A good commit message has the following format:\n" +"\n" +"- First line: Describe in one sentence what you did.\n" +"- Second line: Blank\n" +"- Remaining lines: Describe why this change is good.\n" +msgstr "" +"コミット・メッセージを入力ã—ã¦ä¸‹ã•ã„。\n" +"\n" +"æ£ã—ã„コミット・メッセージã¯:\n" +"\n" +"- 第1行: 何をã—ãŸã‹ã€ã‚’1行ã§è¦ç´„。\n" +"- 第2行: 空白\n" +"- 残りã®è¡Œ: ãªãœã€ã“ã®å¤‰æ›´ãŒè‰¯ã„変更ã‹ã€ã®èª¬æ˜Žã€‚\n" + +#: lib/commit.tcl:257 +msgid "write-tree failed:" +msgstr "write-tree ãŒå¤±æ•—ã—ã¾ã—ãŸ:" + +#: lib/commit.tcl:275 +#, tcl-format +msgid "Commit %s appears to be corrupt" +msgstr "コミット %s ã¯å£Šã‚Œã¦ã„ã¾ã™" + +#: lib/commit.tcl:279 +msgid "" +"No changes to commit.\n" +"\n" +"No files were modified by this commit and it was not a merge commit.\n" +"\n" +"A rescan will be automatically started now.\n" +msgstr "" +"コミットã™ã‚‹å¤‰æ›´ãŒã‚ã‚Šã¾ã›ã‚“。\n" +"\n" +"マージã§ãªãã€ã¾ãŸã€ä¸€ã¤ã‚‚変更点ãŒã‚ã‚Šã¾ã›ã‚“。\n" +"\n" +"自動的ã«å†ã‚¹ã‚ャンを開始ã—ã¾ã™ã€‚\n" + +#: lib/commit.tcl:286 +msgid "No changes to commit." +msgstr "コミットã™ã‚‹å¤‰æ›´ãŒã‚ã‚Šã¾ã›ã‚“。" + +#: lib/commit.tcl:303 +#, tcl-format +msgid "warning: Tcl does not support encoding '%s'." +msgstr "è¦å‘Š: Tcl ã¯ã‚¨ãƒ³ã‚³ãƒ¼ãƒ‡ã‚£ãƒ³ã‚° '%s' をサãƒãƒ¼ãƒˆã—ã¦ã„ã¾ã›ã‚“" + +#: lib/commit.tcl:317 +msgid "commit-tree failed:" +msgstr "commit-tree ãŒå¤±æ•—ã—ã¾ã—ãŸ:" + +#: lib/commit.tcl:339 +msgid "update-ref failed:" +msgstr "update-ref ãŒå¤±æ•—ã—ã¾ã—ãŸ:" + +#: lib/commit.tcl:430 +#, tcl-format +msgid "Created commit %s: %s" +msgstr "コミット %s を作æˆã—ã¾ã—ãŸ: %s" + +#: lib/console.tcl:57 +msgid "Working... please wait..." +msgstr "実行ä¸â€¦ãŠå¾…ã¡ä¸‹ã•ã„…" + +#: lib/console.tcl:183 +msgid "Success" +msgstr "æˆåŠŸ" + +#: lib/console.tcl:196 +msgid "Error: Command Failed" +msgstr "エラー: コマンドãŒå¤±æ•—ã—ã¾ã—ãŸ" + +#: lib/database.tcl:43 +msgid "Number of loose objects" +msgstr "ã°ã‚‰ã°ã‚‰ãªã‚ªãƒ–ジェクトã®æ•°" + +#: lib/database.tcl:44 +msgid "Disk space used by loose objects" +msgstr "ã°ã‚‰ã°ã‚‰ãªã‚ªãƒ–ジェクトã®ä½¿ç”¨ã™ã‚‹ãƒ‡ã‚£ã‚¹ã‚¯é‡" + +#: lib/database.tcl:45 +msgid "Number of packed objects" +msgstr "パックã•ã‚ŒãŸã‚ªãƒ–ジェクトã®æ•°" + +#: lib/database.tcl:46 +msgid "Number of packs" +msgstr "パックã®æ•°" + +#: lib/database.tcl:47 +msgid "Disk space used by packed objects" +msgstr "パックã•ã‚ŒãŸã‚ªãƒ–ジェクトã®ä½¿ç”¨ã™ã‚‹ãƒ‡ã‚£ã‚¹ã‚¯é‡" + +#: lib/database.tcl:48 +msgid "Packed objects waiting for pruning" +msgstr "パックã«å˜åœ¨ã™ã‚‹ã®ã§æ¨ã¦ã¦è‰¯ã„オブジェクトã®æ•°" + +#: lib/database.tcl:49 +msgid "Garbage files" +msgstr "ゴミファイル" + +#: lib/database.tcl:72 +msgid "Compressing the object database" +msgstr "データベース圧縮" + +#: lib/database.tcl:83 +msgid "Verifying the object database with fsck-objects" +msgstr "fsck-objects ã§ã‚ªãƒ–ジェクト・データベースを検証ã—ã¦ã„ã¾ã™" + +#: lib/database.tcl:108 +#, tcl-format +msgid "" +"This repository currently has approximately %i loose objects.\n" +"\n" +"To maintain optimal performance it is strongly recommended that you compress " +"the database when more than %i loose objects exist.\n" +"\n" +"Compress the database now?" +msgstr "" +"ã“ã®ãƒªãƒã‚¸ãƒˆãƒªã«ã¯ãŠãŠã‚ˆã %i 個ã®å€‹åˆ¥ã‚ªãƒ–ジェクトãŒã‚ã‚Šã¾ã™\n" +"\n" +"最é©ãªæ€§èƒ½ã‚’ä¿ã¤ãŸã‚ã«ã€%i 個以上ã®å€‹åˆ¥ã‚ªãƒ–ジェクトを作る毎ã«ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã‚’圧" +"縮ã™ã‚‹ã“ã¨ã‚’推奨ã—ã¾ã™\n" +"\n" +"データベースを圧縮ã—ã¾ã™ã‹ï¼Ÿ" + +#: lib/date.tcl:25 +#, tcl-format +msgid "Invalid date from Git: %s" +msgstr "Git ã‹ã‚‰å‡ºãŸç„¡åŠ¹ãªæ—¥ä»˜: %s" + +#: lib/diff.tcl:42 +#, tcl-format +msgid "" +"No differences detected.\n" +"\n" +"%s has no changes.\n" +"\n" +"The modification date of this file was updated by another application, but " +"the content within the file was not changed.\n" +"\n" +"A rescan will be automatically started to find other files which may have " +"the same state." +msgstr "" +"変更ãŒã‚ã‚Šã¾ã›ã‚“。\n" +"\n" +"%s ã«ã¯å¤‰æ›´ãŒã‚ã‚Šã¾ã›ã‚“。\n" +"\n" +"ã“ã®ãƒ•ã‚¡ã‚¤ãƒ«ã®å¤‰æ›´æ™‚刻ã¯ä»–ã®ã‚¢ãƒ—リケーションã«ã‚ˆã£ã¦æ›´æ–°ã•ã‚Œã¦ã„ã¾ã™ãŒãƒ•ã‚¡ã‚¤" +"ル内容ã«ã¯å¤‰æ›´ãŒã‚ã‚Šã¾ã›ã‚“。\n" +"\n" +"åŒæ§˜ãªçŠ¶æ…‹ã®ãƒ•ã‚¡ã‚¤ãƒ«ã‚’探ã™ãŸã‚ã«ã€è‡ªå‹•çš„ã«å†ã‚¹ã‚ャンを開始ã—ã¾ã™ã€‚" + +#: lib/diff.tcl:81 +#, tcl-format +msgid "Loading diff of %s..." +msgstr "%s ã®å¤‰æ›´ç‚¹ã‚’ãƒãƒ¼ãƒ‰ä¸â€¦" + +#: lib/diff.tcl:114 lib/diff.tcl:184 +#, tcl-format +msgid "Unable to display %s" +msgstr "%s を表示ã§ãã¾ã›ã‚“" + +#: lib/diff.tcl:115 +msgid "Error loading file:" +msgstr "ファイルをèªã‚€éš›ã®ã‚¨ãƒ©ãƒ¼ã§ã™:" + +#: lib/diff.tcl:122 +msgid "Git Repository (subproject)" +msgstr "Git リãƒã‚¸ãƒˆãƒª(サブプãƒã‚¸ã‚§ã‚¯ãƒˆ)" + +#: lib/diff.tcl:134 +msgid "* Binary file (not showing content)." +msgstr "* ãƒã‚¤ãƒŠãƒªãƒ•ã‚¡ã‚¤ãƒ«(内容ã¯è¡¨ç¤ºã—ã¾ã›ã‚“)" + +#: lib/diff.tcl:185 +msgid "Error loading diff:" +msgstr "diff ã‚’èªã‚€éš›ã®ã‚¨ãƒ©ãƒ¼ã§ã™:" + +#: lib/diff.tcl:302 +msgid "Failed to unstage selected hunk." +msgstr "é¸æŠžã•ã‚ŒãŸãƒ‘ッãƒã‚’コミット予定ã‹ã‚‰å¤–ã›ã¾ã›ã‚“。" + +#: lib/diff.tcl:309 +msgid "Failed to stage selected hunk." +msgstr "é¸æŠžã•ã‚ŒãŸãƒ‘ッãƒã‚’コミット予定ã«åŠ ãˆã‚‰ã‚Œã¾ã›ã‚“。" + +#: lib/error.tcl:12 lib/error.tcl:102 +msgid "error" +msgstr "エラー" + +#: lib/error.tcl:28 +msgid "warning" +msgstr "è¦å‘Š" + +#: lib/error.tcl:81 +msgid "You must correct the above errors before committing." +msgstr "コミットã™ã‚‹å‰ã«ã€ä»¥ä¸Šã®ã‚¨ãƒ©ãƒ¼ã‚’ä¿®æ£ã—ã¦ä¸‹ã•ã„" + +#: lib/index.tcl:6 +msgid "Unable to unlock the index." +msgstr "インデックスをãƒãƒƒã‚¯ã§ãã¾ã›ã‚“" + +#: lib/index.tcl:15 +msgid "Index Error" +msgstr "索引エラー" + +#: lib/index.tcl:21 +msgid "" +"Updating the Git index failed. A rescan will be automatically started to " +"resynchronize git-gui." +msgstr "GIT インデックスã®æ›´æ–°ãŒå¤±æ•—ã—ã¾ã—ãŸã€‚git-gui ã¨åŒæœŸã‚’ã¨ã‚‹ãŸã‚ã«å†ã‚¹ã‚ャンã—ã¾ã™ã€‚" + +#: lib/index.tcl:27 +msgid "Continue" +msgstr "続行" + +#: lib/index.tcl:31 +msgid "Unlock Index" +msgstr "インデックスã®ãƒãƒƒã‚¯è§£é™¤" + +#: lib/index.tcl:282 +#, tcl-format +msgid "Unstaging %s from commit" +msgstr "コミットã‹ã‚‰ '%s' ã‚’é™ã‚ã™" + +#: lib/index.tcl:326 +#, tcl-format +msgid "Adding %s" +msgstr "コミット㫠%s ã‚’åŠ ãˆã¦ã„ã¾ã™" + +#: lib/index.tcl:381 +#, tcl-format +msgid "Revert changes in file %s?" +msgstr "ファイル %s ã«ã—ãŸå¤‰æ›´ã‚’å…ƒã«æˆ»ã—ã¾ã™ã‹ï¼Ÿ" + +#: lib/index.tcl:383 +#, tcl-format +msgid "Revert changes in these %i files?" +msgstr "ã“れら %i 個ã®ãƒ•ã‚¡ã‚¤ãƒ«ã«ã—ãŸå¤‰æ›´ã‚’å…ƒã«æˆ»ã—ã¾ã™ã‹ï¼Ÿ" + +#: lib/index.tcl:389 +msgid "Any unstaged changes will be permanently lost by the revert." +msgstr "変更を元ã«æˆ»ã™ã¨ã‚³ãƒŸãƒƒãƒˆäºˆå®šã—ã¦ã„ãªã„変更ã¯å…¨ã¦å¤±ã‚ã‚Œã¾ã™ã€‚" + +#: lib/index.tcl:392 +msgid "Do Nothing" +msgstr "何もã—ãªã„" + +#: lib/merge.tcl:13 +msgid "" +"Cannot merge while amending.\n" +"\n" +"You must finish amending this commit before starting any type of merge.\n" +msgstr "" +"訂æ£ä¸ã«ã¯ãƒžãƒ¼ã‚¸ã§ãã¾ã›ã‚“。\n" +"\n" +"訂æ£å‡¦ç†ã‚’完了ã™ã‚‹ã¾ã§ã¯æ–°ãŸã«ãƒžãƒ¼ã‚¸ã‚’開始ã§ãã¾ã›ã‚“。\n" + +#: lib/merge.tcl:27 +msgid "" +"Last scanned state does not match repository state.\n" +"\n" +"Another Git program has modified this repository since the last scan. A " +"rescan must be performed before a merge can be performed.\n" +"\n" +"The rescan will be automatically started now.\n" +msgstr "" +"最後ã«ã‚¹ã‚ャンã—ãŸçŠ¶æ…‹ã¯ãƒªãƒã‚¸ãƒˆãƒªã®çŠ¶æ…‹ã¨åˆè‡´ã—ã¾ã›ã‚“。\n" +"\n" +"最後ã«ã‚¹ã‚ャンã—ã¦ä»¥å¾Œã€åˆ¥ã® Git プãƒã‚°ãƒ©ãƒ ãŒãƒªãƒã‚¸ãƒˆãƒªã‚’変更ã—ã¦ã„ã¾ã™ã€‚マー" +"ジを開始ã™ã‚‹å‰ã«ã€å†ã‚¹ã‚ャンãŒå¿…è¦ã§ã™ã€‚\n" +"\n" +"自動的ã«å†ã‚¹ã‚ャンを開始ã—ã¾ã™ã€‚\n" + +#: lib/merge.tcl:44 +#, tcl-format +msgid "" +"You are in the middle of a conflicted merge.\n" +"\n" +"File %s has merge conflicts.\n" +"\n" +"You must resolve them, stage the file, and commit to complete the current " +"merge. Only then can you begin another merge.\n" +msgstr "" +"è¡çªã®ã‚ã£ãŸãƒžãƒ¼ã‚¸ã®é€”ä¸ã§ã™ã€‚\n" +"\n" +"ファイル %s ã«ã¯ãƒžãƒ¼ã‚¸ä¸ã®è¡çªãŒæ®‹ã£ã¦ã„ã¾ã™ã€‚\n" +"\n" +"ã“ã®ãƒ•ã‚¡ã‚¤ãƒ«ã®è¡çªã‚’解決ã—ã€ã‚³ãƒŸãƒƒãƒˆäºˆå®šã«åŠ ãˆã¦ã€ã‚³ãƒŸãƒƒãƒˆã™ã‚‹ã“ã¨ã§ãƒžãƒ¼ã‚¸ã‚’" +"完了ã—ã¾ã™ã€‚ãã†ã‚„ã£ã¦å§‹ã‚ã¦ã€æ–°ãŸãªãƒžãƒ¼ã‚¸ã‚’開始ã§ãるよã†ã«ãªã‚Šã¾ã™ã€‚\n" + +#: lib/merge.tcl:54 +#, tcl-format +msgid "" +"You are in the middle of a change.\n" +"\n" +"File %s is modified.\n" +"\n" +"You should complete the current commit before starting a merge. Doing so " +"will help you abort a failed merge, should the need arise.\n" +msgstr "" +"変更ã®é€”ä¸ã§ã™ã€‚\n" +"\n" +"ファイル %s ã¯å¤‰æ›´ä¸ã§ã™ã€‚\n" +"\n" +"ç¾åœ¨ã®ã‚³ãƒŸãƒƒãƒˆã‚’完了ã—ã¦ã‹ã‚‰ãƒžãƒ¼ã‚¸ã‚’開始ã—ã¦ä¸‹ã•ã„。ãã†ã™ã‚‹æ–¹ãŒãƒžãƒ¼ã‚¸ã«å¤±æ•—" +"ã—ãŸã¨ãã®å›žå¾©ãŒæ¥½ã§ã™ã€‚\n" + +#: lib/merge.tcl:106 +#, tcl-format +msgid "%s of %s" +msgstr "%s ã® %s ブランãƒ" + +#: lib/merge.tcl:119 +#, tcl-format +msgid "Merging %s and %s" +msgstr "%s 㨠%s をマージã—ã¾ã™" + +#: lib/merge.tcl:131 +msgid "Merge completed successfully." +msgstr "マージãŒå®Œäº†ã—ã¾ã—ãŸ" + +#: lib/merge.tcl:133 +msgid "Merge failed. Conflict resolution is required." +msgstr "マージãŒå¤±æ•—ã—ã¾ã—ãŸã€‚è¡çªã®è§£æ±ºãŒå¿…è¦ã§ã™ã€‚" + +#: lib/merge.tcl:158 +#, tcl-format +msgid "Merge Into %s" +msgstr "%s ã«ãƒžãƒ¼ã‚¸" + +#: lib/merge.tcl:177 +msgid "Revision To Merge" +msgstr "マージã™ã‚‹ãƒªãƒ“ジョン" + +#: lib/merge.tcl:212 +msgid "" +"Cannot abort while amending.\n" +"\n" +"You must finish amending this commit.\n" +msgstr "" +"訂æ£ä¸ã«ã¯ä¸æ¢ã§ãã¾ã›ã‚“。\n" +"\n" +"ã¾ãšä»Šã®ã‚³ãƒŸãƒƒãƒˆè¨‚æ£ã‚’完了ã•ã›ã¦ä¸‹ã•ã„。\n" + +#: lib/merge.tcl:222 +msgid "" +"Abort merge?\n" +"\n" +"Aborting the current merge will cause *ALL* uncommitted changes to be lost.\n" +"\n" +"Continue with aborting the current merge?" +msgstr "" +"マージをä¸æ–ã—ã¾ã™ã‹ï¼Ÿ\n" +"\n" +"ç¾åœ¨ã®ãƒžãƒ¼ã‚¸ã‚’ä¸æ–ã™ã‚‹ã¨ã€ã‚³ãƒŸãƒƒãƒˆã—ã¦ã„ãªã„å…¨ã¦ã®å¤‰æ›´ãŒå¤±ã‚ã‚Œã¾ã™ã€‚\n" +"\n" +"マージをä¸æ–ã—ã¦ã‚ˆã‚ã—ã„ã§ã™ã‹ï¼Ÿ" + +#: lib/merge.tcl:228 +msgid "" +"Reset changes?\n" +"\n" +"Resetting the changes will cause *ALL* uncommitted changes to be lost.\n" +"\n" +"Continue with resetting the current changes?" +msgstr "" +"変更点をリセットã—ã¾ã™ã‹ï¼Ÿ\n" +"\n" +"変更点をリセットã™ã‚‹ã¨ã€ã‚³ãƒŸãƒƒãƒˆã—ã¦ã„ãªã„å…¨ã¦ã®å¤‰æ›´ãŒå¤±ã‚ã‚Œã¾ã™ã€‚\n" +"\n" +"リセットã—ã¦ã‚ˆã‚ã—ã„ã§ã™ã‹ï¼Ÿ" + +#: lib/merge.tcl:239 +msgid "Aborting" +msgstr "ä¸æ–ã—ã¦ã„ã¾ã™" + +#: lib/merge.tcl:266 +msgid "Abort failed." +msgstr "ä¸æ–ã«å¤±æ•—ã—ã¾ã—ãŸã€‚" + +#: lib/merge.tcl:268 +msgid "Abort completed. Ready." +msgstr "ä¸æ–完了。" + +#: lib/option.tcl:82 +msgid "Restore Defaults" +msgstr "既定値ã«æˆ»ã™" + +#: lib/option.tcl:86 +msgid "Save" +msgstr "ä¿å˜" + +#: lib/option.tcl:96 +#, tcl-format +msgid "%s Repository" +msgstr "%s リãƒã‚¸ãƒˆãƒª" + +#: lib/option.tcl:97 +msgid "Global (All Repositories)" +msgstr "大域(全ã¦ã®ãƒªãƒã‚¸ãƒˆãƒªï¼‰" + +#: lib/option.tcl:103 +msgid "User Name" +msgstr "ユーザå" + +#: lib/option.tcl:104 +msgid "Email Address" +msgstr "é›»åメールアドレス" + +#: lib/option.tcl:106 +msgid "Summarize Merge Commits" +msgstr "マージコミットã®è¦ç´„" + +#: lib/option.tcl:107 +msgid "Merge Verbosity" +msgstr "マージã®å†—長度" + +#: lib/option.tcl:108 +msgid "Show Diffstat After Merge" +msgstr "マージ後㫠diffstat を表示" + +#: lib/option.tcl:110 +msgid "Trust File Modification Timestamps" +msgstr "ãƒ•ã‚¡ã‚¤ãƒ«å¤‰æ›´æ™‚åˆ»ã‚’ä¿¡é ¼ã™ã‚‹" + +#: lib/option.tcl:111 +msgid "Prune Tracking Branches During Fetch" +msgstr "フェッãƒä¸ã«ãƒˆãƒ©ãƒƒã‚ングブランãƒã‚’刈る" + +#: lib/option.tcl:112 +msgid "Match Tracking Branches" +msgstr "トラッã‚ングブランãƒã‚’åˆã‚ã›ã‚‹" + +#: lib/option.tcl:113 +msgid "Number of Diff Context Lines" +msgstr "diff ã®æ–‡è„ˆè¡Œæ•°" + +#: lib/option.tcl:114 +msgid "New Branch Name Template" +msgstr "æ–°ã—ã„ブランãƒåã®ãƒ†ãƒ³ãƒ—レート" + +#: lib/option.tcl:176 +msgid "Change Font" +msgstr "フォントを変更" + +#: lib/option.tcl:180 +#, tcl-format +msgid "Choose %s" +msgstr "%s ã‚’é¸æŠž" + +#: lib/option.tcl:186 +msgid "pt." +msgstr "ãƒã‚¤ãƒ³ãƒˆ" + +#: lib/option.tcl:200 +msgid "Preferences" +msgstr "è¨å®š" + +#: lib/option.tcl:235 +msgid "Failed to completely save options:" +msgstr "完全ã«ã‚ªãƒ—ションをä¿å˜ã§ãã¾ã›ã‚“:" + +#: lib/remote_branch_delete.tcl:29 lib/remote_branch_delete.tcl:34 +msgid "Delete Remote Branch" +msgstr "リモート・ブランãƒã‚’削除" + +#: lib/remote_branch_delete.tcl:47 +msgid "From Repository" +msgstr "å…ƒã®ãƒªãƒã‚¸ãƒˆãƒª" + +#: lib/remote_branch_delete.tcl:50 lib/transport.tcl:123 +msgid "Remote:" +msgstr "リモート:" + +#: lib/remote_branch_delete.tcl:66 lib/transport.tcl:138 +msgid "Arbitrary URL:" +msgstr "ä»»æ„ã® URL:" + +#: lib/remote_branch_delete.tcl:84 +msgid "Branches" +msgstr "ブランãƒ" + +#: lib/remote_branch_delete.tcl:109 +msgid "Delete Only If" +msgstr "æ¡ä»¶ä»˜ã§å‰Šé™¤" + +#: lib/remote_branch_delete.tcl:111 +msgid "Merged Into:" +msgstr "マージ先:" + +#: lib/remote_branch_delete.tcl:119 +msgid "Always (Do not perform merge checks)" +msgstr "ç„¡æ¡ä»¶ï¼ˆãƒžãƒ¼ã‚¸æ¤œæŸ»ã‚’ã—ãªã„)" + +#: lib/remote_branch_delete.tcl:152 +msgid "A branch is required for 'Merged Into'." +msgstr "'マージ先' ã«ã¯ãƒ–ランãƒãŒå¿…è¦ã§ã™ã€‚" + +#: lib/remote_branch_delete.tcl:184 +#, tcl-format +msgid "" +"The following branches are not completely merged into %s:\n" +"\n" +" - %s" +msgstr "" +"以下ã®ãƒ–ランãƒã¯ %s ã«å®Œå…¨ã«ãƒžãƒ¼ã‚¸ã•ã‚Œã¦ã„ã¾ã›ã‚“:\n" +"\n" +" - %s" + +#: lib/remote_branch_delete.tcl:189 +#, tcl-format +msgid "" +"One or more of the merge tests failed because you have not fetched the " +"necessary commits. Try fetching from %s first." +msgstr "" +"å¿…è¦ãªã‚³ãƒŸãƒƒãƒˆãŒä¸è¶³ã—ã¦ã„ã‚‹ãŸã‚ã«ã€ãƒžãƒ¼ã‚¸æ¤œæŸ»ãŒå¤±æ•—ã—ã¾ã—ãŸã€‚ã¾ãš %s ã‹ã‚‰" +"フェッãƒã—ã¦ä¸‹ã•ã„。" + +#: lib/remote_branch_delete.tcl:207 +msgid "Please select one or more branches to delete." +msgstr "削除ã™ã‚‹ãƒ–ランãƒã‚’é¸æŠžã—ã¦ä¸‹ã•ã„。" + +#: lib/remote_branch_delete.tcl:216 +msgid "" +"Recovering deleted branches is difficult.\n" +"\n" +"Delete the selected branches?" +msgstr "" +"削除ã—ãŸãƒ–ランãƒã‚’回復ã™ã‚‹ã®ã¯å›°é›£ã§ã™ã€‚\n" +"\n" +"é¸æŠžã—ãŸãƒ–ランãƒã‚’削除ã—ã¦è‰¯ã„ã§ã™ã‹ï¼Ÿ" + +#: lib/remote_branch_delete.tcl:226 +#, tcl-format +msgid "Deleting branches from %s" +msgstr "%s ã‹ã‚‰ãƒ–ランãƒã‚’削除ã—ã¦ã„ã¾ã™ã€‚" + +#: lib/remote_branch_delete.tcl:286 +msgid "No repository selected." +msgstr "リãƒã‚¸ãƒˆãƒªãŒé¸æŠžã•ã‚Œã¦ã„ã¾ã›ã‚“。" + +#: lib/remote_branch_delete.tcl:291 +#, tcl-format +msgid "Scanning %s..." +msgstr "%s をスã‚ャンã—ã¦ã„ã¾ã™â€¦" + +#: lib/remote.tcl:165 +msgid "Prune from" +msgstr "ã‹ã‚‰åˆˆè¾¼ã‚€â€¦" + +#: lib/remote.tcl:170 +msgid "Fetch from" +msgstr "å–å¾—å…ƒ" + +#: lib/remote.tcl:213 +msgid "Push to" +msgstr "プッシュ先" + +#: lib/shortcut.tcl:20 lib/shortcut.tcl:61 +msgid "Cannot write shortcut:" +msgstr "ショートカットãŒæ›¸ã‘ã¾ã›ã‚“:" + +#: lib/shortcut.tcl:136 +msgid "Cannot write icon:" +msgstr "アイコンãŒæ›¸ã‘ã¾ã›ã‚“:" + +#: lib/status_bar.tcl:83 +#, tcl-format +msgid "%s ... %*i of %*i %s (%3i%%)" +msgstr "%1$s ... %4$*i %6$s ä¸ã® %2$*i (%7$3i%%)" + +#: lib/transport.tcl:6 +#, tcl-format +msgid "fetch %s" +msgstr "%s ã‚’å–å¾—" + +#: lib/transport.tcl:7 +#, tcl-format +msgid "Fetching new changes from %s" +msgstr "%s ã‹ã‚‰æ–°ã—ã„変更をフェッãƒã—ã¦ã„ã¾ã™" + +#: lib/transport.tcl:18 +#, tcl-format +msgid "remote prune %s" +msgstr "é 隔刈込 %s" + +#: lib/transport.tcl:19 +#, tcl-format +msgid "Pruning tracking branches deleted from %s" +msgstr "%s ã‹ã‚‰å‰Šé™¤ã•ã‚ŒãŸãƒˆãƒ©ãƒƒã‚ング・ブランãƒã‚’刈ã£ã¦ã„ã¾ã™" + +#: lib/transport.tcl:25 lib/transport.tcl:71 +#, tcl-format +msgid "push %s" +msgstr "%s をプッシュ" + +#: lib/transport.tcl:26 +#, tcl-format +msgid "Pushing changes to %s" +msgstr "%s ã¸å¤‰æ›´ã‚’プッシュã—ã¦ã„ã¾ã™" + +#: lib/transport.tcl:72 +#, tcl-format +msgid "Pushing %s %s to %s" +msgstr "%3$s 㸠%1$s %2$s をプッシュã—ã¦ã„ã¾ã™" + +#: lib/transport.tcl:89 +msgid "Push Branches" +msgstr "ブランãƒã‚’プッシュ" + +#: lib/transport.tcl:103 +msgid "Source Branches" +msgstr "å…ƒã®ãƒ–ランãƒ" + +#: lib/transport.tcl:120 +msgid "Destination Repository" +msgstr "é€ã‚Šå…ˆãƒªãƒã‚¸ãƒˆãƒª" + +#: lib/transport.tcl:158 +msgid "Transfer Options" +msgstr "通信オプション" + +#: lib/transport.tcl:160 +msgid "Force overwrite existing branch (may discard changes)" +msgstr "æ—¢å˜ãƒ–ランãƒã‚’上書ã(å¤‰æ›´ã‚’ç ´æ£„ã™ã‚‹å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™)" + +#: lib/transport.tcl:164 +msgid "Use thin pack (for slow network connections)" +msgstr "Thin Pack を使ã†ï¼ˆé…ã„ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯æŽ¥ç¶šï¼‰" + +#: lib/transport.tcl:168 +msgid "Include tags" +msgstr "ã‚¿ã‚°ã‚’å«ã‚ã‚‹" diff --git a/git-gui/po/po2msg.sh b/git-gui/po/po2msg.sh new file mode 100644 index 0000000000..c63248e375 --- /dev/null +++ b/git-gui/po/po2msg.sh @@ -0,0 +1,133 @@ +#!/bin/sh +# Tcl ignores the next line -*- tcl -*- \ +exec tclsh "$0" -- "$@" + +# This is a really stupid program, which serves as an alternative to +# msgfmt. It _only_ translates to Tcl mode, does _not_ validate the +# input, and does _not_ output any statistics. + +proc u2a {s} { + set res "" + foreach i [split $s ""] { + scan $i %c c + if {$c<128} { + # escape '[', '\' and ']' + if {$c == 0x5b || $c == 0x5d} { + append res "\\" + } + append res $i + } else { + append res \\u[format %04.4x $c] + } + } + return $res +} + +set output_directory "." +set lang "dummy" +set files [list] +set show_statistics 0 + +# parse options +for {set i 0} {$i < $argc} {incr i} { + set arg [lindex $argv $i] + if {$arg == "--statistics"} { + incr show_statistics + continue + } + if {$arg == "--tcl"} { + # we know + continue + } + if {$arg == "-l"} { + incr i + set lang [lindex $argv $i] + continue + } + if {$arg == "-d"} { + incr i + set tmp [lindex $argv $i] + regsub "\[^/\]$" $tmp "&/" output_directory + continue + } + lappend files $arg +} + +proc flush_msg {} { + global msgid msgstr mode lang out fuzzy + global translated_count fuzzy_count not_translated_count + + if {![info exists msgid] || $mode == ""} { + return + } + set mode "" + if {$fuzzy == 1} { + incr fuzzy_count + set fuzzy 0 + return + } + + if {$msgid == ""} { + set prefix "set ::msgcat::header" + } else { + if {$msgstr == ""} { + incr not_translated_count + return + } + set prefix "::msgcat::mcset $lang \"[u2a $msgid]\"" + incr translated_count + } + + puts $out "$prefix \"[u2a $msgstr]\"" +} + +set fuzzy 0 +set translated_count 0 +set fuzzy_count 0 +set not_translated_count 0 +foreach file $files { + regsub "^.*/\(\[^/\]*\)\.po$" $file "$output_directory\\1.msg" outfile + set in [open $file "r"] + fconfigure $in -encoding utf-8 + set out [open $outfile "w"] + + set mode "" + while {[gets $in line] >= 0} { + if {[regexp "^#" $line]} { + if {[regexp ", fuzzy" $line]} { + set fuzzy 1 + } else { + flush_msg + } + continue + } elseif {[regexp "^msgid \"(.*)\"$" $line dummy match]} { + flush_msg + set msgid $match + set mode "msgid" + } elseif {[regexp "^msgstr \"(.*)\"$" $line dummy match]} { + set msgstr $match + set mode "msgstr" + } elseif {$line == ""} { + flush_msg + } elseif {[regexp "^\"(.*)\"$" $line dummy match]} { + if {$mode == "msgid"} { + append msgid $match + } elseif {$mode == "msgstr"} { + append msgstr $match + } else { + puts stderr "I do not know what to do: $match" + } + } else { + puts stderr "Cannot handle $line" + } + } + flush_msg + close $in + close $out +} + +if {$show_statistics} { + puts [concat "$translated_count translated messages, " \ + "$fuzzy_count fuzzy ones, " \ + "$not_translated_count untranslated ones."] +} diff --git a/git-gui/po/ru.po b/git-gui/po/ru.po new file mode 100644 index 0000000000..6727a832ea --- /dev/null +++ b/git-gui/po/ru.po @@ -0,0 +1,1893 @@ +# Translation of git-gui to russian +# Copyright (C) 2007 Shawn Pearce +# This file is distributed under the same license as the git-gui package. +# Irina Riesen <irina.riesen@gmail.com>, 2007. +# +msgid "" +msgstr "" +"Project-Id-Version: git-gui\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2007-10-31 21:23+0100\n" +"PO-Revision-Date: 2007-10-22 22:30-0200\n" +"Last-Translator: Alex Riesen <raa.lkml@gmail.com>\n" +"Language-Team: Russian Translation <git@vger.kernel.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: git-gui.sh:41 git-gui.sh:597 git-gui.sh:611 git-gui.sh:624 git-gui.sh:707 +#: git-gui.sh:726 +msgid "git-gui: fatal error" +msgstr "git-gui: критичеÑÐºÐ°Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ°" + +#: git-gui.sh:558 +#, tcl-format +msgid "Invalid font specified in %s:" +msgstr "Ð’ %s уÑтановлен неверный шрифт:" + +#: git-gui.sh:583 +msgid "Main Font" +msgstr "Шрифт интерфейÑа" + +#: git-gui.sh:584 +msgid "Diff/Console Font" +msgstr "Шрифт конÑоли и изменений (diff)" + +#: git-gui.sh:598 +msgid "Cannot find git in PATH." +msgstr "git не найден в PATH." + +#: git-gui.sh:625 +msgid "Cannot parse Git version string:" +msgstr "Ðевозможно раÑпознать Ñтроку верÑии Git: " + +#: git-gui.sh:643 +#, tcl-format +msgid "" +"Git version cannot be determined.\n" +"\n" +"%s claims it is version '%s'.\n" +"\n" +"%s requires at least Git 1.5.0 or later.\n" +"\n" +"Assume '%s' is version 1.5.0?\n" +msgstr "" +"Ðевозможно определить верÑию Git\n" +"%s указывает на верÑию '%s'.\n" +"\n" +"Ð´Ð»Ñ %s требуетÑÑ Ð²ÐµÑ€ÑÐ¸Ñ Git, Ð½Ð°Ñ‡Ð¸Ð½Ð°Ñ Ñ 1.5.0\n" +"\n" +"ПринÑÑ‚ÑŒ '%s' как верÑию 1.5.0?\n" + +#: git-gui.sh:881 +msgid "Git directory not found:" +msgstr "Каталог Git не найден:" + +#: git-gui.sh:888 +msgid "Cannot move to top of working directory:" +msgstr "Ðевозможно перейти к корню рабочего каталога репозиториÑ: " + +#: git-gui.sh:895 +msgid "Cannot use funny .git directory:" +msgstr "Каталог.git иÑпорчен: " + +#: git-gui.sh:900 +msgid "No working directory" +msgstr "ОтÑутÑтвует рабочий каталог" + +#: git-gui.sh:1047 +msgid "Refreshing file status..." +msgstr "Обновление информации о ÑоÑтоÑнии файлов..." + +#: git-gui.sh:1112 +msgid "Scanning for modified files ..." +msgstr "ПоиÑк измененных файлов..." + +#: git-gui.sh:1287 lib/browser.tcl:245 +msgid "Ready." +msgstr "Готово." + +#: git-gui.sh:1553 +msgid "Unmodified" +msgstr "Ðе изменено" + +#: git-gui.sh:1555 +msgid "Modified, not staged" +msgstr "Изменено, не подготовлено" + +#: git-gui.sh:1556 git-gui.sh:1561 +msgid "Staged for commit" +msgstr "Подготовлено Ð´Ð»Ñ ÑохранениÑ" + +#: git-gui.sh:1557 git-gui.sh:1562 +msgid "Portions staged for commit" +msgstr "ЧаÑти, подготовленные Ð´Ð»Ñ ÑохранениÑ" + +#: git-gui.sh:1558 git-gui.sh:1563 +msgid "Staged for commit, missing" +msgstr "Подготовлено Ð´Ð»Ñ ÑохранениÑ, отÑутÑтвует" + +#: git-gui.sh:1560 +msgid "Untracked, not staged" +msgstr "Ðе отÑлеживаетÑÑ, не подготовлено" + +#: git-gui.sh:1565 +msgid "Missing" +msgstr "ОтÑутÑтвует" + +#: git-gui.sh:1566 +msgid "Staged for removal" +msgstr "Подготовлено Ð´Ð»Ñ ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ" + +#: git-gui.sh:1567 +msgid "Staged for removal, still present" +msgstr "Подготовлено Ð´Ð»Ñ ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ, еще не удалено" + +#: git-gui.sh:1569 git-gui.sh:1570 git-gui.sh:1571 git-gui.sh:1572 +msgid "Requires merge resolution" +msgstr "ТребуетÑÑ Ñ€Ð°Ð·Ñ€ÐµÑˆÐµÐ½Ð¸Ðµ конфликта при объединении" + +#: git-gui.sh:1607 +msgid "Starting gitk... please wait..." +msgstr "ЗапуÑкаетÑÑ gitk... пожалуйÑта, ждите..." + +#: git-gui.sh:1616 +#, tcl-format +msgid "" +"Unable to start gitk:\n" +"\n" +"%s does not exist" +msgstr "" +"Ðе удалоÑÑŒ запуÑтить gitk:\n" +"\n" +"%s не ÑущеÑтвует" + +#: git-gui.sh:1816 lib/choose_repository.tcl:35 +msgid "Repository" +msgstr "Репозиторий" + +#: git-gui.sh:1817 +msgid "Edit" +msgstr "Редактировать" + +#: git-gui.sh:1819 lib/choose_rev.tcl:560 +msgid "Branch" +msgstr "Ветвь" + +#: git-gui.sh:1822 lib/choose_rev.tcl:547 +msgid "Commit@@noun" +msgstr "СоÑтоÑние" + +#: git-gui.sh:1825 lib/merge.tcl:121 lib/merge.tcl:150 lib/merge.tcl:168 +msgid "Merge" +msgstr "Объединить" + +#: git-gui.sh:1826 lib/choose_rev.tcl:556 +msgid "Remote" +msgstr "Внешние репозитории" + +#: git-gui.sh:1835 +msgid "Browse Current Branch's Files" +msgstr "ПроÑмотреть файлы текущей ветви" + +#: git-gui.sh:1839 +msgid "Browse Branch Files..." +msgstr "Показать файлы ветви..." + +#: git-gui.sh:1844 +msgid "Visualize Current Branch's History" +msgstr "ИÑÑ‚Ð¾Ñ€Ð¸Ñ Ñ‚ÐµÐºÑƒÑ‰ÐµÐ¹ ветви наглÑдно" + +#: git-gui.sh:1848 +msgid "Visualize All Branch History" +msgstr "ИÑÑ‚Ð¾Ñ€Ð¸Ñ Ð²Ñех ветвей наглÑдно" + +#: git-gui.sh:1855 +#, tcl-format +msgid "Browse %s's Files" +msgstr "Показать файлы ветви %s" + +#: git-gui.sh:1857 +#, tcl-format +msgid "Visualize %s's History" +msgstr "ИÑÑ‚Ð¾Ñ€Ð¸Ñ Ð²ÐµÑ‚Ð²Ð¸ %s наглÑдно" + +#: git-gui.sh:1862 lib/database.tcl:27 lib/database.tcl:67 +msgid "Database Statistics" +msgstr "СтатиÑтика базы данных" + +#: git-gui.sh:1865 lib/database.tcl:34 +msgid "Compress Database" +msgstr "Сжать базу данных" + +#: git-gui.sh:1868 +msgid "Verify Database" +msgstr "Проверить базу данных" + +#: git-gui.sh:1875 git-gui.sh:1879 git-gui.sh:1883 lib/shortcut.tcl:7 +#: lib/shortcut.tcl:39 lib/shortcut.tcl:71 +msgid "Create Desktop Icon" +msgstr "Создать Ñрлык на рабочем Ñтоле" + +#: git-gui.sh:1888 lib/choose_repository.tcl:176 lib/choose_repository.tcl:184 +msgid "Quit" +msgstr "Выход" + +#: git-gui.sh:1895 +msgid "Undo" +msgstr "Отменить" + +#: git-gui.sh:1898 +msgid "Redo" +msgstr "Повторить" + +#: git-gui.sh:1902 git-gui.sh:2395 +msgid "Cut" +msgstr "Вырезать" + +#: git-gui.sh:1905 git-gui.sh:2398 git-gui.sh:2469 git-gui.sh:2541 +#: lib/console.tcl:67 +msgid "Copy" +msgstr "Копировать" + +#: git-gui.sh:1908 git-gui.sh:2401 +msgid "Paste" +msgstr "Ð’Ñтавить" + +#: git-gui.sh:1911 git-gui.sh:2404 lib/branch_delete.tcl:26 +#: lib/remote_branch_delete.tcl:38 +msgid "Delete" +msgstr "Удалить" + +#: git-gui.sh:1915 git-gui.sh:2408 git-gui.sh:2545 lib/console.tcl:69 +msgid "Select All" +msgstr "Выделить вÑе" + +#: git-gui.sh:1924 +msgid "Create..." +msgstr "Создать..." + +#: git-gui.sh:1930 +msgid "Checkout..." +msgstr "Перейти..." + +#: git-gui.sh:1936 +msgid "Rename..." +msgstr "Переименовать..." + +#: git-gui.sh:1941 git-gui.sh:2040 +msgid "Delete..." +msgstr "Удалить..." + +#: git-gui.sh:1946 +msgid "Reset..." +msgstr "СброÑить..." + +#: git-gui.sh:1958 git-gui.sh:2342 +msgid "New Commit" +msgstr "Ðовое ÑоÑтоÑние" + +#: git-gui.sh:1966 git-gui.sh:2349 +msgid "Amend Last Commit" +msgstr "ИÑправить поÑледнее ÑоÑтоÑние" + +#: git-gui.sh:1975 git-gui.sh:2309 lib/remote_branch_delete.tcl:99 +msgid "Rescan" +msgstr "Перечитать" + +#: git-gui.sh:1981 +msgid "Stage To Commit" +msgstr "Подготовить Ð´Ð»Ñ ÑохранениÑ" + +#: git-gui.sh:1986 +msgid "Stage Changed Files To Commit" +msgstr "Подготовить измененные файлы Ð´Ð»Ñ ÑохранениÑ" + +#: git-gui.sh:1992 +msgid "Unstage From Commit" +msgstr "Убрать из подготовленного" + +#: git-gui.sh:1997 lib/index.tcl:393 +msgid "Revert Changes" +msgstr "Отменить изменениÑ" + +#: git-gui.sh:2004 git-gui.sh:2321 git-gui.sh:2419 +msgid "Sign Off" +msgstr "ПодпиÑать" + +#: git-gui.sh:2008 git-gui.sh:2325 +msgid "Commit@@verb" +msgstr "Сохранить" + +#: git-gui.sh:2019 +msgid "Local Merge..." +msgstr "Локальное объединение..." + +#: git-gui.sh:2024 +msgid "Abort Merge..." +msgstr "Прервать объединение..." + +#: git-gui.sh:2036 +msgid "Push..." +msgstr "Отправить..." + +#: git-gui.sh:2047 lib/choose_repository.tcl:40 +msgid "Apple" +msgstr "" + +#: git-gui.sh:2050 git-gui.sh:2072 lib/about.tcl:13 +#: lib/choose_repository.tcl:43 lib/choose_repository.tcl:49 +#, tcl-format +msgid "About %s" +msgstr "О %s" + +#: git-gui.sh:2054 +msgid "Preferences..." +msgstr "ÐаÑтройки..." + +#: git-gui.sh:2062 git-gui.sh:2587 +msgid "Options..." +msgstr "ÐаÑтройки..." + +#: git-gui.sh:2068 lib/choose_repository.tcl:46 +msgid "Help" +msgstr "Помощь" + +#: git-gui.sh:2109 +msgid "Online Documentation" +msgstr "Ð”Ð¾ÐºÑƒÐ¼ÐµÐ½Ñ‚Ð°Ñ†Ð¸Ñ Ð² интернете" + +#: git-gui.sh:2193 +#, tcl-format +msgid "fatal: cannot stat path %s: No such file or directory" +msgstr "критичеÑÐºÐ°Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ°: %s: нет такого файла или каталога" + +#: git-gui.sh:2226 +msgid "Current Branch:" +msgstr "Ð¢ÐµÐºÑƒÑ‰Ð°Ñ Ð²ÐµÑ‚Ð²ÑŒ:" + +#: git-gui.sh:2247 +msgid "Staged Changes (Will Commit)" +msgstr "Подготовлено (будет Ñохранено)" + +#: git-gui.sh:2266 +msgid "Unstaged Changes" +msgstr "Изменено (не будет Ñохранено)" + +#: git-gui.sh:2315 +msgid "Stage Changed" +msgstr "Подготовить вÑе" + +#: git-gui.sh:2331 lib/transport.tcl:93 lib/transport.tcl:182 +msgid "Push" +msgstr "Отправить" + +#: git-gui.sh:2361 +msgid "Initial Commit Message:" +msgstr "Комментарий к первому ÑоÑтоÑнию:" + +#: git-gui.sh:2362 +msgid "Amended Commit Message:" +msgstr "Комментарий к иÑправленному ÑоÑтоÑнию:" + +#: git-gui.sh:2363 +msgid "Amended Initial Commit Message:" +msgstr "Комментарий к иÑправленному первоначальному ÑоÑтоÑнию:" + +#: git-gui.sh:2364 +msgid "Amended Merge Commit Message:" +msgstr "Комментарий к иÑправленному объединению:" + +#: git-gui.sh:2365 +msgid "Merge Commit Message:" +msgstr "Комментарий к объединению:" + +#: git-gui.sh:2366 +msgid "Commit Message:" +msgstr "Комментарий к ÑоÑтоÑнию:" + +#: git-gui.sh:2411 git-gui.sh:2549 lib/console.tcl:71 +msgid "Copy All" +msgstr "Копировать вÑе" + +#: git-gui.sh:2435 lib/blame.tcl:104 +msgid "File:" +msgstr "Файл:" + +#: git-gui.sh:2537 +msgid "Refresh" +msgstr "Обновить" + +#: git-gui.sh:2558 +msgid "Apply/Reverse Hunk" +msgstr "Применить/Убрать изменение" + +#: git-gui.sh:2564 +msgid "Decrease Font Size" +msgstr "Уменьшить размер шрифта" + +#: git-gui.sh:2568 +msgid "Increase Font Size" +msgstr "Увеличить размер шрифта" + +#: git-gui.sh:2573 +msgid "Show Less Context" +msgstr "Меньше контекÑта" + +#: git-gui.sh:2580 +msgid "Show More Context" +msgstr "Больше контекÑта" + +#: git-gui.sh:2594 +msgid "Unstage Hunk From Commit" +msgstr "Ðе ÑохранÑÑ‚ÑŒ чаÑÑ‚ÑŒ" + +#: git-gui.sh:2596 +msgid "Stage Hunk For Commit" +msgstr "Подготовить чаÑÑ‚ÑŒ Ð´Ð»Ñ ÑохранениÑ" + +#: git-gui.sh:2615 +msgid "Initializing..." +msgstr "ИнициализациÑ..." + +#: git-gui.sh:2706 +#, tcl-format +msgid "" +"Possible environment issues exist.\n" +"\n" +"The following environment variables are probably\n" +"going to be ignored by any Git subprocess run\n" +"by %s:\n" +"\n" +msgstr "" +"Возможны ошибки в переменных окружениÑ.\n" +"\n" +"Переменные окружениÑ, которые возможно\n" +"будут проигнорированы командами Git,\n" +"запущенными из %s\n" +"\n" + +#: git-gui.sh:2736 +msgid "" +"\n" +"This is due to a known issue with the\n" +"Tcl binary distributed by Cygwin." +msgstr "" +"\n" +"Ðто извеÑÑ‚Ð½Ð°Ñ Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼Ð° Ñ Tcl,\n" +"раÑпроÑтранÑемым Cygwin." + +#: git-gui.sh:2741 +#, tcl-format +msgid "" +"\n" +"\n" +"A good replacement for %s\n" +"is placing values for the user.name and\n" +"user.email settings into your personal\n" +"~/.gitconfig file.\n" +msgstr "" +"\n" +"\n" +"ВмеÑто иÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ %s можно\n" +"Ñохранить Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ user.name и\n" +"user.email в Вашем перÑональном\n" +"файле ~/.gitconfig.\n" + +#: lib/about.tcl:25 +msgid "git-gui - a graphical user interface for Git." +msgstr "git-gui - графичеÑкий пользовательÑкий Ð¸Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ Ðº Git." + +#: lib/blame.tcl:77 +msgid "File Viewer" +msgstr "ПроÑмотр файла" + +#: lib/blame.tcl:81 +msgid "Commit:" +msgstr "Сохраненное ÑоÑтоÑние:" + +#: lib/blame.tcl:249 +msgid "Copy Commit" +msgstr "Скопировать SHA-1" + +#: lib/blame.tcl:369 +#, tcl-format +msgid "Reading %s..." +msgstr "Чтение %s..." + +#: lib/blame.tcl:473 +msgid "Loading copy/move tracking annotations..." +msgstr "Загрузка аннотации копирований/переименований..." + +#: lib/blame.tcl:493 +msgid "lines annotated" +msgstr "Ñтрок прокомментировано" + +#: lib/blame.tcl:674 +msgid "Loading original location annotations..." +msgstr "Загрузка аннотаций первоначального Ð¿Ð¾Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð¾Ð±ÑŠÐµÐºÑ‚Ð°..." + +#: lib/blame.tcl:677 +msgid "Annotation complete." +msgstr "ÐÐ½Ð½Ð¾Ñ‚Ð°Ñ†Ð¸Ñ Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð°." + +#: lib/blame.tcl:731 +msgid "Loading annotation..." +msgstr "Загрузка аннотации..." + +#: lib/blame.tcl:787 +msgid "Author:" +msgstr "Ðвтор:" + +#: lib/blame.tcl:791 +msgid "Committer:" +msgstr "Сохранил:" + +#: lib/blame.tcl:796 +msgid "Original File:" +msgstr "ИÑходный файл:" + +#: lib/blame.tcl:910 +msgid "Originally By:" +msgstr "ИÑточник:" + +#: lib/blame.tcl:916 +msgid "In File:" +msgstr "Файл:" + +#: lib/blame.tcl:921 +msgid "Copied Or Moved Here By:" +msgstr "Скопировано/перемещено в:" + +#: lib/branch_checkout.tcl:14 lib/branch_checkout.tcl:19 +msgid "Checkout Branch" +msgstr "Перейти на ветвь" + +#: lib/branch_checkout.tcl:23 +msgid "Checkout" +msgstr "Перейти" + +#: lib/branch_checkout.tcl:27 lib/branch_create.tcl:35 +#: lib/branch_delete.tcl:32 lib/branch_rename.tcl:30 lib/browser.tcl:281 +#: lib/checkout_op.tcl:522 lib/choose_font.tcl:43 lib/merge.tcl:172 +#: lib/option.tcl:90 lib/remote_branch_delete.tcl:42 lib/transport.tcl:97 +msgid "Cancel" +msgstr "Отменить" + +#: lib/branch_checkout.tcl:32 lib/browser.tcl:286 +msgid "Revision" +msgstr "ВерÑиÑ" + +#: lib/branch_checkout.tcl:36 lib/branch_create.tcl:69 lib/option.tcl:202 +msgid "Options" +msgstr "ÐаÑтройки" + +#: lib/branch_checkout.tcl:39 lib/branch_create.tcl:92 +msgid "Fetch Tracking Branch" +msgstr "Получить Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð¸Ð· внешней ветви" + +#: lib/branch_checkout.tcl:44 +msgid "Detach From Local Branch" +msgstr "ОтÑоединить от локальной ветви" + +#: lib/branch_create.tcl:22 +msgid "Create Branch" +msgstr "Создание ветви" + +#: lib/branch_create.tcl:27 +msgid "Create New Branch" +msgstr "Создать новую ветвь" + +#: lib/branch_create.tcl:31 lib/choose_repository.tcl:375 +msgid "Create" +msgstr "Создать" + +#: lib/branch_create.tcl:40 +msgid "Branch Name" +msgstr "Ðазвание ветви" + +#: lib/branch_create.tcl:43 +msgid "Name:" +msgstr "Ðазвание:" + +#: lib/branch_create.tcl:58 +msgid "Match Tracking Branch Name" +msgstr "ВзÑÑ‚ÑŒ из имен ветвей ÑлежениÑ" + +#: lib/branch_create.tcl:66 +msgid "Starting Revision" +msgstr "ÐÐ°Ñ‡Ð°Ð»ÑŒÐ½Ð°Ñ Ð²ÐµÑ€ÑиÑ" + +#: lib/branch_create.tcl:72 +msgid "Update Existing Branch:" +msgstr "Обновить имеющуюÑÑ Ð²ÐµÑ‚Ð²ÑŒ:" + +#: lib/branch_create.tcl:75 +msgid "No" +msgstr "Ðет" + +#: lib/branch_create.tcl:80 +msgid "Fast Forward Only" +msgstr "Только Fast Forward" + +#: lib/branch_create.tcl:85 lib/checkout_op.tcl:514 +msgid "Reset" +msgstr "СброÑ" + +#: lib/branch_create.tcl:97 +msgid "Checkout After Creation" +msgstr "ПоÑле ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ñделать текущей" + +#: lib/branch_create.tcl:131 +msgid "Please select a tracking branch." +msgstr "Укажите ветвь ÑлежениÑ." + +#: lib/branch_create.tcl:140 +#, tcl-format +msgid "Tracking branch %s is not a branch in the remote repository." +msgstr "Ветвь ÑÐ»ÐµÐ¶ÐµÐ½Ð¸Ñ %s не ÑвлÑетÑÑ Ð²ÐµÑ‚Ð²ÑŒÑŽ во внешнем репозитории." + +#: lib/branch_create.tcl:153 lib/branch_rename.tcl:86 +msgid "Please supply a branch name." +msgstr "Укажите название ветви." + +#: lib/branch_create.tcl:164 lib/branch_rename.tcl:106 +#, tcl-format +msgid "'%s' is not an acceptable branch name." +msgstr "ÐедопуÑтимое название ветви '%s'." + +#: lib/branch_delete.tcl:15 +msgid "Delete Branch" +msgstr "Удаление ветви" + +#: lib/branch_delete.tcl:20 +msgid "Delete Local Branch" +msgstr "Удалить локальную ветвь" + +#: lib/branch_delete.tcl:37 +msgid "Local Branches" +msgstr "Локальные ветви" + +#: lib/branch_delete.tcl:52 +msgid "Delete Only If Merged Into" +msgstr "Удалить только в Ñлучае, еÑли было объединение Ñ" + +#: lib/branch_delete.tcl:54 +msgid "Always (Do not perform merge test.)" +msgstr "Ð’Ñегда (не выполнÑÑ‚ÑŒ проверку на объединение)" + +#: lib/branch_delete.tcl:103 +#, tcl-format +msgid "The following branches are not completely merged into %s:" +msgstr "Следующие ветви объединены Ñ %s не полноÑтью:" + +#: lib/branch_delete.tcl:115 +msgid "" +"Recovering deleted branches is difficult. \n" +"\n" +" Delete the selected branches?" +msgstr "" +"ВоÑÑтанавливать удаленные ветви Ñложно. \n" +"\n" +" Удалить выбранные ветви?" + +#: lib/branch_delete.tcl:141 +#, tcl-format +msgid "" +"Failed to delete branches:\n" +"%s" +msgstr "" +"Ðе удалоÑÑŒ удалить ветви:\n" +"%s" + +#: lib/branch_rename.tcl:14 lib/branch_rename.tcl:22 +msgid "Rename Branch" +msgstr "Переименование ветви" + +#: lib/branch_rename.tcl:26 +msgid "Rename" +msgstr "Переименовать" + +#: lib/branch_rename.tcl:36 +msgid "Branch:" +msgstr "Ветвь:" + +#: lib/branch_rename.tcl:39 +msgid "New Name:" +msgstr "Ðовое название:" + +#: lib/branch_rename.tcl:75 +msgid "Please select a branch to rename." +msgstr "Укажите ветвь Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ¸Ð¼ÐµÐ½Ð¾Ð²Ð°Ð½Ð¸Ñ." + +#: lib/branch_rename.tcl:96 lib/checkout_op.tcl:179 +#, tcl-format +msgid "Branch '%s' already exists." +msgstr "Ветвь '%s' уже ÑущеÑтвует." + +#: lib/branch_rename.tcl:117 +#, tcl-format +msgid "Failed to rename '%s'." +msgstr "Ðе удалоÑÑŒ переименовать '%s'. " + +#: lib/browser.tcl:17 +msgid "Starting..." +msgstr "ЗапуÑк..." + +#: lib/browser.tcl:26 +msgid "File Browser" +msgstr "ПроÑмотр ÑпиÑка файлов" + +#: lib/browser.tcl:125 lib/browser.tcl:142 +#, tcl-format +msgid "Loading %s..." +msgstr "Загрузка %s..." + +#: lib/browser.tcl:186 +msgid "[Up To Parent]" +msgstr "[Ðа уровень выше]" + +#: lib/browser.tcl:266 lib/browser.tcl:272 +msgid "Browse Branch Files" +msgstr "Показать файлы ветви" + +#: lib/browser.tcl:277 lib/choose_repository.tcl:391 +#: lib/choose_repository.tcl:482 lib/choose_repository.tcl:492 +#: lib/choose_repository.tcl:989 +msgid "Browse" +msgstr "Показать" + +#: lib/checkout_op.tcl:79 +#, tcl-format +msgid "Fetching %s from %s" +msgstr "Получение %s из %s " + +#: lib/checkout_op.tcl:127 +#, tcl-format +msgid "fatal: Cannot resolve %s" +msgstr "критичеÑÐºÐ°Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ°: невозможно разрешить %s" + +#: lib/checkout_op.tcl:140 lib/console.tcl:79 lib/database.tcl:31 +msgid "Close" +msgstr "Закрыть" + +#: lib/checkout_op.tcl:169 +#, tcl-format +msgid "Branch '%s' does not exist." +msgstr "Ветвь '%s' не ÑущеÑтвует " + +#: lib/checkout_op.tcl:206 +#, tcl-format +msgid "" +"Branch '%s' already exists.\n" +"\n" +"It cannot fast-forward to %s.\n" +"A merge is required." +msgstr "" +"Ветвь '%s' уже ÑущеÑтвует.\n" +"\n" +"Она не может быть прокручена(fast-forward) к %s.\n" +"ТребуетÑÑ Ð¾Ð±ÑŠÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ðµ." + +#: lib/checkout_op.tcl:220 +#, tcl-format +msgid "Merge strategy '%s' not supported." +msgstr "Ð¡Ñ‚Ñ€Ð°Ñ‚ÐµÐ³Ð¸Ñ Ð¾Ð±ÑŠÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ '%s' не поддерживаетÑÑ." + +#: lib/checkout_op.tcl:239 +#, tcl-format +msgid "Failed to update '%s'." +msgstr "Ðе удалоÑÑŒ обновить '%s'." + +#: lib/checkout_op.tcl:251 +msgid "Staging area (index) is already locked." +msgstr "Ð Ð°Ð±Ð¾Ñ‡Ð°Ñ Ð¾Ð±Ð»Ð°ÑÑ‚ÑŒ заблокирована другим процеÑÑом." + +#: lib/checkout_op.tcl:266 +msgid "" +"Last scanned state does not match repository state.\n" +"\n" +"Another Git program has modified this repository since the last scan. A " +"rescan must be performed before the current branch can be changed.\n" +"\n" +"The rescan will be automatically started now.\n" +msgstr "" +"ПоÑледнее прочитанное ÑоÑтоÑние Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ð¸Ñ Ð½Ðµ ÑоответÑтвует текущему.\n" +"\n" +"С момента поÑледней проверки репозиторий был изменен другой программой Git. " +"Ðеобходимо перечитать репозиторий, прежде чем изменÑÑ‚ÑŒ текущую ветвь.\n" +"\n" +"Ðто будет Ñделано ÑÐµÐ¹Ñ‡Ð°Ñ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡ÐµÑки.\n" + +#: lib/checkout_op.tcl:322 +#, tcl-format +msgid "Updating working directory to '%s'..." +msgstr "Обновление рабочего каталога из '%s'..." + +#: lib/checkout_op.tcl:353 +#, tcl-format +msgid "Aborted checkout of '%s' (file level merging is required)." +msgstr "Прерван переход на '%s' (требуетÑÑ Ð¾Ð±ÑŠÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ðµ на уровне файлов)" + +#: lib/checkout_op.tcl:354 +msgid "File level merge required." +msgstr "ТребуетÑÑ Ð¾Ð±ÑŠÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ðµ на уровне файлов." + +#: lib/checkout_op.tcl:358 +#, tcl-format +msgid "Staying on branch '%s'." +msgstr "Ветвь '%s' оÑтаетÑÑ Ñ‚ÐµÐºÑƒÑ‰ÐµÐ¹." + +#: lib/checkout_op.tcl:429 +msgid "" +"You are no longer on a local branch.\n" +"\n" +"If you wanted to be on a branch, create one now starting from 'This Detached " +"Checkout'." +msgstr "" +"Ð’Ñ‹ находитеÑÑŒ не в локальной ветви.\n" +"\n" +"ЕÑли вы хотите Ñнова вернутьÑÑ Ðº какой-нибудь ветви, Ñоздайте ее ÑейчаÑ, " +"Ð½Ð°Ñ‡Ð¸Ð½Ð°Ñ Ñ 'Текущего отÑоединенного ÑоÑтоÑниÑ'." + +#: lib/checkout_op.tcl:446 +#, tcl-format +msgid "Checked out '%s'." +msgstr "Ветвь '%s' Ñделана текущей." + +#: lib/checkout_op.tcl:478 +#, tcl-format +msgid "Resetting '%s' to '%s' will lose the following commits:" +msgstr "Ð¡Ð±Ñ€Ð¾Ñ '%s' в '%s' приведет к потере Ñледующих Ñохраненных ÑоÑтоÑний: " + +#: lib/checkout_op.tcl:500 +msgid "Recovering lost commits may not be easy." +msgstr "ВоÑÑтановить потерÑнные Ñохраненные ÑоÑтоÑÐ½Ð¸Ñ Ð±ÑƒÐ´ÐµÑ‚ Ñложно." + +#: lib/checkout_op.tcl:505 +#, tcl-format +msgid "Reset '%s'?" +msgstr "СброÑить '%s'?" + +#: lib/checkout_op.tcl:510 lib/merge.tcl:164 +msgid "Visualize" +msgstr "ÐаглÑдно" + +#: lib/checkout_op.tcl:578 +#, tcl-format +msgid "" +"Failed to set current branch.\n" +"\n" +"This working directory is only partially switched. We successfully updated " +"your files, but failed to update an internal Git file.\n" +"\n" +"This should not have occurred. %s will now close and give up." +msgstr "" +"Ðе удалоÑÑŒ уÑтановить текущую ветвь.\n" +"\n" +"Ваш рабочий каталог обновлен только чаÑтично. Были обновлены вÑе файлы кроме " +"Ñлужебных файлов Git. \n" +"\n" +"Ðтого не должно было произойти. %s завершаетÑÑ." + +#: lib/choose_font.tcl:39 +msgid "Select" +msgstr "Выбрать" + +#: lib/choose_font.tcl:53 +msgid "Font Family" +msgstr "Шрифт" + +#: lib/choose_font.tcl:73 +msgid "Font Size" +msgstr "Размер шрифта" + +#: lib/choose_font.tcl:90 +msgid "Font Example" +msgstr "Пример текÑта" + +#: lib/choose_font.tcl:101 +msgid "" +"This is example text.\n" +"If you like this text, it can be your font." +msgstr "" +"Ðто пример текÑта.\n" +"ЕÑли Вам нравитÑÑ Ñтот текÑÑ‚, Ñто может быть Ваш шрифт." + +#: lib/choose_repository.tcl:27 +msgid "Git Gui" +msgstr "" + +#: lib/choose_repository.tcl:80 lib/choose_repository.tcl:380 +msgid "Create New Repository" +msgstr "Создать новый репозиторий" + +#: lib/choose_repository.tcl:86 +msgid "New..." +msgstr "Ðовый..." + +#: lib/choose_repository.tcl:93 lib/choose_repository.tcl:468 +msgid "Clone Existing Repository" +msgstr "Склонировать ÑущеÑтвующий репозиторий" + +#: lib/choose_repository.tcl:99 +msgid "Clone..." +msgstr "Склонировать..." + +#: lib/choose_repository.tcl:106 lib/choose_repository.tcl:978 +msgid "Open Existing Repository" +msgstr "Выбрать ÑущеÑтвующий репозиторий" + +#: lib/choose_repository.tcl:112 +msgid "Open..." +msgstr "Открыть..." + +#: lib/choose_repository.tcl:125 +msgid "Recent Repositories" +msgstr "Ðедавние репозитории" + +#: lib/choose_repository.tcl:131 +msgid "Open Recent Repository:" +msgstr "Открыть поÑледний репозиторий" + +#: lib/choose_repository.tcl:294 +#, tcl-format +msgid "Location %s already exists." +msgstr "Путь '%s' уже ÑущеÑтвует." + +#: lib/choose_repository.tcl:300 lib/choose_repository.tcl:307 +#: lib/choose_repository.tcl:314 +#, tcl-format +msgid "Failed to create repository %s:" +msgstr "Ðе удалоÑÑŒ Ñоздать репозиторий %s:" + +#: lib/choose_repository.tcl:385 lib/choose_repository.tcl:486 +msgid "Directory:" +msgstr "Каталог:" + +#: lib/choose_repository.tcl:415 lib/choose_repository.tcl:544 +#: lib/choose_repository.tcl:1013 +msgid "Git Repository" +msgstr "Репозиторий" + +#: lib/choose_repository.tcl:430 lib/choose_repository.tcl:437 +#, tcl-format +msgid "Directory %s already exists." +msgstr "Каталог '%s' уже ÑущеÑтвует." + +#: lib/choose_repository.tcl:442 +#, tcl-format +msgid "File %s already exists." +msgstr "Файл '%s' уже ÑущеÑтвует." + +#: lib/choose_repository.tcl:463 +msgid "Clone" +msgstr "Склонировать" + +#: lib/choose_repository.tcl:476 +msgid "URL:" +msgstr "СÑылка:" + +#: lib/choose_repository.tcl:496 +msgid "Clone Type:" +msgstr "Тип клона:" + +#: lib/choose_repository.tcl:502 +msgid "Standard (Fast, Semi-Redundant, Hardlinks)" +msgstr "Стандартный (БыÑтрый, полуизбыточный, \"жеÑткие\" ÑÑылки)" + +#: lib/choose_repository.tcl:508 +msgid "Full Copy (Slower, Redundant Backup)" +msgstr "ÐŸÐ¾Ð»Ð½Ð°Ñ ÐºÐ¾Ð¿Ð¸Ñ (Медленный, Ñоздает резервную копию)" + +#: lib/choose_repository.tcl:514 +msgid "Shared (Fastest, Not Recommended, No Backup)" +msgstr "Общий (Самый быÑтрый, не рекомендуетÑÑ, без резервной копии)" + +#: lib/choose_repository.tcl:550 lib/choose_repository.tcl:597 +#: lib/choose_repository.tcl:738 lib/choose_repository.tcl:808 +#: lib/choose_repository.tcl:1019 lib/choose_repository.tcl:1027 +#, tcl-format +msgid "Not a Git repository: %s" +msgstr "Каталог не ÑвлÑетÑÑ Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ð¸ÐµÐ¼: %s" + +#: lib/choose_repository.tcl:586 +msgid "Standard only available for local repository." +msgstr "Стандартный клон возможен только Ð´Ð»Ñ Ð»Ð¾ÐºÐ°Ð»ÑŒÐ½Ð¾Ð³Ð¾ репозиториÑ." + +#: lib/choose_repository.tcl:590 +msgid "Shared only available for local repository." +msgstr "Общий клон возможен только Ð´Ð»Ñ Ð»Ð¾ÐºÐ°Ð»ÑŒÐ½Ð¾Ð³Ð¾ репозиториÑ." + +#: lib/choose_repository.tcl:617 +msgid "Failed to configure origin" +msgstr "Ðе могу Ñконфигурировать иÑходный репозиторий." + +#: lib/choose_repository.tcl:629 +msgid "Counting objects" +msgstr "Считаю объекты" + +#: lib/choose_repository.tcl:630 +msgid "buckets" +msgstr "" + +#: lib/choose_repository.tcl:654 +#, tcl-format +msgid "Unable to copy objects/info/alternates: %s" +msgstr "Ðе могу Ñкопировать objects/info/alternates: %s" + +#: lib/choose_repository.tcl:690 +#, tcl-format +msgid "Nothing to clone from %s." +msgstr "Ðечего клонировать Ñ %s." + +#: lib/choose_repository.tcl:692 lib/choose_repository.tcl:906 +#: lib/choose_repository.tcl:918 +msgid "The 'master' branch has not been initialized." +msgstr "Ðе инициализирована ветвь 'master'." + +#: lib/choose_repository.tcl:705 +msgid "Hardlinks are unavailable. Falling back to copying." +msgstr "\"ЖеÑткие ÑÑылки\" не доÑтупны. Буду иÑпользовать копирование." + +#: lib/choose_repository.tcl:717 +#, tcl-format +msgid "Cloning from %s" +msgstr "Клонирование %s" + +#: lib/choose_repository.tcl:748 +msgid "Copying objects" +msgstr "Копирование objects" + +#: lib/choose_repository.tcl:749 +msgid "KiB" +msgstr "КБ" + +#: lib/choose_repository.tcl:773 +#, tcl-format +msgid "Unable to copy object: %s" +msgstr "Ðе могу Ñкопировать объект: %s" + +#: lib/choose_repository.tcl:783 +msgid "Linking objects" +msgstr "Создание ÑÑылок на objects" + +#: lib/choose_repository.tcl:784 +msgid "objects" +msgstr "объекты" + +#: lib/choose_repository.tcl:792 +#, tcl-format +msgid "Unable to hardlink object: %s" +msgstr "Ðе могу \"жеÑтко ÑвÑзать\" объект: %s" + +#: lib/choose_repository.tcl:847 +msgid "Cannot fetch branches and objects. See console output for details." +msgstr "" +"Ðе могу получить ветви и объекты. Ð”Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð°Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð½Ð° конÑоли." + +#: lib/choose_repository.tcl:858 +msgid "Cannot fetch tags. See console output for details." +msgstr "Ðе могу получить метки. Ð”Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð°Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð½Ð° конÑоли." + +#: lib/choose_repository.tcl:882 +msgid "Cannot determine HEAD. See console output for details." +msgstr "Ðе могу определить HEAD. Ð”Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð°Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð½Ð° конÑоли." + +#: lib/choose_repository.tcl:891 +#, tcl-format +msgid "Unable to cleanup %s" +msgstr "Ðе могу очиÑтить %s" + +#: lib/choose_repository.tcl:897 +msgid "Clone failed." +msgstr "Клонирование не удалоÑÑŒ." + +#: lib/choose_repository.tcl:904 +msgid "No default branch obtained." +msgstr "Ðе было получено ветви по умолчанию." + +#: lib/choose_repository.tcl:915 +#, tcl-format +msgid "Cannot resolve %s as a commit." +msgstr "Ðе могу раÑпознать %s как ÑоÑтоÑние." + +#: lib/choose_repository.tcl:927 +msgid "Creating working directory" +msgstr "Создаю рабочий каталог" + +#: lib/choose_repository.tcl:928 lib/index.tcl:65 lib/index.tcl:127 +#: lib/index.tcl:193 +msgid "files" +msgstr "файлов" + +#: lib/choose_repository.tcl:957 +msgid "Initial file checkout failed." +msgstr "Ðе удалоÑÑŒ получить начальное ÑоÑтоÑние файлов репозиториÑ." + +#: lib/choose_repository.tcl:973 +msgid "Open" +msgstr "Открыть" + +#: lib/choose_repository.tcl:983 +msgid "Repository:" +msgstr "Репозиторий:" + +#: lib/choose_repository.tcl:1033 +#, tcl-format +msgid "Failed to open repository %s:" +msgstr "Ðе удалоÑÑŒ открыть репозиторий %s:" + +#: lib/choose_rev.tcl:53 +msgid "This Detached Checkout" +msgstr "Текущее отÑоединенное ÑоÑтоÑние" + +#: lib/choose_rev.tcl:60 +msgid "Revision Expression:" +msgstr "Выражение Ð´Ð»Ñ Ð¾Ð¿Ñ€ÐµÐ´ÐµÐ»ÐµÐ½Ð¸Ñ Ð²ÐµÑ€Ñии:" + +#: lib/choose_rev.tcl:74 +msgid "Local Branch" +msgstr "Ð›Ð¾ÐºÐ°Ð»ÑŒÐ½Ð°Ñ Ð²ÐµÑ‚Ð²ÑŒ:" + +#: lib/choose_rev.tcl:79 +msgid "Tracking Branch" +msgstr "Ветвь ÑлежениÑ" + +#: lib/choose_rev.tcl:84 lib/choose_rev.tcl:537 +msgid "Tag" +msgstr "Таг" + +#: lib/choose_rev.tcl:317 +#, tcl-format +msgid "Invalid revision: %s" +msgstr "ÐÐµÐ²ÐµÑ€Ð½Ð°Ñ Ð²ÐµÑ€ÑиÑ: %s" + +#: lib/choose_rev.tcl:338 +msgid "No revision selected." +msgstr "ВерÑÐ¸Ñ Ð½Ðµ указана." + +#: lib/choose_rev.tcl:346 +msgid "Revision expression is empty." +msgstr "ПуÑтое выражение Ð´Ð»Ñ Ð¾Ð¿Ñ€ÐµÐ´ÐµÐ»ÐµÐ½Ð¸Ñ Ð²ÐµÑ€Ñии." + +#: lib/choose_rev.tcl:530 +msgid "Updated" +msgstr "Обновлено" + +#: lib/choose_rev.tcl:558 +msgid "URL" +msgstr "СÑылка" + +#: lib/commit.tcl:9 +msgid "" +"There is nothing to amend.\n" +"\n" +"You are about to create the initial commit. There is no commit before this " +"to amend.\n" +msgstr "" +"ОтÑутÑтвует ÑоÑтоÑние Ð´Ð»Ñ Ð¸ÑправлениÑ.\n" +"\n" +"Ð’Ñ‹ Ñоздаете первое ÑоÑтоÑние в репозитории, здеÑÑŒ еще нечего иÑправлÑÑ‚ÑŒ.\n" + +#: lib/commit.tcl:18 +msgid "" +"Cannot amend while merging.\n" +"\n" +"You are currently in the middle of a merge that has not been fully " +"completed. You cannot amend the prior commit unless you first abort the " +"current merge activity.\n" +msgstr "" +"Ðевозможно иÑправить ÑоÑтоÑние во Ð²Ñ€ÐµÐ¼Ñ Ð¾Ð±ÑŠÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ.\n" +"\n" +"Текущее объединение не завершено. Ðевозможно иÑправить предыдущее " +"Ñохраненное ÑоÑтоÑние не Ð¿Ñ€ÐµÑ€Ñ‹Ð²Ð°Ñ Ñ‚ÐµÐºÑƒÑ‰ÐµÐµ объединение.\n" + +#: lib/commit.tcl:49 +msgid "Error loading commit data for amend:" +msgstr "Ошибка при загрузке данных Ð´Ð»Ñ Ð¸ÑÐ¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ñохраненного ÑоÑтоÑниÑ:" + +#: lib/commit.tcl:76 +msgid "Unable to obtain your identity:" +msgstr "Ðевозможно получить информацию об авторÑтве:" + +#: lib/commit.tcl:81 +msgid "Invalid GIT_COMMITTER_IDENT:" +msgstr "Ðеверный GIT_COMMITTER_IDENT:" + +#: lib/commit.tcl:133 +msgid "" +"Last scanned state does not match repository state.\n" +"\n" +"Another Git program has modified this repository since the last scan. A " +"rescan must be performed before another commit can be created.\n" +"\n" +"The rescan will be automatically started now.\n" +msgstr "" +"ПоÑледнее прочитанное ÑоÑтоÑние Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ð¸Ñ Ð½Ðµ ÑоответÑтвует текущему.\n" +"\n" +"С момента поÑледней проверки репозиторий был изменен другой программой Git. " +"Ðеобходимо перечитать репозиторий, прежде чем изменÑÑ‚ÑŒ текущую ветвь. \n" +"\n" +"Ðто будет Ñделано ÑÐµÐ¹Ñ‡Ð°Ñ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡ÐµÑки.\n" + +#: lib/commit.tcl:154 +#, tcl-format +msgid "" +"Unmerged files cannot be committed.\n" +"\n" +"File %s has merge conflicts. You must resolve them and stage the file " +"before committing.\n" +msgstr "" +"ÐÐµÐ»ÑŒÐ·Ñ Ñохранить необъединенные файлы.\n" +"\n" +"Ð”Ð»Ñ Ñ„Ð°Ð¹Ð»Ð° %s возник конфликт объединениÑ. Разрешите конфликт и добавьте к " +"подготовленным файлам перед Ñохранением.\n" + +#: lib/commit.tcl:162 +#, tcl-format +msgid "" +"Unknown file state %s detected.\n" +"\n" +"File %s cannot be committed by this program.\n" +msgstr "" +"Обнаружено неизвеÑтное ÑоÑтоÑние файла %s.\n" +"\n" +"Файл %s не может быть Ñохранен данной программой.\n" + +#: lib/commit.tcl:170 +msgid "" +"No changes to commit.\n" +"\n" +"You must stage at least 1 file before you can commit.\n" +msgstr "" +"ОтÑутÑтвуют Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð´Ð»Ñ ÑохранениÑ.\n" +"\n" +"Подготовьте Ñ…Ð¾Ñ‚Ñ Ð±Ñ‹ один файл до ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ñохраненного ÑоÑтоÑниÑ.\n" + +#: lib/commit.tcl:183 +msgid "" +"Please supply a commit message.\n" +"\n" +"A good commit message has the following format:\n" +"\n" +"- First line: Describe in one sentance what you did.\n" +"- Second line: Blank\n" +"- Remaining lines: Describe why this change is good.\n" +msgstr "" +"Ðапишите комментарий к Ñохраненному ÑоÑтоÑнию.\n" +"\n" +"РекомендуетÑÑ Ñледующий формат комментариÑ:\n" +"\n" +"- Ð¿ÐµÑ€Ð²Ð°Ñ Ñтрока: краткое опиÑание Ñделанных изменений.\n" +"- Ð²Ñ‚Ð¾Ñ€Ð°Ñ Ñтрока пуÑтаÑ\n" +"- оÑтавшиеÑÑ Ñтроки: опишите, что дают ваши изменениÑ.\n" + +#: lib/commit.tcl:257 +msgid "write-tree failed:" +msgstr "Программа write-tree завершилаÑÑŒ Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ¾Ð¹:" + +#: lib/commit.tcl:275 +#, tcl-format +msgid "Commit %s appears to be corrupt" +msgstr "СоÑтоÑние %s выглÑдит поврежденным" + +#: lib/commit.tcl:279 +msgid "" +"No changes to commit.\n" +"\n" +"No files were modified by this commit and it was not a merge commit.\n" +"\n" +"A rescan will be automatically started now.\n" +msgstr "" +"ОтÑутÑтвуют Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð´Ð»Ñ ÑохранениÑ.\n" +"\n" +"Ðи один файл не был изменен и не было объединениÑ.\n" +"\n" +"Ð¡ÐµÐ¹Ñ‡Ð°Ñ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡ÐµÑки запуÑтитÑÑ Ð¿ÐµÑ€ÐµÑ‡Ð¸Ñ‚Ñ‹Ð²Ð°Ð½Ð¸Ðµ репозиториÑ.\n" + +#: lib/commit.tcl:286 +msgid "No changes to commit." +msgstr "ОтуÑтвуют Ð¸Ð·Ð¼ÐµÐ½Ð¸Ñ Ð´Ð»Ñ ÑохранениÑ." + +#: lib/commit.tcl:303 +#, tcl-format +msgid "warning: Tcl does not support encoding '%s'." +msgstr "предупреждение: Tcl не поддерживает кодировку '%s'." + +#: lib/commit.tcl:317 +msgid "commit-tree failed:" +msgstr "Программа commit-tree завершилаÑÑŒ Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ¾Ð¹:" + +#: lib/commit.tcl:339 +msgid "update-ref failed:" +msgstr "Программа update-ref завершилаÑÑŒ Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ¾Ð¹:" + +#: lib/commit.tcl:430 +#, tcl-format +msgid "Created commit %s: %s" +msgstr "Создано ÑоÑтоÑние %s: %s " + +#: lib/console.tcl:57 +msgid "Working... please wait..." +msgstr "Ð’ процеÑÑе... пожалуйÑта, ждите..." + +#: lib/console.tcl:183 +msgid "Success" +msgstr "ПроцеÑÑ ÑƒÑпешно завершен" + +#: lib/console.tcl:196 +msgid "Error: Command Failed" +msgstr "Ошибка: не удалоÑÑŒ выполнить команду" + +#: lib/database.tcl:43 +msgid "Number of loose objects" +msgstr "КоличеÑтво неÑвÑзанных объектов" + +#: lib/database.tcl:44 +msgid "Disk space used by loose objects" +msgstr "Объем диÑкового проÑтранÑтва, занÑтый неÑвÑзанными объектами" + +#: lib/database.tcl:45 +msgid "Number of packed objects" +msgstr "КоличеÑтво упакованных объектов" + +#: lib/database.tcl:46 +msgid "Number of packs" +msgstr "КоличеÑтво pack-файлов" + +#: lib/database.tcl:47 +msgid "Disk space used by packed objects" +msgstr "Объем диÑкового проÑтранÑтва, занÑтый упакованными объектами" + +#: lib/database.tcl:48 +msgid "Packed objects waiting for pruning" +msgstr "ÐеÑвÑзанные объекты, которые можно удалить" + +#: lib/database.tcl:49 +msgid "Garbage files" +msgstr "МуÑор" + +#: lib/database.tcl:72 +msgid "Compressing the object database" +msgstr "Сжатие базы объектов" + +#: lib/database.tcl:83 +msgid "Verifying the object database with fsck-objects" +msgstr "Проверка базы объектов при помощи fsck" + +#: lib/database.tcl:108 +#, tcl-format +msgid "" +"This repository currently has approximately %i loose objects.\n" +"\n" +"To maintain optimal performance it is strongly recommended that you compress " +"the database when more than %i loose objects exist.\n" +"\n" +"Compress the database now?" +msgstr "" +"Ðтот репозиторий ÑÐµÐ¹Ñ‡Ð°Ñ Ñодержит примерно %i Ñвободных объектов\n" +"\n" +"Ð”Ð»Ñ Ð»ÑƒÑ‡ÑˆÐµÐ¹ производительноÑти рекомендуетÑÑ Ñжать базу данных, когда еÑÑ‚ÑŒ " +"более %i неÑвÑзанных объектов.\n" +"\n" +"Сжать базу данных ÑейчаÑ?" + +#: lib/date.tcl:25 +#, tcl-format +msgid "Invalid date from Git: %s" +msgstr "ÐÐµÐ¿Ñ€Ð°Ð²Ð¸Ð»ÑŒÐ½Ð°Ñ Ð´Ð°Ñ‚Ð° в репозитории: %s" + +#: lib/diff.tcl:42 +#, tcl-format +msgid "" +"No differences detected.\n" +"\n" +"%s has no changes.\n" +"\n" +"The modification date of this file was updated by another application, but " +"the content within the file was not changed.\n" +"\n" +"A rescan will be automatically started to find other files which may have " +"the same state." +msgstr "" +"Изменений не обнаружено.\n" +"\n" +"в %s отутÑтвуют изменениÑ.\n" +"\n" +"Дата Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ñ„Ð°Ð¹Ð»Ð° была обновлена другой программой, но Ñодержимое файла " +"оÑталоÑÑŒ прежним.\n" +"\n" +"Ð¡ÐµÐ¹Ñ‡Ð°Ñ Ð±ÑƒÐ´ÐµÑ‚ запущено перечитывание репозиториÑ, чтобы найти подобные файлы." + +#: lib/diff.tcl:81 +#, tcl-format +msgid "Loading diff of %s..." +msgstr "Загрузка изменений в %s..." + +#: lib/diff.tcl:114 lib/diff.tcl:184 +#, tcl-format +msgid "Unable to display %s" +msgstr "Ðе могу показать %s" + +#: lib/diff.tcl:115 +msgid "Error loading file:" +msgstr "Ошибка загрузки файла:" + +#: lib/diff.tcl:122 +msgid "Git Repository (subproject)" +msgstr "Репозиторий Git (подпроект)" + +#: lib/diff.tcl:134 +msgid "* Binary file (not showing content)." +msgstr "* Двоичный файл (Ñодержимое не показано)" + +#: lib/diff.tcl:185 +msgid "Error loading diff:" +msgstr "Ошибка загрузки diff:" + +#: lib/diff.tcl:302 +msgid "Failed to unstage selected hunk." +msgstr "Ðе удалоÑÑŒ иÑключить выбранную чаÑÑ‚ÑŒ." + +#: lib/diff.tcl:309 +msgid "Failed to stage selected hunk." +msgstr "Ðе удалоÑÑŒ подготовить к Ñохранению выбранную чаÑÑ‚ÑŒ." + +#: lib/error.tcl:12 lib/error.tcl:102 +msgid "error" +msgstr "ошибка" + +#: lib/error.tcl:28 +msgid "warning" +msgstr "предупреждение" + +#: lib/error.tcl:81 +msgid "You must correct the above errors before committing." +msgstr "Прежде чем Ñохранить, иÑправьте вышеуказанные ошибки." + +#: lib/index.tcl:6 +msgid "Unable to unlock the index." +msgstr "Ðе удалоÑÑŒ разблокировать индекÑ" + +#: lib/index.tcl:15 +msgid "Index Error" +msgstr "Ошибка индекÑа" + +#: lib/index.tcl:21 +msgid "" +"Updating the Git index failed. A rescan will be automatically started to " +"resynchronize git-gui." +msgstr "" +"Ðе удалоÑÑŒ обновить Ð¸Ð½Ð´ÐµÐºÑ Git. СоÑтоÑние Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ð¸Ñ Ð±ÑƒÐ´ÐµÑ‚" +"перечитано автоматичеÑки." + +#: lib/index.tcl:27 +msgid "Continue" +msgstr "Продолжить" + +#: lib/index.tcl:31 +msgid "Unlock Index" +msgstr "Разблокировать индекÑ" + +#: lib/index.tcl:282 +#, tcl-format +msgid "Unstaging %s from commit" +msgstr "Удаление %s из подготовленного" + +#: lib/index.tcl:326 +#, tcl-format +msgid "Adding %s" +msgstr "Добавление %s..." + +#: lib/index.tcl:381 +#, tcl-format +msgid "Revert changes in file %s?" +msgstr "Отменить Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð² файле %s?" + +#: lib/index.tcl:383 +#, tcl-format +msgid "Revert changes in these %i files?" +msgstr "Отменить Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð² %i файле(-ах)?" + +#: lib/index.tcl:389 +msgid "Any unstaged changes will be permanently lost by the revert." +msgstr "" +"Любые изменениÑ, не подготовленные к Ñохранению, будут потерÑны при данной " +"операции." + +#: lib/index.tcl:392 +msgid "Do Nothing" +msgstr "Ðичего не делать" + +#: lib/merge.tcl:13 +msgid "" +"Cannot merge while amending.\n" +"\n" +"You must finish amending this commit before starting any type of merge.\n" +msgstr "" +"Ðевозможно выполнить объединение во Ð²Ñ€ÐµÐ¼Ñ Ð¸ÑправлениÑ.\n" +"\n" +"Завершите иÑправление данного ÑоÑтоÑÐ½Ð¸Ñ Ð¿ÐµÑ€ÐµÐ´ выполнением операции " +"объединениÑ.\n" + +#: lib/merge.tcl:27 +msgid "" +"Last scanned state does not match repository state.\n" +"\n" +"Another Git program has modified this repository since the last scan. A " +"rescan must be performed before a merge can be performed.\n" +"\n" +"The rescan will be automatically started now.\n" +msgstr "" +"ПоÑледнее прочитанное ÑоÑтоÑние Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ð¸Ñ Ð½Ðµ ÑоответÑтвует текущему.\n" +"\n" +"С момента поÑледней проверки репозиторий был изменен другой программой Git. " +"Ðеобходимо перечитать репозиторий, прежде чем изменÑÑ‚ÑŒ текущую ветвь.\n" +"\n" +"Ðто будет Ñделано ÑÐµÐ¹Ñ‡Ð°Ñ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡ÐµÑки.\n" + +#: lib/merge.tcl:44 +#, tcl-format +msgid "" +"You are in the middle of a conflicted merge.\n" +"\n" +"File %s has merge conflicts.\n" +"\n" +"You must resolve them, stage the file, and commit to complete the current " +"merge. Only then can you begin another merge.\n" +msgstr "" +"Предыдущее объединение не завершено из-за конфликта.\n" +"\n" +"Ð”Ð»Ñ Ñ„Ð°Ð¹Ð»Ð° %s возник конфликт объединениÑ.\n" +"\n" +"Разрешите конфликт, подготовьте файл и Ñохраните. Только поÑле Ñтого можно " +"начать Ñледующее объединение.\n" + +#: lib/merge.tcl:54 +#, tcl-format +msgid "" +"You are in the middle of a change.\n" +"\n" +"File %s is modified.\n" +"\n" +"You should complete the current commit before starting a merge. Doing so " +"will help you abort a failed merge, should the need arise.\n" +msgstr "" +"Ð˜Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð½Ðµ Ñохранены.\n" +"\n" +"Файл %s изменен.\n" +"\n" +"Подготовьте и Ñохраните Ð¸Ð·Ð¼ÐµÐ½Ð¸Ñ Ð¿ÐµÑ€ÐµÐ´ началом объединениÑ. Ð’ Ñлучае " +"необходимоÑти Ñто позволит прервать операцию объединениÑ.\n" + +#: lib/merge.tcl:106 +#, tcl-format +msgid "%s of %s" +msgstr "%s из %s" + +#: lib/merge.tcl:119 +#, tcl-format +msgid "Merging %s and %s" +msgstr "Объединение %s и %s" + +#: lib/merge.tcl:131 +msgid "Merge completed successfully." +msgstr "Объединение уÑпешно завершено." + +#: lib/merge.tcl:133 +msgid "Merge failed. Conflict resolution is required." +msgstr "Ðе удалоÑÑŒ завершить объединение. ТребуетÑÑ Ñ€Ð°Ð·Ñ€ÐµÑˆÐµÐ½Ð¸Ðµ конфликта." + +#: lib/merge.tcl:158 +#, tcl-format +msgid "Merge Into %s" +msgstr "Объединить Ñ %s" + +#: lib/merge.tcl:177 +msgid "Revision To Merge" +msgstr "ВерÑÐ¸Ñ Ð´Ð»Ñ Ð¾Ð±ÑŠÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ" + +#: lib/merge.tcl:212 +msgid "" +"Cannot abort while amending.\n" +"\n" +"You must finish amending this commit.\n" +msgstr "" +"Ðевозможно прервать иÑправление.\n" +"\n" +"Завершите текущее иÑправление Ñохраненного ÑоÑтоÑниÑ.\n" + +#: lib/merge.tcl:222 +msgid "" +"Abort merge?\n" +"\n" +"Aborting the current merge will cause *ALL* uncommitted changes to be lost.\n" +"\n" +"Continue with aborting the current merge?" +msgstr "" +"Прервать объединение?\n" +"\n" +"Прерывание Ð¾Ð±ÑŠÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð¸Ð²ÐµÐ´ÐµÑ‚ к потере *ВСЕХ* неÑохраненных изменений.\n" +"\n" +"Продолжить?" + +#: lib/merge.tcl:228 +msgid "" +"Reset changes?\n" +"\n" +"Resetting the changes will cause *ALL* uncommitted changes to be lost.\n" +"\n" +"Continue with resetting the current changes?" +msgstr "" +"Прервать объединение?\n" +"\n" +"Прерывание Ð¾Ð±ÑŠÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð¸Ð²ÐµÐ´ÐµÑ‚ к потере *ВСЕХ* неÑохраненных изменений.\n" +"\n" +"Продолжить?" + +#: lib/merge.tcl:239 +msgid "Aborting" +msgstr "Прерываю" + +#: lib/merge.tcl:266 +msgid "Abort failed." +msgstr "Прервать не удалоÑÑŒ." + +#: lib/merge.tcl:268 +msgid "Abort completed. Ready." +msgstr "Прервано." + +#: lib/option.tcl:82 +msgid "Restore Defaults" +msgstr "ВоÑÑтановить наÑтройки по умолчанию" + +#: lib/option.tcl:86 +msgid "Save" +msgstr "Сохранить" + +#: lib/option.tcl:96 +#, tcl-format +msgid "%s Repository" +msgstr "Ð´Ð»Ñ Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ð¸Ñ %s" + +#: lib/option.tcl:97 +msgid "Global (All Repositories)" +msgstr "Общие (Ð´Ð»Ñ Ð²Ñех репозиториев)" + +#: lib/option.tcl:103 +msgid "User Name" +msgstr "Ð˜Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ" + +#: lib/option.tcl:104 +msgid "Email Address" +msgstr "ÐÐ´ÐµÑ Ñлектронной почты" + +#: lib/option.tcl:106 +msgid "Summarize Merge Commits" +msgstr "Суммарный комментарий при объединении" + +#: lib/option.tcl:107 +msgid "Merge Verbosity" +msgstr "Уровень детальноÑти Ñообщений при объединении" + +#: lib/option.tcl:108 +msgid "Show Diffstat After Merge" +msgstr "Показать отчет об изменениÑÑ… поÑле объединениÑ" + +#: lib/option.tcl:110 +msgid "Trust File Modification Timestamps" +msgstr "ДоверÑÑ‚ÑŒ времени модификации файла" + +#: lib/option.tcl:111 +msgid "Prune Tracking Branches During Fetch" +msgstr "ЧиÑтка ветвей ÑÐ»ÐµÐ¶ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð¸ получении изменений" + +#: lib/option.tcl:112 +msgid "Match Tracking Branches" +msgstr "Ð˜Ð¼Ñ Ð½Ð¾Ð²Ð¾Ð¹ ветви взÑÑ‚ÑŒ из имен ветвей ÑлежениÑ" + +#: lib/option.tcl:113 +msgid "Number of Diff Context Lines" +msgstr "ЧиÑло Ñтрок в контекÑте diff" + +#: lib/option.tcl:114 +msgid "New Branch Name Template" +msgstr "Шаблон Ð´Ð»Ñ Ð¸Ð¼ÐµÐ½Ð¸ новой ветви" + +#: lib/option.tcl:176 +msgid "Change Font" +msgstr "Изменить шрифт" + +#: lib/option.tcl:180 +#, tcl-format +msgid "Choose %s" +msgstr "Выберите %s" + +# carbon copy +#: lib/option.tcl:186 +msgid "pt." +msgstr "" + +#: lib/option.tcl:200 +msgid "Preferences" +msgstr "ÐаÑтройки" + +#: lib/option.tcl:235 +msgid "Failed to completely save options:" +msgstr "Ðе удалоÑÑŒ полноÑтью Ñохранить наÑтройки:" + +#: lib/remote_branch_delete.tcl:29 lib/remote_branch_delete.tcl:34 +msgid "Delete Remote Branch" +msgstr "Удалить внешнюю ветвь" + +#: lib/remote_branch_delete.tcl:47 +msgid "From Repository" +msgstr "Из репозиториÑ" + +#: lib/remote_branch_delete.tcl:50 lib/transport.tcl:123 +msgid "Remote:" +msgstr "внешний:" + +#: lib/remote_branch_delete.tcl:66 lib/transport.tcl:138 +msgid "Arbitrary URL:" +msgstr "по указанному URL:" + +#: lib/remote_branch_delete.tcl:84 +msgid "Branches" +msgstr "Ветви" + +#: lib/remote_branch_delete.tcl:109 +msgid "Delete Only If" +msgstr "Удалить только в Ñлучае, еÑли" + +#: lib/remote_branch_delete.tcl:111 +msgid "Merged Into:" +msgstr "Объединено Ñ:" + +#: lib/remote_branch_delete.tcl:119 +msgid "Always (Do not perform merge checks)" +msgstr "Ð’Ñегда (не выполнÑÑ‚ÑŒ проверку объединений)" + +#: lib/remote_branch_delete.tcl:152 +msgid "A branch is required for 'Merged Into'." +msgstr "Ð”Ð»Ñ Ð¾Ð¿Ñ†Ð¸Ð¸ 'Объединено Ñ' требуетÑÑ ÑƒÐºÐ°Ð·Ð°Ñ‚ÑŒ ветвь." + +#: lib/remote_branch_delete.tcl:184 +#, tcl-format +msgid "" +"The following branches are not completely merged into %s:\n" +"\n" +" - %s" +msgstr "" +"Следующие ветви объединены Ñ %s не полноÑтью:\n" +" - %s" + +#: lib/remote_branch_delete.tcl:189 +#, tcl-format +msgid "" +"One or more of the merge tests failed because you have not fetched the " +"necessary commits. Try fetching from %s first." +msgstr "" +"Один или неÑколько теÑтов на объединение не прошли, потому что Ð’Ñ‹ не " +"получили необходимые ÑоÑтоÑниÑ. ПопытайтеÑÑŒ получить их из %s." + +#: lib/remote_branch_delete.tcl:207 +msgid "Please select one or more branches to delete." +msgstr "Укажите одну или неÑколько ветвей Ð´Ð»Ñ ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ." + +#: lib/remote_branch_delete.tcl:216 +msgid "" +"Recovering deleted branches is difficult.\n" +"\n" +"Delete the selected branches?" +msgstr "" +"ВоÑÑтановить удаленные ветви Ñложно.\n" +"\n" +"Продолжить?" + +#: lib/remote_branch_delete.tcl:226 +#, tcl-format +msgid "Deleting branches from %s" +msgstr "Удаление ветвей из %s" + +#: lib/remote_branch_delete.tcl:286 +msgid "No repository selected." +msgstr "Ðе указан репозиторий." + +#: lib/remote_branch_delete.tcl:291 +#, tcl-format +msgid "Scanning %s..." +msgstr "Перечитывание %s... " + +#: lib/remote.tcl:165 +msgid "Prune from" +msgstr "ЧиÑтка" + +#: lib/remote.tcl:170 +msgid "Fetch from" +msgstr "Получение из" + +#: lib/remote.tcl:213 +msgid "Push to" +msgstr "Отправить" + +#: lib/shortcut.tcl:20 lib/shortcut.tcl:61 +msgid "Cannot write shortcut:" +msgstr "Ðевозможно запиÑать ÑÑылку:" + +#: lib/shortcut.tcl:136 +msgid "Cannot write icon:" +msgstr "Ðевозможно запиÑать значок:" + +#: lib/status_bar.tcl:83 +#, tcl-format +msgid "%s ... %*i of %*i %s (%3i%%)" +msgstr "%s ... %*i из %*i %s (%3i%%)" + +#: lib/transport.tcl:6 +#, tcl-format +msgid "fetch %s" +msgstr "получение %s" + +#: lib/transport.tcl:7 +#, tcl-format +msgid "Fetching new changes from %s" +msgstr "Получение изменений из %s " + +# carbon copy +#: lib/transport.tcl:18 +#, tcl-format +msgid "remote prune %s" +msgstr "чиÑтка внешнего %s" + +#: lib/transport.tcl:19 +#, tcl-format +msgid "Pruning tracking branches deleted from %s" +msgstr "ЧиÑтка ветвей ÑлежениÑ, удаленных из %s" + +#: lib/transport.tcl:25 lib/transport.tcl:71 +#, tcl-format +msgid "push %s" +msgstr "отправить %s" + +#: lib/transport.tcl:26 +#, tcl-format +msgid "Pushing changes to %s" +msgstr "Отправка изменений в %s " + +#: lib/transport.tcl:72 +#, tcl-format +msgid "Pushing %s %s to %s" +msgstr "Отправка %s %s в %s" + +#: lib/transport.tcl:89 +msgid "Push Branches" +msgstr "Отправить Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð² ветвÑÑ…" + +#: lib/transport.tcl:103 +msgid "Source Branches" +msgstr "ИÑходные ветви" + +#: lib/transport.tcl:120 +msgid "Destination Repository" +msgstr "Репозиторий назначениÑ" + +#: lib/transport.tcl:158 +msgid "Transfer Options" +msgstr "ÐаÑтройки отправки" + +#: lib/transport.tcl:160 +msgid "Force overwrite existing branch (may discard changes)" +msgstr "Ðамеренно перепиÑать ÑущеÑтвующую ветвь (возможна Ð¿Ð¾Ñ‚ÐµÑ€Ñ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ð¹)" + +#: lib/transport.tcl:164 +msgid "Use thin pack (for slow network connections)" +msgstr "ИÑпользовать thin pack (Ð´Ð»Ñ Ð¼ÐµÐ´Ð»ÐµÐ½Ð½Ñ‹Ñ… Ñетевых подключений)" + +#: lib/transport.tcl:168 +msgid "Include tags" +msgstr "Передать таги" + +#~ msgid "Next >" +#~ msgstr "Дальше >" diff --git a/git-gui/po/zh_cn.po b/git-gui/po/zh_cn.po new file mode 100644 index 0000000000..621c9479b2 --- /dev/null +++ b/git-gui/po/zh_cn.po @@ -0,0 +1,1769 @@ +# Translation of git-gui to Chinese +# Copyright (C) 2007 Shawn Pearce +# This file is distributed under the same license as the git-gui package. +# Xudong Guan <xudong.guan@gmail.com>, 2007. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: git-gui\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2007-10-10 04:04-0400\n" +"PO-Revision-Date: 2007-07-21 01:23-0700\n" +"Last-Translator: Xudong Guan <xudong.guan@gmail.com>\n" +"Language-Team: Chinese\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: git-gui.sh:41 git-gui.sh:634 git-gui.sh:648 git-gui.sh:661 git-gui.sh:744 +#: git-gui.sh:763 +msgid "git-gui: fatal error" +msgstr "" + +#: git-gui.sh:595 +#, tcl-format +msgid "Invalid font specified in %s:" +msgstr "" + +#: git-gui.sh:620 +msgid "Main Font" +msgstr "" + +#: git-gui.sh:621 +msgid "Diff/Console Font" +msgstr "" + +#: git-gui.sh:635 +msgid "Cannot find git in PATH." +msgstr "" + +#: git-gui.sh:662 +msgid "Cannot parse Git version string:" +msgstr "" + +#: git-gui.sh:680 +#, tcl-format +msgid "" +"Git version cannot be determined.\n" +"\n" +"%s claims it is version '%s'.\n" +"\n" +"%s requires at least Git 1.5.0 or later.\n" +"\n" +"Assume '%s' is version 1.5.0?\n" +msgstr "" + +#: git-gui.sh:853 +msgid "Git directory not found:" +msgstr "" + +#: git-gui.sh:860 +msgid "Cannot move to top of working directory:" +msgstr "" + +#: git-gui.sh:867 +msgid "Cannot use funny .git directory:" +msgstr "" + +#: git-gui.sh:872 +msgid "No working directory" +msgstr "" + +#: git-gui.sh:1019 +msgid "Refreshing file status..." +msgstr "" + +#: git-gui.sh:1084 +msgid "Scanning for modified files ..." +msgstr "" + +#: git-gui.sh:1259 lib/browser.tcl:245 +#, fuzzy +msgid "Ready." +msgstr "é‡åš" + +#: git-gui.sh:1525 +msgid "Unmodified" +msgstr "" + +#: git-gui.sh:1527 +msgid "Modified, not staged" +msgstr "" + +#: git-gui.sh:1528 git-gui.sh:1533 +#, fuzzy +msgid "Staged for commit" +msgstr "从本次æ交移除" + +#: git-gui.sh:1529 git-gui.sh:1534 +#, fuzzy +msgid "Portions staged for commit" +msgstr "从本次æ交移除" + +#: git-gui.sh:1530 git-gui.sh:1535 +msgid "Staged for commit, missing" +msgstr "" + +#: git-gui.sh:1532 +msgid "Untracked, not staged" +msgstr "" + +#: git-gui.sh:1537 +msgid "Missing" +msgstr "" + +#: git-gui.sh:1538 +msgid "Staged for removal" +msgstr "" + +#: git-gui.sh:1539 +msgid "Staged for removal, still present" +msgstr "" + +#: git-gui.sh:1541 git-gui.sh:1542 git-gui.sh:1543 git-gui.sh:1544 +msgid "Requires merge resolution" +msgstr "" + +#: git-gui.sh:1579 +msgid "Starting gitk... please wait..." +msgstr "" + +#: git-gui.sh:1588 +#, tcl-format +msgid "" +"Unable to start gitk:\n" +"\n" +"%s does not exist" +msgstr "" + +#: git-gui.sh:1788 lib/choose_repository.tcl:32 +msgid "Repository" +msgstr "ç‰ˆæœ¬æ ‘" + +#: git-gui.sh:1789 +msgid "Edit" +msgstr "编辑" + +#: git-gui.sh:1791 lib/choose_rev.tcl:560 +msgid "Branch" +msgstr "分支" + +#: git-gui.sh:1794 lib/choose_rev.tcl:547 +#, fuzzy +msgid "Commit@@noun" +msgstr "æ交" + +#: git-gui.sh:1797 lib/merge.tcl:121 lib/merge.tcl:150 lib/merge.tcl:168 +msgid "Merge" +msgstr "åˆå¹¶" + +#: git-gui.sh:1798 lib/choose_rev.tcl:556 +#, fuzzy +msgid "Remote" +msgstr "改å..." + +#: git-gui.sh:1807 +msgid "Browse Current Branch's Files" +msgstr "æµè§ˆå½“å‰åˆ†æ”¯æ–‡ä»¶" + +#: git-gui.sh:1811 +#, fuzzy +msgid "Browse Branch Files..." +msgstr "æµè§ˆå½“å‰åˆ†æ”¯æ–‡ä»¶" + +#: git-gui.sh:1816 +msgid "Visualize Current Branch's History" +msgstr "调用gitk显示当å‰åˆ†æ”¯" + +#: git-gui.sh:1820 +msgid "Visualize All Branch History" +msgstr "调用gitk显示所有分支" + +#: git-gui.sh:1827 +#, fuzzy, tcl-format +msgid "Browse %s's Files" +msgstr "æµè§ˆå½“å‰åˆ†æ”¯æ–‡ä»¶" + +#: git-gui.sh:1829 +#, fuzzy, tcl-format +msgid "Visualize %s's History" +msgstr "调用gitk显示所有分支" + +#: git-gui.sh:1834 lib/database.tcl:27 lib/database.tcl:67 +msgid "Database Statistics" +msgstr "æ•°æ®åº“统计数æ®" + +#: git-gui.sh:1837 lib/database.tcl:34 +msgid "Compress Database" +msgstr "压缩数æ®åº“" + +#: git-gui.sh:1840 +msgid "Verify Database" +msgstr "验è¯æ•°æ®åº“" + +#: git-gui.sh:1847 git-gui.sh:1851 git-gui.sh:1855 lib/shortcut.tcl:9 +#: lib/shortcut.tcl:45 lib/shortcut.tcl:84 +msgid "Create Desktop Icon" +msgstr "创建桌é¢å›¾æ ‡" + +#: git-gui.sh:1860 lib/choose_repository.tcl:36 lib/choose_repository.tcl:95 +msgid "Quit" +msgstr "退出" + +#: git-gui.sh:1867 +msgid "Undo" +msgstr "撤销" + +#: git-gui.sh:1870 +msgid "Redo" +msgstr "é‡åš" + +#: git-gui.sh:1874 git-gui.sh:2366 +msgid "Cut" +msgstr "剪切" + +#: git-gui.sh:1877 git-gui.sh:2369 git-gui.sh:2440 git-gui.sh:2512 +#: lib/console.tcl:67 +msgid "Copy" +msgstr "å¤åˆ¶" + +#: git-gui.sh:1880 git-gui.sh:2372 +msgid "Paste" +msgstr "粘贴" + +#: git-gui.sh:1883 git-gui.sh:2375 lib/branch_delete.tcl:26 +#: lib/remote_branch_delete.tcl:38 +msgid "Delete" +msgstr "åˆ é™¤" + +#: git-gui.sh:1887 git-gui.sh:2379 git-gui.sh:2516 lib/console.tcl:69 +msgid "Select All" +msgstr "全选" + +#: git-gui.sh:1896 +msgid "Create..." +msgstr "新建..." + +#: git-gui.sh:1902 +msgid "Checkout..." +msgstr "切æ¢..." + +#: git-gui.sh:1908 +msgid "Rename..." +msgstr "改å..." + +#: git-gui.sh:1913 git-gui.sh:2012 +msgid "Delete..." +msgstr "åˆ é™¤..." + +#: git-gui.sh:1918 +msgid "Reset..." +msgstr "é‡ç½®æ‰€æœ‰ä¿®åŠ¨..." + +#: git-gui.sh:1930 git-gui.sh:2313 +msgid "New Commit" +msgstr "æ–°æ交" + +#: git-gui.sh:1938 git-gui.sh:2320 +msgid "Amend Last Commit" +msgstr "修订上次æ交" + +#: git-gui.sh:1947 git-gui.sh:2280 lib/remote_branch_delete.tcl:99 +msgid "Rescan" +msgstr "é‡æ–°æ‰«æ" + +#: git-gui.sh:1953 +#, fuzzy +msgid "Stage To Commit" +msgstr "从本次æ交移除" + +#: git-gui.sh:1958 +#, fuzzy +msgid "Stage Changed Files To Commit" +msgstr "将被æ交的修改" + +#: git-gui.sh:1964 +msgid "Unstage From Commit" +msgstr "从本次æ交移除" + +#: git-gui.sh:1969 lib/index.tcl:352 +msgid "Revert Changes" +msgstr "æ¢å¤ä¿®æ”¹" + +#: git-gui.sh:1976 git-gui.sh:2292 git-gui.sh:2390 +msgid "Sign Off" +msgstr "ç¾å" + +#: git-gui.sh:1980 git-gui.sh:2296 +#, fuzzy +msgid "Commit@@verb" +msgstr "æ交" + +#: git-gui.sh:1991 +msgid "Local Merge..." +msgstr "本地åˆå¹¶..." + +#: git-gui.sh:1996 +msgid "Abort Merge..." +msgstr "å–消åˆå¹¶..." + +#: git-gui.sh:2008 +msgid "Push..." +msgstr "ä¸Šä¼ ..." + +#: git-gui.sh:2019 lib/choose_repository.tcl:41 +msgid "Apple" +msgstr "苹果" + +#: git-gui.sh:2022 git-gui.sh:2044 lib/about.tcl:13 +#: lib/choose_repository.tcl:44 lib/choose_repository.tcl:50 +#, tcl-format +msgid "About %s" +msgstr "关于%s" + +#: git-gui.sh:2026 +msgid "Preferences..." +msgstr "" + +#: git-gui.sh:2034 git-gui.sh:2558 +msgid "Options..." +msgstr "选项..." + +#: git-gui.sh:2040 lib/choose_repository.tcl:47 +msgid "Help" +msgstr "帮助" + +#: git-gui.sh:2081 +msgid "Online Documentation" +msgstr "在线文档" + +#: git-gui.sh:2165 +#, tcl-format +msgid "fatal: cannot stat path %s: No such file or directory" +msgstr "" + +#: git-gui.sh:2198 +msgid "Current Branch:" +msgstr "当å‰åˆ†æ”¯:" + +#: git-gui.sh:2219 +#, fuzzy +msgid "Staged Changes (Will Commit)" +msgstr "将被æ交的修改" + +#: git-gui.sh:2239 +msgid "Unstaged Changes" +msgstr "" + +#: git-gui.sh:2286 +msgid "Stage Changed" +msgstr "" + +#: git-gui.sh:2302 lib/transport.tcl:93 lib/transport.tcl:182 +msgid "Push" +msgstr "ä¸Šä¼ " + +#: git-gui.sh:2332 +msgid "Initial Commit Message:" +msgstr "åˆå§‹æ交æè¿°:" + +#: git-gui.sh:2333 +msgid "Amended Commit Message:" +msgstr "修订æ交æè¿°:" + +#: git-gui.sh:2334 +msgid "Amended Initial Commit Message:" +msgstr "修订åˆå§‹æ交æè¿°:" + +#: git-gui.sh:2335 +msgid "Amended Merge Commit Message:" +msgstr "修订åˆå¹¶æ交æè¿°:" + +#: git-gui.sh:2336 +msgid "Merge Commit Message:" +msgstr "åˆå¹¶æ交æè¿°:" + +#: git-gui.sh:2337 +msgid "Commit Message:" +msgstr "æ交æè¿°:" + +#: git-gui.sh:2382 git-gui.sh:2520 lib/console.tcl:71 +msgid "Copy All" +msgstr "全部å¤åˆ¶" + +#: git-gui.sh:2406 lib/blame.tcl:104 +msgid "File:" +msgstr "" + +#: git-gui.sh:2508 +msgid "Refresh" +msgstr "刷新" + +#: git-gui.sh:2529 +msgid "Apply/Reverse Hunk" +msgstr "应用/撤消æ¤ä¿®æ”¹å—" + +#: git-gui.sh:2535 +msgid "Decrease Font Size" +msgstr "缩å°å—体" + +#: git-gui.sh:2539 +msgid "Increase Font Size" +msgstr "放大å—体" + +#: git-gui.sh:2544 +msgid "Show Less Context" +msgstr "显示更多diff上下文" + +#: git-gui.sh:2551 +msgid "Show More Context" +msgstr "显示更少diff上下文" + +#: git-gui.sh:2565 +#, fuzzy +msgid "Unstage Hunk From Commit" +msgstr "从本次æ交移除" + +#: git-gui.sh:2567 +#, fuzzy +msgid "Stage Hunk For Commit" +msgstr "从本次æ交移除" + +#: git-gui.sh:2586 +msgid "Initializing..." +msgstr "" + +#: git-gui.sh:2677 +#, tcl-format +msgid "" +"Possible environment issues exist.\n" +"\n" +"The following environment variables are probably\n" +"going to be ignored by any Git subprocess run\n" +"by %s:\n" +"\n" +msgstr "" + +#: git-gui.sh:2707 +msgid "" +"\n" +"This is due to a known issue with the\n" +"Tcl binary distributed by Cygwin." +msgstr "" + +#: git-gui.sh:2712 +#, tcl-format +msgid "" +"\n" +"\n" +"A good replacement for %s\n" +"is placing values for the user.name and\n" +"user.email settings into your personal\n" +"~/.gitconfig file.\n" +msgstr "" + +#: lib/about.tcl:25 +msgid "git-gui - a graphical user interface for Git." +msgstr "" + +#: lib/blame.tcl:77 +msgid "File Viewer" +msgstr "" + +#: lib/blame.tcl:81 +#, fuzzy +msgid "Commit:" +msgstr "æ交" + +#: lib/blame.tcl:249 +#, fuzzy +msgid "Copy Commit" +msgstr "æ交" + +#: lib/blame.tcl:369 +#, tcl-format +msgid "Reading %s..." +msgstr "" + +#: lib/blame.tcl:473 +msgid "Loading copy/move tracking annotations..." +msgstr "" + +#: lib/blame.tcl:493 +msgid "lines annotated" +msgstr "" + +#: lib/blame.tcl:674 +msgid "Loading original location annotations..." +msgstr "" + +#: lib/blame.tcl:677 +msgid "Annotation complete." +msgstr "" + +#: lib/blame.tcl:731 +msgid "Loading annotation..." +msgstr "" + +#: lib/blame.tcl:787 +msgid "Author:" +msgstr "" + +#: lib/blame.tcl:791 +#, fuzzy +msgid "Committer:" +msgstr "æ交" + +#: lib/blame.tcl:796 +msgid "Original File:" +msgstr "" + +#: lib/blame.tcl:910 +msgid "Originally By:" +msgstr "" + +#: lib/blame.tcl:916 +msgid "In File:" +msgstr "" + +#: lib/blame.tcl:921 +msgid "Copied Or Moved Here By:" +msgstr "" + +#: lib/branch_checkout.tcl:14 lib/branch_checkout.tcl:19 +#, fuzzy +msgid "Checkout Branch" +msgstr "当å‰åˆ†æ”¯:" + +#: lib/branch_checkout.tcl:23 +#, fuzzy +msgid "Checkout" +msgstr "切æ¢..." + +#: lib/branch_checkout.tcl:27 lib/branch_create.tcl:35 +#: lib/branch_delete.tcl:32 lib/branch_rename.tcl:30 lib/browser.tcl:281 +#: lib/checkout_op.tcl:522 lib/choose_font.tcl:43 lib/merge.tcl:172 +#: lib/option.tcl:90 lib/remote_branch_delete.tcl:42 lib/transport.tcl:97 +msgid "Cancel" +msgstr "" + +#: lib/branch_checkout.tcl:32 lib/browser.tcl:286 +msgid "Revision" +msgstr "" + +#: lib/branch_checkout.tcl:36 lib/branch_create.tcl:69 lib/option.tcl:202 +#, fuzzy +msgid "Options" +msgstr "选项..." + +#: lib/branch_checkout.tcl:39 lib/branch_create.tcl:92 +msgid "Fetch Tracking Branch" +msgstr "" + +#: lib/branch_checkout.tcl:44 +msgid "Detach From Local Branch" +msgstr "" + +#: lib/branch_create.tcl:22 +#, fuzzy +msgid "Create Branch" +msgstr "当å‰åˆ†æ”¯:" + +#: lib/branch_create.tcl:27 +#, fuzzy +msgid "Create New Branch" +msgstr "当å‰åˆ†æ”¯:" + +#: lib/branch_create.tcl:31 lib/choose_repository.tcl:199 +#, fuzzy +msgid "Create" +msgstr "新建..." + +#: lib/branch_create.tcl:40 +#, fuzzy +msgid "Branch Name" +msgstr "分支" + +#: lib/branch_create.tcl:43 +msgid "Name:" +msgstr "" + +#: lib/branch_create.tcl:58 +msgid "Match Tracking Branch Name" +msgstr "" + +#: lib/branch_create.tcl:66 +msgid "Starting Revision" +msgstr "" + +#: lib/branch_create.tcl:72 +msgid "Update Existing Branch:" +msgstr "" + +#: lib/branch_create.tcl:75 +msgid "No" +msgstr "" + +#: lib/branch_create.tcl:80 +msgid "Fast Forward Only" +msgstr "" + +#: lib/branch_create.tcl:85 lib/checkout_op.tcl:514 +#, fuzzy +msgid "Reset" +msgstr "é‡ç½®æ‰€æœ‰ä¿®åŠ¨..." + +#: lib/branch_create.tcl:97 +msgid "Checkout After Creation" +msgstr "" + +#: lib/branch_create.tcl:131 +msgid "Please select a tracking branch." +msgstr "" + +#: lib/branch_create.tcl:140 +#, tcl-format +msgid "Tracking branch %s is not a branch in the remote repository." +msgstr "" + +#: lib/branch_create.tcl:153 lib/branch_rename.tcl:86 +msgid "Please supply a branch name." +msgstr "" + +#: lib/branch_create.tcl:164 lib/branch_rename.tcl:106 +#, tcl-format +msgid "'%s' is not an acceptable branch name." +msgstr "" + +#: lib/branch_delete.tcl:15 +#, fuzzy +msgid "Delete Branch" +msgstr "当å‰åˆ†æ”¯:" + +#: lib/branch_delete.tcl:20 +msgid "Delete Local Branch" +msgstr "" + +#: lib/branch_delete.tcl:37 +#, fuzzy +msgid "Local Branches" +msgstr "分支" + +#: lib/branch_delete.tcl:52 +msgid "Delete Only If Merged Into" +msgstr "" + +#: lib/branch_delete.tcl:54 +msgid "Always (Do not perform merge test.)" +msgstr "" + +#: lib/branch_delete.tcl:103 +#, tcl-format +msgid "The following branches are not completely merged into %s:" +msgstr "" + +#: lib/branch_delete.tcl:115 +msgid "" +"Recovering deleted branches is difficult. \n" +"\n" +" Delete the selected branches?" +msgstr "" + +#: lib/branch_delete.tcl:141 +#, tcl-format +msgid "" +"Failed to delete branches:\n" +"%s" +msgstr "" + +#: lib/branch_rename.tcl:14 lib/branch_rename.tcl:22 +#, fuzzy +msgid "Rename Branch" +msgstr "当å‰åˆ†æ”¯:" + +#: lib/branch_rename.tcl:26 +#, fuzzy +msgid "Rename" +msgstr "改å..." + +#: lib/branch_rename.tcl:36 +#, fuzzy +msgid "Branch:" +msgstr "分支" + +#: lib/branch_rename.tcl:39 +msgid "New Name:" +msgstr "" + +#: lib/branch_rename.tcl:75 +msgid "Please select a branch to rename." +msgstr "" + +#: lib/branch_rename.tcl:96 lib/checkout_op.tcl:179 +#, tcl-format +msgid "Branch '%s' already exists." +msgstr "" + +#: lib/branch_rename.tcl:117 +#, tcl-format +msgid "Failed to rename '%s'." +msgstr "" + +#: lib/browser.tcl:17 +msgid "Starting..." +msgstr "" + +#: lib/browser.tcl:26 +msgid "File Browser" +msgstr "" + +#: lib/browser.tcl:125 lib/browser.tcl:142 +#, tcl-format +msgid "Loading %s..." +msgstr "" + +#: lib/browser.tcl:186 +msgid "[Up To Parent]" +msgstr "" + +#: lib/browser.tcl:266 lib/browser.tcl:272 +#, fuzzy +msgid "Browse Branch Files" +msgstr "æµè§ˆå½“å‰åˆ†æ”¯æ–‡ä»¶" + +#: lib/browser.tcl:277 lib/choose_repository.tcl:215 +#: lib/choose_repository.tcl:305 lib/choose_repository.tcl:315 +#: lib/choose_repository.tcl:811 +msgid "Browse" +msgstr "" + +#: lib/checkout_op.tcl:79 +#, tcl-format +msgid "Fetching %s from %s" +msgstr "" + +#: lib/checkout_op.tcl:127 +#, tcl-format +msgid "fatal: Cannot resolve %s" +msgstr "" + +#: lib/checkout_op.tcl:140 lib/console.tcl:79 lib/database.tcl:31 +msgid "Close" +msgstr "" + +#: lib/checkout_op.tcl:169 +#, tcl-format +msgid "Branch '%s' does not exist." +msgstr "" + +#: lib/checkout_op.tcl:206 +#, tcl-format +msgid "" +"Branch '%s' already exists.\n" +"\n" +"It cannot fast-forward to %s.\n" +"A merge is required." +msgstr "" + +#: lib/checkout_op.tcl:220 +#, tcl-format +msgid "Merge strategy '%s' not supported." +msgstr "" + +#: lib/checkout_op.tcl:239 +#, tcl-format +msgid "Failed to update '%s'." +msgstr "" + +#: lib/checkout_op.tcl:251 +msgid "Staging area (index) is already locked." +msgstr "" + +#: lib/checkout_op.tcl:266 +msgid "" +"Last scanned state does not match repository state.\n" +"\n" +"Another Git program has modified this repository since the last scan. A " +"rescan must be performed before the current branch can be changed.\n" +"\n" +"The rescan will be automatically started now.\n" +msgstr "" + +#: lib/checkout_op.tcl:322 +#, tcl-format +msgid "Updating working directory to '%s'..." +msgstr "" + +#: lib/checkout_op.tcl:353 +#, tcl-format +msgid "Aborted checkout of '%s' (file level merging is required)." +msgstr "" + +#: lib/checkout_op.tcl:354 +msgid "File level merge required." +msgstr "" + +#: lib/checkout_op.tcl:358 +#, tcl-format +msgid "Staying on branch '%s'." +msgstr "" + +#: lib/checkout_op.tcl:429 +msgid "" +"You are no longer on a local branch.\n" +"\n" +"If you wanted to be on a branch, create one now starting from 'This Detached " +"Checkout'." +msgstr "" + +#: lib/checkout_op.tcl:446 +#, fuzzy, tcl-format +msgid "Checked out '%s'." +msgstr "切æ¢..." + +#: lib/checkout_op.tcl:478 +#, tcl-format +msgid "Resetting '%s' to '%s' will lose the following commits:" +msgstr "" + +#: lib/checkout_op.tcl:500 +msgid "Recovering lost commits may not be easy." +msgstr "" + +#: lib/checkout_op.tcl:505 +#, tcl-format +msgid "Reset '%s'?" +msgstr "" + +#: lib/checkout_op.tcl:510 lib/merge.tcl:164 +msgid "Visualize" +msgstr "" + +#: lib/checkout_op.tcl:578 +#, tcl-format +msgid "" +"Failed to set current branch.\n" +"\n" +"This working directory is only partially switched. We successfully updated " +"your files, but failed to update an internal Git file.\n" +"\n" +"This should not have occurred. %s will now close and give up." +msgstr "" + +#: lib/choose_font.tcl:39 +#, fuzzy +msgid "Select" +msgstr "全选" + +#: lib/choose_font.tcl:53 +msgid "Font Family" +msgstr "" + +#: lib/choose_font.tcl:73 +#, fuzzy +msgid "Font Size" +msgstr "缩å°å—体" + +#: lib/choose_font.tcl:90 +msgid "Font Example" +msgstr "" + +#: lib/choose_font.tcl:101 +msgid "" +"This is example text.\n" +"If you like this text, it can be your font." +msgstr "" + +#: lib/choose_repository.tcl:25 +msgid "Git Gui" +msgstr "" + +#: lib/choose_repository.tcl:69 lib/choose_repository.tcl:204 +#, fuzzy +msgid "Create New Repository" +msgstr "ç‰ˆæœ¬æ ‘" + +#: lib/choose_repository.tcl:74 lib/choose_repository.tcl:291 +#, fuzzy +msgid "Clone Existing Repository" +msgstr "ç‰ˆæœ¬æ ‘" + +#: lib/choose_repository.tcl:79 lib/choose_repository.tcl:800 +#, fuzzy +msgid "Open Existing Repository" +msgstr "ç‰ˆæœ¬æ ‘" + +#: lib/choose_repository.tcl:91 +msgid "Next >" +msgstr "" + +#: lib/choose_repository.tcl:152 +#, tcl-format +msgid "Location %s already exists." +msgstr "" + +#: lib/choose_repository.tcl:158 lib/choose_repository.tcl:165 +#: lib/choose_repository.tcl:172 +#, tcl-format +msgid "Failed to create repository %s:" +msgstr "" + +#: lib/choose_repository.tcl:209 lib/choose_repository.tcl:309 +msgid "Directory:" +msgstr "" + +#: lib/choose_repository.tcl:238 lib/choose_repository.tcl:363 +#: lib/choose_repository.tcl:834 +#, fuzzy +msgid "Git Repository" +msgstr "ç‰ˆæœ¬æ ‘" + +#: lib/choose_repository.tcl:253 lib/choose_repository.tcl:260 +#, tcl-format +msgid "Directory %s already exists." +msgstr "" + +#: lib/choose_repository.tcl:265 +#, tcl-format +msgid "File %s already exists." +msgstr "" + +#: lib/choose_repository.tcl:286 +msgid "Clone" +msgstr "" + +#: lib/choose_repository.tcl:299 +msgid "URL:" +msgstr "" + +#: lib/choose_repository.tcl:319 +msgid "Clone Type:" +msgstr "" + +#: lib/choose_repository.tcl:325 +msgid "Standard (Fast, Semi-Redundant, Hardlinks)" +msgstr "" + +#: lib/choose_repository.tcl:331 +msgid "Full Copy (Slower, Redundant Backup)" +msgstr "" + +#: lib/choose_repository.tcl:337 +msgid "Shared (Fastest, Not Recommended, No Backup)" +msgstr "" + +#: lib/choose_repository.tcl:369 lib/choose_repository.tcl:418 +#: lib/choose_repository.tcl:560 lib/choose_repository.tcl:630 +#: lib/choose_repository.tcl:840 lib/choose_repository.tcl:848 +#, tcl-format +msgid "Not a Git repository: %s" +msgstr "" + +#: lib/choose_repository.tcl:405 +msgid "Standard only available for local repository." +msgstr "" + +#: lib/choose_repository.tcl:409 +msgid "Shared only available for local repository." +msgstr "" + +#: lib/choose_repository.tcl:439 +msgid "Failed to configure origin" +msgstr "" + +#: lib/choose_repository.tcl:451 +msgid "Counting objects" +msgstr "" + +#: lib/choose_repository.tcl:452 +msgid "buckets" +msgstr "" + +#: lib/choose_repository.tcl:476 +#, tcl-format +msgid "Unable to copy objects/info/alternates: %s" +msgstr "" + +#: lib/choose_repository.tcl:512 +#, tcl-format +msgid "Nothing to clone from %s." +msgstr "" + +#: lib/choose_repository.tcl:514 lib/choose_repository.tcl:728 +#: lib/choose_repository.tcl:740 +msgid "The 'master' branch has not been initialized." +msgstr "" + +#: lib/choose_repository.tcl:527 +msgid "Hardlinks are unavailable. Falling back to copying." +msgstr "" + +#: lib/choose_repository.tcl:539 +#, tcl-format +msgid "Cloning from %s" +msgstr "" + +#: lib/choose_repository.tcl:570 +#, fuzzy +msgid "Copying objects" +msgstr "压缩数æ®åº“" + +#: lib/choose_repository.tcl:571 +msgid "KiB" +msgstr "" + +#: lib/choose_repository.tcl:595 +#, tcl-format +msgid "Unable to copy object: %s" +msgstr "" + +#: lib/choose_repository.tcl:605 +msgid "Linking objects" +msgstr "" + +#: lib/choose_repository.tcl:606 +msgid "objects" +msgstr "" + +#: lib/choose_repository.tcl:614 +#, tcl-format +msgid "Unable to hardlink object: %s" +msgstr "" + +#: lib/choose_repository.tcl:669 +msgid "Cannot fetch branches and objects. See console output for details." +msgstr "" + +#: lib/choose_repository.tcl:680 +msgid "Cannot fetch tags. See console output for details." +msgstr "" + +#: lib/choose_repository.tcl:704 +msgid "Cannot determine HEAD. See console output for details." +msgstr "" + +#: lib/choose_repository.tcl:713 +#, tcl-format +msgid "Unable to cleanup %s" +msgstr "" + +#: lib/choose_repository.tcl:719 +msgid "Clone failed." +msgstr "" + +#: lib/choose_repository.tcl:726 +msgid "No default branch obtained." +msgstr "" + +#: lib/choose_repository.tcl:737 +#, tcl-format +msgid "Cannot resolve %s as a commit." +msgstr "" + +#: lib/choose_repository.tcl:749 +msgid "Creating working directory" +msgstr "" + +#: lib/choose_repository.tcl:750 lib/index.tcl:15 lib/index.tcl:80 +#: lib/index.tcl:149 +msgid "files" +msgstr "" + +#: lib/choose_repository.tcl:779 +msgid "Initial file checkout failed." +msgstr "" + +#: lib/choose_repository.tcl:795 +msgid "Open" +msgstr "" + +#: lib/choose_repository.tcl:805 +#, fuzzy +msgid "Repository:" +msgstr "ç‰ˆæœ¬æ ‘" + +#: lib/choose_repository.tcl:854 +#, tcl-format +msgid "Failed to open repository %s:" +msgstr "" + +#: lib/choose_rev.tcl:53 +msgid "This Detached Checkout" +msgstr "" + +#: lib/choose_rev.tcl:60 +msgid "Revision Expression:" +msgstr "" + +#: lib/choose_rev.tcl:74 +#, fuzzy +msgid "Local Branch" +msgstr "分支" + +#: lib/choose_rev.tcl:79 +#, fuzzy +msgid "Tracking Branch" +msgstr "当å‰åˆ†æ”¯:" + +#: lib/choose_rev.tcl:84 lib/choose_rev.tcl:537 +msgid "Tag" +msgstr "" + +#: lib/choose_rev.tcl:317 +#, tcl-format +msgid "Invalid revision: %s" +msgstr "" + +#: lib/choose_rev.tcl:338 +msgid "No revision selected." +msgstr "" + +#: lib/choose_rev.tcl:346 +msgid "Revision expression is empty." +msgstr "" + +#: lib/choose_rev.tcl:530 +msgid "Updated" +msgstr "" + +#: lib/choose_rev.tcl:558 +msgid "URL" +msgstr "" + +#: lib/commit.tcl:9 +msgid "" +"There is nothing to amend.\n" +"\n" +"You are about to create the initial commit. There is no commit before this " +"to amend.\n" +msgstr "" + +#: lib/commit.tcl:18 +msgid "" +"Cannot amend while merging.\n" +"\n" +"You are currently in the middle of a merge that has not been fully " +"completed. You cannot amend the prior commit unless you first abort the " +"current merge activity.\n" +msgstr "" + +#: lib/commit.tcl:49 +msgid "Error loading commit data for amend:" +msgstr "" + +#: lib/commit.tcl:76 +msgid "Unable to obtain your identity:" +msgstr "" + +#: lib/commit.tcl:81 +msgid "Invalid GIT_COMMITTER_IDENT:" +msgstr "" + +#: lib/commit.tcl:133 +msgid "" +"Last scanned state does not match repository state.\n" +"\n" +"Another Git program has modified this repository since the last scan. A " +"rescan must be performed before another commit can be created.\n" +"\n" +"The rescan will be automatically started now.\n" +msgstr "" + +#: lib/commit.tcl:154 +#, tcl-format +msgid "" +"Unmerged files cannot be committed.\n" +"\n" +"File %s has merge conflicts. You must resolve them and stage the file " +"before committing.\n" +msgstr "" + +#: lib/commit.tcl:162 +#, tcl-format +msgid "" +"Unknown file state %s detected.\n" +"\n" +"File %s cannot be committed by this program.\n" +msgstr "" + +#: lib/commit.tcl:170 +msgid "" +"No changes to commit.\n" +"\n" +"You must stage at least 1 file before you can commit.\n" +msgstr "" + +#: lib/commit.tcl:183 +msgid "" +"Please supply a commit message.\n" +"\n" +"A good commit message has the following format:\n" +"\n" +"- First line: Describe in one sentance what you did.\n" +"- Second line: Blank\n" +"- Remaining lines: Describe why this change is good.\n" +msgstr "" + +#: lib/commit.tcl:257 +msgid "write-tree failed:" +msgstr "" + +#: lib/commit.tcl:275 +#, tcl-format +msgid "Commit %s appears to be corrupt" +msgstr "" + +#: lib/commit.tcl:279 +msgid "" +"No changes to commit.\n" +"\n" +"No files were modified by this commit and it was not a merge commit.\n" +"\n" +"A rescan will be automatically started now.\n" +msgstr "" + +#: lib/commit.tcl:286 +msgid "No changes to commit." +msgstr "" + +#: lib/commit.tcl:303 +#, tcl-format +msgid "warning: Tcl does not support encoding '%s'." +msgstr "" + +#: lib/commit.tcl:317 +msgid "commit-tree failed:" +msgstr "" + +#: lib/commit.tcl:339 +msgid "update-ref failed:" +msgstr "" + +#: lib/commit.tcl:430 +#, tcl-format +msgid "Created commit %s: %s" +msgstr "" + +#: lib/console.tcl:57 +msgid "Working... please wait..." +msgstr "" + +#: lib/console.tcl:183 +msgid "Success" +msgstr "" + +#: lib/console.tcl:196 +msgid "Error: Command Failed" +msgstr "" + +#: lib/database.tcl:43 +msgid "Number of loose objects" +msgstr "" + +#: lib/database.tcl:44 +msgid "Disk space used by loose objects" +msgstr "" + +#: lib/database.tcl:45 +msgid "Number of packed objects" +msgstr "" + +#: lib/database.tcl:46 +msgid "Number of packs" +msgstr "" + +#: lib/database.tcl:47 +msgid "Disk space used by packed objects" +msgstr "" + +#: lib/database.tcl:48 +msgid "Packed objects waiting for pruning" +msgstr "" + +#: lib/database.tcl:49 +msgid "Garbage files" +msgstr "" + +#: lib/database.tcl:72 +#, fuzzy +msgid "Compressing the object database" +msgstr "压缩数æ®åº“" + +#: lib/database.tcl:83 +msgid "Verifying the object database with fsck-objects" +msgstr "" + +#: lib/database.tcl:108 +#, tcl-format +msgid "" +"This repository currently has approximately %i loose objects.\n" +"\n" +"To maintain optimal performance it is strongly recommended that you compress " +"the database when more than %i loose objects exist.\n" +"\n" +"Compress the database now?" +msgstr "" + +#: lib/date.tcl:25 +#, tcl-format +msgid "Invalid date from Git: %s" +msgstr "" + +#: lib/diff.tcl:42 +#, tcl-format +msgid "" +"No differences detected.\n" +"\n" +"%s has no changes.\n" +"\n" +"The modification date of this file was updated by another application, but " +"the content within the file was not changed.\n" +"\n" +"A rescan will be automatically started to find other files which may have " +"the same state." +msgstr "" + +#: lib/diff.tcl:81 +#, tcl-format +msgid "Loading diff of %s..." +msgstr "" + +#: lib/diff.tcl:114 lib/diff.tcl:184 +#, tcl-format +msgid "Unable to display %s" +msgstr "" + +#: lib/diff.tcl:115 +msgid "Error loading file:" +msgstr "" + +#: lib/diff.tcl:122 +msgid "Git Repository (subproject)" +msgstr "" + +#: lib/diff.tcl:134 +msgid "* Binary file (not showing content)." +msgstr "" + +#: lib/diff.tcl:185 +msgid "Error loading diff:" +msgstr "" + +#: lib/diff.tcl:302 +msgid "Failed to unstage selected hunk." +msgstr "" + +#: lib/diff.tcl:309 +msgid "Failed to stage selected hunk." +msgstr "" + +#: lib/error.tcl:12 lib/error.tcl:102 +msgid "error" +msgstr "" + +#: lib/error.tcl:28 +msgid "warning" +msgstr "" + +#: lib/error.tcl:81 +msgid "You must correct the above errors before committing." +msgstr "" + +#: lib/index.tcl:241 +#, fuzzy, tcl-format +msgid "Unstaging %s from commit" +msgstr "从本次æ交移除" + +#: lib/index.tcl:285 +#, tcl-format +msgid "Adding %s" +msgstr "" + +#: lib/index.tcl:340 +#, fuzzy, tcl-format +msgid "Revert changes in file %s?" +msgstr "æ¢å¤ä¿®æ”¹" + +#: lib/index.tcl:342 +#, tcl-format +msgid "Revert changes in these %i files?" +msgstr "" + +#: lib/index.tcl:348 +msgid "Any unstaged changes will be permanently lost by the revert." +msgstr "" + +#: lib/index.tcl:351 +msgid "Do Nothing" +msgstr "" + +#: lib/merge.tcl:13 +msgid "" +"Cannot merge while amending.\n" +"\n" +"You must finish amending this commit before starting any type of merge.\n" +msgstr "" + +#: lib/merge.tcl:27 +msgid "" +"Last scanned state does not match repository state.\n" +"\n" +"Another Git program has modified this repository since the last scan. A " +"rescan must be performed before a merge can be performed.\n" +"\n" +"The rescan will be automatically started now.\n" +msgstr "" + +#: lib/merge.tcl:44 +#, tcl-format +msgid "" +"You are in the middle of a conflicted merge.\n" +"\n" +"File %s has merge conflicts.\n" +"\n" +"You must resolve them, stage the file, and commit to complete the current " +"merge. Only then can you begin another merge.\n" +msgstr "" + +#: lib/merge.tcl:54 +#, tcl-format +msgid "" +"You are in the middle of a change.\n" +"\n" +"File %s is modified.\n" +"\n" +"You should complete the current commit before starting a merge. Doing so " +"will help you abort a failed merge, should the need arise.\n" +msgstr "" + +#: lib/merge.tcl:106 +#, tcl-format +msgid "%s of %s" +msgstr "" + +#: lib/merge.tcl:119 +#, tcl-format +msgid "Merging %s and %s" +msgstr "" + +#: lib/merge.tcl:131 +msgid "Merge completed successfully." +msgstr "" + +#: lib/merge.tcl:133 +msgid "Merge failed. Conflict resolution is required." +msgstr "" + +#: lib/merge.tcl:158 +#, tcl-format +msgid "Merge Into %s" +msgstr "" + +#: lib/merge.tcl:177 +msgid "Revision To Merge" +msgstr "" + +#: lib/merge.tcl:212 +msgid "" +"Cannot abort while amending.\n" +"\n" +"You must finish amending this commit.\n" +msgstr "" + +#: lib/merge.tcl:222 +msgid "" +"Abort merge?\n" +"\n" +"Aborting the current merge will cause *ALL* uncommitted changes to be lost.\n" +"\n" +"Continue with aborting the current merge?" +msgstr "" + +#: lib/merge.tcl:228 +msgid "" +"Reset changes?\n" +"\n" +"Resetting the changes will cause *ALL* uncommitted changes to be lost.\n" +"\n" +"Continue with resetting the current changes?" +msgstr "" + +#: lib/merge.tcl:239 +msgid "Aborting" +msgstr "" + +#: lib/merge.tcl:266 +msgid "Abort failed." +msgstr "" + +#: lib/merge.tcl:268 +msgid "Abort completed. Ready." +msgstr "" + +#: lib/option.tcl:82 +msgid "Restore Defaults" +msgstr "" + +#: lib/option.tcl:86 +msgid "Save" +msgstr "" + +#: lib/option.tcl:96 +#, fuzzy, tcl-format +msgid "%s Repository" +msgstr "ç‰ˆæœ¬æ ‘" + +#: lib/option.tcl:97 +msgid "Global (All Repositories)" +msgstr "" + +#: lib/option.tcl:103 +msgid "User Name" +msgstr "" + +#: lib/option.tcl:104 +msgid "Email Address" +msgstr "" + +#: lib/option.tcl:106 +#, fuzzy +msgid "Summarize Merge Commits" +msgstr "修订åˆå¹¶æ交æè¿°:" + +#: lib/option.tcl:107 +msgid "Merge Verbosity" +msgstr "" + +#: lib/option.tcl:108 +msgid "Show Diffstat After Merge" +msgstr "" + +#: lib/option.tcl:110 +msgid "Trust File Modification Timestamps" +msgstr "" + +#: lib/option.tcl:111 +msgid "Prune Tracking Branches During Fetch" +msgstr "" + +#: lib/option.tcl:112 +msgid "Match Tracking Branches" +msgstr "" + +#: lib/option.tcl:113 +msgid "Number of Diff Context Lines" +msgstr "" + +#: lib/option.tcl:114 +msgid "New Branch Name Template" +msgstr "" + +#: lib/option.tcl:176 +msgid "Change Font" +msgstr "" + +#: lib/option.tcl:180 +#, tcl-format +msgid "Choose %s" +msgstr "" + +#: lib/option.tcl:186 +msgid "pt." +msgstr "" + +#: lib/option.tcl:200 +msgid "Preferences" +msgstr "" + +#: lib/option.tcl:235 +msgid "Failed to completely save options:" +msgstr "" + +#: lib/remote.tcl:165 +msgid "Prune from" +msgstr "" + +#: lib/remote.tcl:170 +#, fuzzy +msgid "Fetch from" +msgstr "导入" + +#: lib/remote.tcl:213 +#, fuzzy +msgid "Push to" +msgstr "ä¸Šä¼ " + +#: lib/remote_branch_delete.tcl:29 lib/remote_branch_delete.tcl:34 +msgid "Delete Remote Branch" +msgstr "" + +#: lib/remote_branch_delete.tcl:47 +#, fuzzy +msgid "From Repository" +msgstr "ç‰ˆæœ¬æ ‘" + +#: lib/remote_branch_delete.tcl:50 lib/transport.tcl:123 +msgid "Remote:" +msgstr "" + +#: lib/remote_branch_delete.tcl:66 lib/transport.tcl:138 +msgid "Arbitrary URL:" +msgstr "" + +#: lib/remote_branch_delete.tcl:84 +#, fuzzy +msgid "Branches" +msgstr "分支" + +#: lib/remote_branch_delete.tcl:109 +#, fuzzy +msgid "Delete Only If" +msgstr "åˆ é™¤" + +#: lib/remote_branch_delete.tcl:111 +msgid "Merged Into:" +msgstr "" + +#: lib/remote_branch_delete.tcl:119 +msgid "Always (Do not perform merge checks)" +msgstr "" + +#: lib/remote_branch_delete.tcl:152 +msgid "A branch is required for 'Merged Into'." +msgstr "" + +#: lib/remote_branch_delete.tcl:184 +#, tcl-format +msgid "" +"The following branches are not completely merged into %s:\n" +"\n" +" - %s" +msgstr "" + +#: lib/remote_branch_delete.tcl:189 +#, tcl-format +msgid "" +"One or more of the merge tests failed because you have not fetched the " +"necessary commits. Try fetching from %s first." +msgstr "" + +#: lib/remote_branch_delete.tcl:207 +msgid "Please select one or more branches to delete." +msgstr "" + +#: lib/remote_branch_delete.tcl:216 +msgid "" +"Recovering deleted branches is difficult.\n" +"\n" +"Delete the selected branches?" +msgstr "" + +#: lib/remote_branch_delete.tcl:226 +#, tcl-format +msgid "Deleting branches from %s" +msgstr "" + +#: lib/remote_branch_delete.tcl:286 +msgid "No repository selected." +msgstr "" + +#: lib/remote_branch_delete.tcl:291 +#, tcl-format +msgid "Scanning %s..." +msgstr "" + +#: lib/shortcut.tcl:26 lib/shortcut.tcl:74 +msgid "Cannot write script:" +msgstr "" + +#: lib/shortcut.tcl:149 +msgid "Cannot write icon:" +msgstr "" + +#: lib/status_bar.tcl:83 +#, tcl-format +msgid "%s ... %*i of %*i %s (%3i%%)" +msgstr "" + +#: lib/transport.tcl:6 +#, fuzzy, tcl-format +msgid "fetch %s" +msgstr "导入" + +#: lib/transport.tcl:7 +#, tcl-format +msgid "Fetching new changes from %s" +msgstr "" + +#: lib/transport.tcl:18 +#, tcl-format +msgid "remote prune %s" +msgstr "" + +#: lib/transport.tcl:19 +#, tcl-format +msgid "Pruning tracking branches deleted from %s" +msgstr "" + +#: lib/transport.tcl:25 lib/transport.tcl:71 +#, tcl-format +msgid "push %s" +msgstr "" + +#: lib/transport.tcl:26 +#, tcl-format +msgid "Pushing changes to %s" +msgstr "" + +#: lib/transport.tcl:72 +#, tcl-format +msgid "Pushing %s %s to %s" +msgstr "" + +#: lib/transport.tcl:89 +#, fuzzy +msgid "Push Branches" +msgstr "分支" + +#: lib/transport.tcl:103 +#, fuzzy +msgid "Source Branches" +msgstr "当å‰åˆ†æ”¯:" + +#: lib/transport.tcl:120 +#, fuzzy +msgid "Destination Repository" +msgstr "ç‰ˆæœ¬æ ‘" + +#: lib/transport.tcl:158 +msgid "Transfer Options" +msgstr "" + +#: lib/transport.tcl:160 +msgid "Force overwrite existing branch (may discard changes)" +msgstr "" + +#: lib/transport.tcl:164 +msgid "Use thin pack (for slow network connections)" +msgstr "" + +#: lib/transport.tcl:168 +msgid "Include tags" +msgstr "" + +#~ msgid "Add To Commit" +#~ msgstr "æ·»åŠ åˆ°æœ¬æ¬¡æ交" + +#~ msgid "Add Existing To Commit" +#~ msgstr "æ·»åŠ é»˜è®¤ä¿®æ”¹æ–‡ä»¶" + +#~ msgid "Unstaged Changes (Will Not Be Committed)" +#~ msgstr "ä¸è¢«æ交的修改" + +#~ msgid "Add Existing" +#~ msgstr "æ·»åŠ é»˜è®¤ä¿®æ”¹æ–‡ä»¶" + +#, fuzzy +#~ msgid "Push to %s..." +#~ msgstr "ä¸Šä¼ ..." diff --git a/git-gui/windows/git-gui.sh b/git-gui/windows/git-gui.sh new file mode 100644 index 0000000000..98f32c0a07 --- /dev/null +++ b/git-gui/windows/git-gui.sh @@ -0,0 +1,16 @@ +#!/bin/sh +# Tcl ignores the next line -*- tcl -*- \ +exec wish "$0" -- "$@" + +if { $argc >=2 && [lindex $argv 0] == "--working-dir" } { + cd [lindex $argv 1] + set argv [lrange $argv 2 end] + incr argc -2 +} + +set gitguidir [file dirname [info script]] +regsub -all ";" $gitguidir "\\;" gitguidir +set env(PATH) "$gitguidir;$env(PATH)" +unset gitguidir + +source [file join [file dirname [info script]] git-gui.tcl] diff --git a/git-instaweb.sh b/git-instaweb.sh index 95c3e5aa1f..8503ae4030 100755 --- a/git-instaweb.sh +++ b/git-instaweb.sh @@ -2,9 +2,21 @@ # # Copyright (c) 2006 Eric Wong # -USAGE='[--start] [--stop] [--restart] - [--local] [--httpd=<httpd>] [--port=<port>] [--browser=<browser>] - [--module-path=<path> (for Apache2 only)]' + +OPTIONS_KEEPDASHDASH= +OPTIONS_SPEC="\ +git-instaweb [options] (--start | --stop | --restart) +-- +l,local only bind on 127.0.0.1 +p,port= the port to bind to +d,httpd= the command to launch +b,browser= the browser to launch +m,module-path= the module path (only needed for apache2) + Action +stop stop the web server +start start the web server +restart restart the web server +" . git-sh-setup @@ -15,7 +27,7 @@ browser="`git config --get instaweb.browser`" port=`git config --get instaweb.port` module_path="`git config --get instaweb.modulepath`" -conf=$GIT_DIR/gitweb/httpd.conf +conf="$GIT_DIR/gitweb/httpd.conf" # Defaults: @@ -32,7 +44,7 @@ start_httpd () { httpd_only="`echo $httpd | cut -f1 -d' '`" if case "$httpd_only" in /*) : ;; *) which $httpd_only >/dev/null;; esac then - $httpd $fqgitdir/gitweb/httpd.conf + $httpd "$fqgitdir/gitweb/httpd.conf" else # many httpds are installed in /usr/sbin or /usr/local/sbin # these days and those are not in most users $PATHs @@ -78,52 +90,26 @@ do start_httpd exit 0 ;; - --local|-l) + -l|--local) local=true ;; - -d|--httpd|--httpd=*) - case "$#,$1" in - *,*=*) - httpd=`expr "$1" : '-[^=]*=\(.*\)'` ;; - 1,*) - usage ;; - *) - httpd="$2" - shift ;; - esac + -d|--httpd) + shift + httpd="$1" + ;; + -b|--browser) + shift + browser="$1" ;; - -b|--browser|--browser=*) - case "$#,$1" in - *,*=*) - browser=`expr "$1" : '-[^=]*=\(.*\)'` ;; - 1,*) - usage ;; - *) - browser="$2" - shift ;; - esac + -p|--port) + shift + port="$1" ;; - -p|--port|--port=*) - case "$#,$1" in - *,*=*) - port=`expr "$1" : '-[^=]*=\(.*\)'` ;; - 1,*) - usage ;; - *) - port="$2" - shift ;; - esac + -m|--module-path) + shift + module_path="$1" ;; - -m|--module-path=*|--module-path) - case "$#,$1" in - *,*=*) - module_path=`expr "$1" : '-[^=]*=\(.*\)'` ;; - 1,*) - usage ;; - *) - module_path="$2" - shift ;; - esac + --) ;; *) usage @@ -185,14 +171,14 @@ server.pid-file = "$fqgitdir/pid" cgi.assign = ( ".cgi" => "" ) mimetype.assign = ( ".css" => "text/css" ) EOF - test "$local" = true && echo 'server.bind = "127.0.0.1"' >> "$conf" + test x"$local" = xtrue && echo 'server.bind = "127.0.0.1"' >> "$conf" } apache2_conf () { test -z "$module_path" && module_path=/usr/lib/apache2/modules mkdir -p "$GIT_DIR/gitweb/logs" bind= - test "$local" = true && bind='127.0.0.1:' + test x"$local" = xtrue && bind='127.0.0.1:' echo 'text/css css' > $fqgitdir/mime.types cat > "$conf" <<EOF ServerName "git-instaweb" @@ -245,7 +231,7 @@ EOF } script=' -s#^\(my\|our\) $projectroot =.*#\1 $projectroot = "'`dirname $fqgitdir`'";# +s#^\(my\|our\) $projectroot =.*#\1 $projectroot = "'$(dirname "$fqgitdir")'";# s#\(my\|our\) $gitbin =.*#\1 $gitbin = "'$GIT_EXEC_PATH'";# s#\(my\|our\) $projects_list =.*#\1 $projects_list = $projectroot;# s#\(my\|our\) $git_temp =.*#\1 $git_temp = "'$fqgitdir/gitweb/tmp'";#' @@ -265,8 +251,8 @@ gitweb_css () { EOFGITWEB } -gitweb_cgi $GIT_DIR/gitweb/gitweb.cgi -gitweb_css $GIT_DIR/gitweb/gitweb.css +gitweb_cgi "$GIT_DIR/gitweb/gitweb.cgi" +gitweb_css "$GIT_DIR/gitweb/gitweb.css" case "$httpd" in *lighttpd*) @@ -285,6 +271,5 @@ webrick) esac start_httpd -test -z "$browser" && browser=echo url=http://127.0.0.1:$port -$browser $url || echo $url +"$browser" $url || echo $url diff --git a/git-lost-found.sh b/git-lost-found.sh index c0b00e0fd1..9cedaf80ce 100755 --- a/git-lost-found.sh +++ b/git-lost-found.sh @@ -2,8 +2,11 @@ USAGE='' SUBDIRECTORY_OK='Yes' +OPTIONS_SPEC= . git-sh-setup +echo "WARNING: '$0' is deprecated in favor of 'git fsck --lost-found'" >&2 + if [ "$#" != "0" ] then usage diff --git a/git-merge.sh b/git-merge.sh index c2092a2040..1c123a37e6 100755 --- a/git-merge.sh +++ b/git-merge.sh @@ -3,7 +3,19 @@ # Copyright (c) 2005 Junio C Hamano # -USAGE='[-n] [--summary] [--[no-]commit] [--[no-]squash] [--[no-]ff] [-s <strategy>] [-m=<merge-message>] <commit>+' +OPTIONS_KEEPDASHDASH= +OPTIONS_SPEC="\ +git-merge [options] <remote>... +git-merge [options] <msg> HEAD <remote> +-- +summary show a diffstat at the end of the merge +n,no-summary don't show a diffstat at the end of the merge +squash create a single commit instead of doing a merge +commit perform a commit if the merge sucesses (default) +ff allow fast forward (default) +s,strategy= merge strategy to use +m,message= message to be used for the merge commit (if any) +" SUBDIRECTORY_OK=Yes . git-sh-setup @@ -28,20 +40,19 @@ allow_trivial_merge=t dropsave() { rm -f -- "$GIT_DIR/MERGE_HEAD" "$GIT_DIR/MERGE_MSG" \ - "$GIT_DIR/MERGE_SAVE" || exit 1 + "$GIT_DIR/MERGE_STASH" || exit 1 } savestate() { # Stash away any local modifications. - git diff-index -z --name-only $head | - cpio -0 -o >"$GIT_DIR/MERGE_SAVE" + git stash create >"$GIT_DIR/MERGE_STASH" } restorestate() { - if test -f "$GIT_DIR/MERGE_SAVE" + if test -f "$GIT_DIR/MERGE_STASH" then git reset --hard $head >/dev/null - cpio -iuv <"$GIT_DIR/MERGE_SAVE" + git stash apply $(cat "$GIT_DIR/MERGE_STASH") git update-index --refresh >/dev/null fi } @@ -133,72 +144,47 @@ merge_name () { fi } -parse_option () { - case "$1" in - -n|--n|--no|--no-|--no-s|--no-su|--no-sum|--no-summ|\ - --no-summa|--no-summar|--no-summary) - show_diffstat=false ;; - --summary) - show_diffstat=t ;; - --sq|--squ|--squa|--squas|--squash) - allow_fast_forward=t squash=t no_commit=t ;; - --no-sq|--no-squ|--no-squa|--no-squas|--no-squash) - allow_fast_forward=t squash= no_commit= ;; - --c|--co|--com|--comm|--commi|--commit) - allow_fast_forward=t squash= no_commit= ;; - --no-c|--no-co|--no-com|--no-comm|--no-commi|--no-commit) - allow_fast_forward=t squash= no_commit=t ;; - --ff) - allow_fast_forward=t squash= no_commit= ;; - --no-ff) - allow_fast_forward=false squash= no_commit= ;; - -s=*|--s=*|--st=*|--str=*|--stra=*|--strat=*|--strate=*|\ - --strateg=*|--strategy=*|\ - -s|--s|--st|--str|--stra|--strat|--strate|--strateg|--strategy) - case "$#,$1" in - *,*=*) - strategy=`expr "z$1" : 'z-[^=]*=\(.*\)'` ;; - 1,*) - usage ;; - *) - strategy="$2" - shift ;; - esac - case " $all_strategies " in - *" $strategy "*) - use_strategies="$use_strategies$strategy " ;; - *) - die "available strategies are: $all_strategies" ;; - esac - ;; - -m=*|--m=*|--me=*|--mes=*|--mess=*|--messa=*|--messag=*|--message=*) - merge_msg=`expr "z$1" : 'z-[^=]*=\(.*\)'` - have_message=t - ;; - -m|--m|--me|--mes|--mess|--messa|--messag|--message) - shift - case "$#" in - 1) usage ;; - esac - merge_msg="$1" - have_message=t - ;; - -*) usage ;; - *) return 1 ;; - esac - shift - args_left=$# -} - parse_config () { - while test $# -gt 0 - do - parse_option "$@" || usage - while test $args_left -lt $# - do + while test $# != 0; do + case "$1" in + -n|--no-summary) + show_diffstat=false ;; + --summary) + show_diffstat=t ;; + --squash) + allow_fast_forward=t squash=t no_commit=t ;; + --no-squash) + allow_fast_forward=t squash= no_commit= ;; + --commit) + allow_fast_forward=t squash= no_commit= ;; + --no-commit) + allow_fast_forward=t squash= no_commit=t ;; + --ff) + allow_fast_forward=t squash= no_commit= ;; + --no-ff) + allow_fast_forward=false squash= no_commit= ;; + -s|--strategy) + shift + case " $all_strategies " in + *" $1 "*) + use_strategies="$use_strategies$1 " ;; + *) + die "available strategies are: $all_strategies" ;; + esac + ;; + -m|--message) shift - done + merge_msg="$1" + have_message=t + ;; + --) + shift + break ;; + *) usage ;; + esac + shift done + args_left=$# } test $# != 0 || usage @@ -210,17 +196,12 @@ then mergeopts=$(git config "branch.${branch#refs/heads/}.mergeoptions") if test -n "$mergeopts" then - parse_config $mergeopts + parse_config $mergeopts -- fi fi -while parse_option "$@" -do - while test $args_left -lt $# - do - shift - done -done +parse_config "$@" +while test $args_left -lt $#; do shift; done if test -z "$show_diffstat"; then test "$(git config --bool merge.diffstat)" = false && show_diffstat=false @@ -437,7 +418,7 @@ case "$use_strategies" in single_strategy=no ;; *) - rm -f "$GIT_DIR/MERGE_SAVE" + rm -f "$GIT_DIR/MERGE_STASH" single_strategy=yes ;; esac diff --git a/git-mergetool.sh b/git-mergetool.sh index a68b40386b..2f31fa2417 100755 --- a/git-mergetool.sh +++ b/git-mergetool.sh @@ -10,6 +10,7 @@ USAGE='[--tool=tool] [file to merge] ...' SUBDIRECTORY_OK=Yes +OPTIONS_SPEC= . git-sh-setup require_work_tree prefix=$(git rev-parse --show-prefix) @@ -151,10 +152,11 @@ merge_file () { exit 1 fi - BACKUP="$path.BACKUP.$$" - LOCAL="$path.LOCAL.$$" - REMOTE="$path.REMOTE.$$" - BASE="$path.BASE.$$" + ext="$$$(expr "$path" : '.*\(\.[^/]*\)$')" + BACKUP="$path.BACKUP.$ext" + LOCAL="$path.LOCAL.$ext" + REMOTE="$path.REMOTE.$ext" + BASE="$path.BASE.$ext" mv -- "$path" "$BACKUP" cp -- "$BACKUP" "$path" diff --git a/git-pull.sh b/git-pull.sh index 75ec011969..698e82b116 100755 --- a/git-pull.sh +++ b/git-pull.sh @@ -7,6 +7,7 @@ USAGE='[-n | --no-summary] [--[no-]commit] [--[no-]squash] [--[no-]ff] [-s strategy]... [<fetch-options>] <repo> <head>...' LONG_USAGE='Fetch one or more remote refs and merge it/them into the current HEAD.' SUBDIRECTORY_OK=Yes +OPTIONS_SPEC= . git-sh-setup set_reflog_action "pull $*" require_work_tree @@ -16,6 +17,9 @@ test -z "$(git ls-files -u)" || die "You are in the middle of a conflicted merge." strategy_args= no_summary= no_commit= squash= no_ff= +curr_branch=$(git symbolic-ref -q HEAD) +curr_branch_short=$(echo "$curr_branch" | sed "s|refs/heads/||") +rebase=$(git config --bool branch.$curr_branch_short.rebase) while : do case "$1" in @@ -51,6 +55,12 @@ do esac strategy_args="${strategy_args}-s $strategy " ;; + -r|--r|--re|--reb|--reba|--rebas|--rebase) + rebase=true + ;; + --no-r|--no-re|--no-reb|--no-reba|--no-rebas|--no-rebase) + rebase=false + ;; -h|--h|--he|--hel|--help) usage ;; @@ -94,7 +104,6 @@ merge_head=$(sed -e '/ not-for-merge /d' \ case "$merge_head" in '') - curr_branch=$(git symbolic-ref -q HEAD) case $? in 0) ;; 1) echo >&2 "You are not currently on a branch; you must explicitly" @@ -141,5 +150,6 @@ then fi merge_name=$(git fmt-merge-msg <"$GIT_DIR/FETCH_HEAD") || exit +test true = "$rebase" && exec git-rebase $merge_head exec git-merge $no_summary $no_commit $squash $no_ff $strategy_args \ "$merge_name" HEAD $merge_head diff --git a/git-quiltimport.sh b/git-quiltimport.sh index 880c81d121..233e5eae1d 100755 --- a/git-quiltimport.sh +++ b/git-quiltimport.sh @@ -1,5 +1,12 @@ #!/bin/sh -USAGE='--dry-run --author <author> --patches </path/to/quilt/patch/directory>' +OPTIONS_KEEPDASHDASH= +OPTIONS_SPEC="\ +git-quiltimport [options] +-- +n,dry-run dry run +author= author name and email address for patches without any +patches= path to the quilt series and patches +" SUBDIRECTORY_ON=Yes . git-sh-setup @@ -8,39 +15,25 @@ quilt_author="" while test $# != 0 do case "$1" in - --au=*|--aut=*|--auth=*|--autho=*|--author=*) - quilt_author=$(expr "z$1" : 'z-[^=]*\(.*\)') - shift - ;; - - --au|--aut|--auth|--autho|--author) - case "$#" in 1) usage ;; esac + --author) shift quilt_author="$1" - shift ;; - - --dry-run) - shift + -n|--dry-run) dry_run=1 ;; - - --pa=*|--pat=*|--patc=*|--patch=*|--patche=*|--patches=*) - QUILT_PATCHES=$(expr "z$1" : 'z-[^=]*\(.*\)') - shift - ;; - - --pa|--pat|--patc|--patch|--patche|--patches) - case "$#" in 1) usage ;; esac + --patches) shift QUILT_PATCHES="$1" - shift ;; - + --) + shift + break;; *) - break + usage ;; esac + shift done # Quilt Author @@ -84,8 +77,9 @@ for patch_name in $(grep -v '^#' < "$QUILT_PATCHES/series" ); do } # Parse the author information - export GIT_AUTHOR_NAME=$(sed -ne 's/Author: //p' "$tmp_info") - export GIT_AUTHOR_EMAIL=$(sed -ne 's/Email: //p' "$tmp_info") + GIT_AUTHOR_NAME=$(sed -ne 's/Author: //p' "$tmp_info") + GIT_AUTHOR_EMAIL=$(sed -ne 's/Email: //p' "$tmp_info") + export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL while test -z "$GIT_AUTHOR_EMAIL" && test -z "$GIT_AUTHOR_NAME" ; do if [ -n "$quilt_author" ] ; then GIT_AUTHOR_NAME="$quilt_author_name"; @@ -111,8 +105,9 @@ for patch_name in $(grep -v '^#' < "$QUILT_PATCHES/series" ); do GIT_AUTHOR_EMAIL="$patch_author_email" fi done - export GIT_AUTHOR_DATE=$(sed -ne 's/Date: //p' "$tmp_info") - export SUBJECT=$(sed -ne 's/Subject: //p' "$tmp_info") + GIT_AUTHOR_DATE=$(sed -ne 's/Date: //p' "$tmp_info") + SUBJECT=$(sed -ne 's/Subject: //p' "$tmp_info") + export GIT_AUTHOR_DATE SUBJECT if [ -z "$SUBJECT" ] ; then SUBJECT=$(echo $patch_name | sed -e 's/.patch$//') fi diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index 76dc679e62..f83e00fe8f 100755 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -13,6 +13,7 @@ USAGE='(--continue | --abort | --skip | [--preserve-merges] [--verbose] [--onto <branch>] <upstream> [<branch>])' +OPTIONS_SPEC= . git-sh-setup require_work_tree @@ -29,6 +30,11 @@ test -d "$REWRITTEN" && PRESERVE_MERGES=t test -f "$DOTEST"/strategy && STRATEGY="$(cat "$DOTEST"/strategy)" test -f "$DOTEST"/verbose && VERBOSE=t +GIT_CHERRY_PICK_HELP=" After resolving the conflicts, +mark the corrected paths with 'git add <paths>', and +run 'git rebase --continue'" +export GIT_CHERRY_PICK_HELP + warn () { echo "$*" >&2 } @@ -52,7 +58,7 @@ require_clean_work_tree () { git rev-parse --verify HEAD > /dev/null && git update-index --refresh && git diff-files --quiet && - git diff-index --cached --quiet HEAD || + git diff-index --cached --quiet HEAD -- || die "Working tree is dirty" } @@ -89,6 +95,7 @@ make_patch () { die_with_patch () { make_patch "$1" + git rerere die "$2" } @@ -174,13 +181,13 @@ pick_one_preserving_merges () { msg="$(git cat-file commit $sha1 | sed -e '1,/^$/d')" # No point in merging the first parent, that's HEAD new_parents=${new_parents# $first_parent} - # NEEDSWORK: give rerere a chance if ! GIT_AUTHOR_NAME="$GIT_AUTHOR_NAME" \ GIT_AUTHOR_EMAIL="$GIT_AUTHOR_EMAIL" \ GIT_AUTHOR_DATE="$GIT_AUTHOR_DATE" \ output git merge $STRATEGY -m "$msg" \ $new_parents then + git rerere printf "%s\n" "$msg" > "$GIT_DIR"/MERGE_MSG die Error redoing merge $sha1 fi @@ -355,7 +362,7 @@ do git rev-parse --verify HEAD > /dev/null && git update-index --refresh && git diff-files --quiet && - ! git diff-index --cached --quiet HEAD && + ! git diff-index --cached --quiet HEAD -- && . "$DOTEST"/author-script && { test ! -f "$DOTEST"/amend || git reset --soft HEAD^ } && @@ -368,6 +375,7 @@ do --abort) comment_for_reflog abort + git rerere clear test -d "$DOTEST" || die "No interactive rebase running" HEADNAME=$(cat "$DOTEST"/head-name) @@ -384,6 +392,7 @@ do --skip) comment_for_reflog skip + git rerere clear test -d "$DOTEST" || die "No interactive rebase running" output git reset --hard && do_rest @@ -391,7 +400,7 @@ do -s|--strategy) case "$#,$1" in *,*=*) - STRATEGY="-s `expr "z$1" : 'z-[^=]*=\(.*\)'`" ;; + STRATEGY="-s "$(expr "z$1" : 'z-[^=]*=\(.*\)') ;; 1,*) usage ;; *) @@ -484,8 +493,13 @@ do SHORTUPSTREAM=$(git rev-parse --short $UPSTREAM) SHORTHEAD=$(git rev-parse --short $HEAD) SHORTONTO=$(git rev-parse --short $ONTO) - cat > "$TODO" << EOF -# Rebasing $SHORTUPSTREAM..$SHORTHEAD onto $SHORTONTO + git rev-list $MERGES_OPTION --pretty=oneline --abbrev-commit \ + --abbrev=7 --reverse --left-right --cherry-pick \ + $UPSTREAM...$HEAD | \ + sed -n "s/^>/pick /p" > "$TODO" + cat >> "$TODO" << EOF + +# Rebase $SHORTUPSTREAM..$SHORTHEAD onto $SHORTONTO # # Commands: # pick = use commit @@ -493,12 +507,9 @@ do # squash = use commit, but meld into previous commit # # If you remove a line here THAT COMMIT WILL BE LOST. +# However, if you remove everything, the rebase will be aborted. # EOF - git rev-list $MERGES_OPTION --pretty=oneline --abbrev-commit \ - --abbrev=7 --reverse --left-right --cherry-pick \ - $UPSTREAM...$HEAD | \ - sed -n "s/^>/pick /p" >> "$TODO" has_action "$TODO" || die_abort "Nothing to do" diff --git a/git-rebase.sh b/git-rebase.sh index 224cca98ee..bdcea0ed70 100755 --- a/git-rebase.sh +++ b/git-rebase.sh @@ -29,6 +29,7 @@ Example: git-rebase master~1 topic ' SUBDIRECTORY_OK=Yes +OPTIONS_SPEC= . git-sh-setup set_reflog_action rebase require_work_tree @@ -60,7 +61,7 @@ continue_merge () { fi cmt=`cat "$dotest/current"` - if ! git diff-index --quiet HEAD + if ! git diff-index --quiet HEAD -- then if ! git-commit -C "$cmt" then @@ -87,7 +88,7 @@ call_merge () { cmt="$(cat "$dotest/cmt.$1")" echo "$cmt" > "$dotest/current" hd=$(git rev-parse --verify HEAD) - cmt_name=$(git symbolic-ref HEAD) + cmt_name=$(git symbolic-ref HEAD 2> /dev/null || echo HEAD) msgnum=$(cat "$dotest/msgnum") end=$(cat "$dotest/end") eval GITHEAD_$cmt='"${cmt_name##refs/heads/}~$(($end - $msgnum))"' @@ -115,7 +116,24 @@ call_merge () { esac } +move_to_original_branch () { + test -z "$head_name" && + head_name="$(cat "$dotest"/head-name)" && + onto="$(cat "$dotest"/onto)" && + orig_head="$(cat "$dotest"/orig-head)" + case "$head_name" in + refs/*) + message="rebase finished: $head_name onto $onto" + git update-ref -m "$message" \ + $head_name $(git rev-parse HEAD) $orig_head && + git symbolic-ref HEAD $head_name || + die "Could not move back to $head_name" + ;; + esac +} + finish_rb_merge () { + move_to_original_branch rm -r "$dotest" echo "All done." } @@ -153,10 +171,15 @@ do finish_rb_merge exit fi - git am --resolved --3way --resolvemsg="$RESOLVEMSG" + head_name=$(cat .dotest/head-name) && + onto=$(cat .dotest/onto) && + orig_head=$(cat .dotest/orig-head) && + git am --resolved --3way --resolvemsg="$RESOLVEMSG" && + move_to_original_branch exit ;; --skip) + git reset --hard HEAD || exit $? if test -d "$dotest" then git rerere clear @@ -173,16 +196,23 @@ do finish_rb_merge exit fi - git am -3 --skip --resolvemsg="$RESOLVEMSG" + head_name=$(cat .dotest/head-name) && + onto=$(cat .dotest/onto) && + orig_head=$(cat .dotest/orig-head) && + git am -3 --skip --resolvemsg="$RESOLVEMSG" && + move_to_original_branch exit ;; --abort) git rerere clear if test -d "$dotest" then + move_to_original_branch rm -r "$dotest" elif test -d .dotest then + dotest=.dotest + move_to_original_branch rm -r .dotest else die "No rebase in progress?" @@ -255,7 +285,7 @@ fi # The tree must be really really clean. git update-index --refresh || exit -diff=$(git diff-index --cached --name-status -r HEAD) +diff=$(git diff-index --cached --name-status -r HEAD --) case "$diff" in ?*) echo "cannot rebase: your index is not up-to-date" echo "$diff" @@ -318,6 +348,19 @@ then GIT_PAGER='' git diff --stat --summary "$mb" "$onto" fi +# move to a detached HEAD +orig_head=$(git rev-parse HEAD^0) +head_name=$(git symbolic-ref HEAD 2> /dev/null) +case "$head_name" in +'') + head_name="detached HEAD" + ;; +*) + git checkout "$orig_head" > /dev/null 2>&1 || + die "could not detach HEAD" + ;; +esac + # Rewind the head to "$onto"; this saves our current head in ORIG_HEAD. echo "First, rewinding head to replay your work on top of it..." git-reset --hard "$onto" @@ -327,14 +370,21 @@ git-reset --hard "$onto" if test "$mb" = "$branch" then echo >&2 "Fast-forwarded $branch_name to $onto_name." + move_to_original_branch exit 0 fi if test -z "$do_merge" then git format-patch -k --stdout --full-index --ignore-if-in-upstream "$upstream"..ORIG_HEAD | - git am $git_am_opt --binary -3 -k --resolvemsg="$RESOLVEMSG" - exit $? + git am $git_am_opt --binary -3 -k --resolvemsg="$RESOLVEMSG" && + move_to_original_branch + ret=$? + test 0 != $ret -a -d .dotest && + echo $head_name > .dotest/head-name && + echo $onto > .dotest/onto && + echo $orig_head > .dotest/orig-head + exit $ret fi # start doing a rebase with git-merge @@ -343,8 +393,10 @@ fi mkdir -p "$dotest" echo "$onto" > "$dotest/onto" echo "$onto_name" > "$dotest/onto_name" -prev_head=`git rev-parse HEAD^0` +prev_head=$orig_head echo "$prev_head" > "$dotest/prev_head" +echo "$orig_head" > "$dotest/orig-head" +echo "$head_name" > "$dotest/head-name" msgnum=0 for cmt in `git rev-list --reverse --no-merges "$upstream"..ORIG_HEAD` diff --git a/git-repack.sh b/git-repack.sh index 7220635c96..e18eb3f5dc 100755 --- a/git-repack.sh +++ b/git-repack.sh @@ -3,7 +3,22 @@ # Copyright (c) 2005 Linus Torvalds # -USAGE='[-a|-A] [-d] [-f] [-l] [-n] [-q] [--max-pack-size=N] [--window=N] [--window-memory=N] [--depth=N]' +OPTIONS_KEEPDASHDASH= +OPTIONS_SPEC="\ +git-repack [options] +-- +a pack everything in a single pack +A same as -a, and keep unreachable objects too +d remove redundant packs, and run git-prune-packed +f pass --no-reuse-delta to git-pack-objects +q,quiet be quiet +l pass --local to git-pack-objects + Packing constraints +window= size of the window used for delta compression +window-memory= same as the above, but limit memory size instead of entries count +depth= limits the maximum delta depth +max-pack-size= maximum size of each packfile +" SUBDIRECTORY_OK='Yes' . git-sh-setup @@ -20,10 +35,9 @@ do -q) quiet=-q ;; -f) no_reuse=--no-reuse-object ;; -l) local=--local ;; - --max-pack-size=*) extra="$extra $1" ;; - --window=*) extra="$extra $1" ;; - --window-memory=*) extra="$extra $1" ;; - --depth=*) extra="$extra $1" ;; + --max-pack-size|--window|--window-memory|--depth) + extra="$extra $1=$2"; shift ;; + --) shift; break;; *) usage ;; esac shift diff --git a/git-request-pull.sh b/git-request-pull.sh index a992430679..068f5e0fc7 100755 --- a/git-request-pull.sh +++ b/git-request-pull.sh @@ -8,6 +8,7 @@ USAGE='<commit> <url> [<head>]' LONG_USAGE='Summarizes the changes since <commit> to the standard output, and includes <url> in the message generated.' SUBDIRECTORY_OK='Yes' +OPTIONS_SPEC= . git-sh-setup . git-parse-remote @@ -24,13 +25,13 @@ headrev=`git rev-parse --verify "$head"^0` || exit merge_base=`git merge-base $baserev $headrev` || die "fatal: No commits in common between $base and $head" -url="`get_remote_url "$url"`" -branch=`git peek-remote "$url" \ +url=$(get_remote_url "$url") +branch=$(git peek-remote "$url" \ | sed -n -e "/^$headrev refs.heads./{ s/^.* refs.heads.// p q - }"` + }") if [ -z "$branch" ]; then echo "warn: No branch of $url is at:" >&2 git log --max-count=1 --pretty='format:warn: %h: %s' $headrev >&2 diff --git a/git-send-email.perl b/git-send-email.perl index 96051bc01e..76baa8e431 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -88,8 +88,7 @@ Options: --smtp-ssl If set, connects to the SMTP server using SSL. - --suppress-from Suppress sending emails to yourself if your address - appears in a From: line. Defaults to off. + --suppress-from Suppress sending emails to yourself. Defaults to off. --thread Specify that the "In-Reply-To:" header should be set on all emails. Defaults to on. @@ -146,6 +145,7 @@ sub format_2822_time { my $have_email_valid = eval { require Email::Valid; 1 }; my $smtp; +my $auth; sub unique_email_list(@); sub cleanup_compose_files(); @@ -353,7 +353,7 @@ sub expand_aliases { if (!defined $initial_subject && $compose) { do { - $_ = $term->readline("What subject should the emails start with? ", + $_ = $term->readline("What subject should the initial email start with? ", $initial_subject); } while (!defined $_); $initial_subject = $_; @@ -515,11 +515,13 @@ $time = time - scalar $#files; sub unquote_rfc2047 { local ($_) = @_; - if (s/=\?utf-8\?q\?(.*)\?=/$1/g) { + my $encoding; + if (s/=\?([^?]+)\?q\?(.*)\?=/$2/g) { + $encoding = $1; s/_/ /g; s/=([0-9A-F]{2})/chr(hex($1))/eg; } - return "$_"; + return wantarray ? ($_, $encoding) : $_; } # use the simplest quoting being able to handle the recipient @@ -556,7 +558,11 @@ sub sanitize_address sub send_message { my @recipients = unique_email_list(@to); - @cc = (map { sanitize_address($_) } @cc); + @cc = (grep { my $cc = extract_valid_address($_); + not grep { $cc eq $_ } @recipients + } + map { sanitize_address($_) } + @cc); my $to = join (",\n\t", @recipients); @recipients = unique_email_list(@recipients,@cc,@bcclist); @recipients = (map { extract_valid_address($_) } @recipients); @@ -630,7 +636,7 @@ X-Mailer: git-send-email $gitversion } if ((defined $smtp_authuser) && (defined $smtp_authpass)) { - $smtp->auth( $smtp_authuser, $smtp_authpass ) or die $smtp->message; + $auth ||= $smtp->auth( $smtp_authuser, $smtp_authpass ) or die $smtp->message; } $smtp->mail( $raw_from ) or die $smtp->message; $smtp->to( @recipients ) or die $smtp->message; @@ -642,7 +648,7 @@ X-Mailer: git-send-email $gitversion if ($quiet) { printf (($dry_run ? "Dry-" : "")."Sent %s\n", $subject); } else { - print (($dry_run ? "Dry-" : "")."OK. Log says:\nDate: $date\n"); + print (($dry_run ? "Dry-" : "")."OK. Log says:\n"); if ($smtp_server !~ m#^/#) { print "Server: $smtp_server\n"; print "MAIL FROM:<$raw_from>\n"; @@ -650,7 +656,7 @@ X-Mailer: git-send-email $gitversion } else { print "Sendmail: $smtp_server ".join(' ',@sendmail_parameters)."\n"; } - print "From: $sanitized_sender\nSubject: $subject\nCc: $cc\nTo: $to\n\n"; + print $header, "\n"; if ($smtp) { print "Result: ", $smtp->code, ' ', ($smtp->message =~ /\n([^\n]+\n)$/s), "\n"; @@ -668,6 +674,9 @@ foreach my $t (@files) { open(F,"<",$t) or die "can't open file $t"; my $author = undef; + my $author_encoding; + my $has_content_type; + my $body_encoding; @cc = @initial_cc; @xh = (); my $input_format = undef; @@ -693,12 +702,20 @@ foreach my $t (@files) { next if ($suppress_from); } elsif ($1 eq 'From') { - $author = unquote_rfc2047($2); + ($author, $author_encoding) + = unquote_rfc2047($2); } printf("(mbox) Adding cc: %s from line '%s'\n", $2, $_) unless $quiet; push @cc, $2; } + elsif (/^Content-type:/i) { + $has_content_type = 1; + if (/charset="?[^ "]+/) { + $body_encoding = $1; + } + push @xh, $_; + } elsif (!/^Date:\s/ && /^[-A-Za-z]+:\s+\S/) { push @xh, $_; } @@ -730,6 +747,7 @@ foreach my $t (@files) { if (/^(Signed-off-by|Cc): (.*)$/i && $signed_off_cc) { my $c = $2; chomp $c; + next if ($c eq $sender and $suppress_from); push @cc, $c; printf("(sob) Adding cc: %s from line '%s'\n", $c, $_) unless $quiet; @@ -745,6 +763,7 @@ foreach my $t (@files) { my $c = $_; $c =~ s/^\s*//g; $c =~ s/\n$//g; + next if ($c eq $sender and $suppress_from); push @cc, $c; printf("(cc-cmd) Adding cc: %s from: '%s'\n", $c, $cc_cmd) unless $quiet; @@ -755,6 +774,22 @@ foreach my $t (@files) { if (defined $author) { $message = "From: $author\n\n$message"; + if (defined $author_encoding) { + if ($has_content_type) { + if ($body_encoding eq $author_encoding) { + # ok, we already have the right encoding + } + else { + # uh oh, we should re-encode + } + } + else { + push @xh, + 'MIME-Version: 1.0', + "Content-Type: text/plain; charset=$author_encoding", + 'Content-Transfer-Encoding: 8bit'; + } + } } send_message(); diff --git a/git-sh-setup.sh b/git-sh-setup.sh index 86d7d4c4e7..5aa62dda15 100755 --- a/git-sh-setup.sh +++ b/git-sh-setup.sh @@ -16,9 +16,40 @@ die() { exit 1 } -usage() { - die "Usage: $0 $USAGE" -} +if test -n "$OPTIONS_SPEC"; then + usage() { + exec "$0" -h + } + + parseopt_extra= + [ -n "$OPTIONS_KEEPDASHDASH" ] && + parseopt_extra="--keep-dashdash" + + eval "$( + echo "$OPTIONS_SPEC" | + git rev-parse --parseopt $parseopt_extra -- "$@" || + echo exit $? + )" +else + usage() { + die "Usage: $0 $USAGE" + } + + if [ -z "$LONG_USAGE" ] + then + LONG_USAGE="Usage: $0 $USAGE" + else + LONG_USAGE="Usage: $0 $USAGE + +$LONG_USAGE" + fi + + case "$1" in + -h|--h|--he|--hel|--help) + echo "$LONG_USAGE" + exit + esac +fi set_reflog_action() { if [ -z "${GIT_REFLOG_ACTION:+set}" ] @@ -91,21 +122,6 @@ get_author_ident_from_commit () { LANG=C LC_ALL=C sed -ne "$pick_author_script" } -if [ -z "$LONG_USAGE" ] -then - LONG_USAGE="Usage: $0 $USAGE" -else - LONG_USAGE="Usage: $0 $USAGE - -$LONG_USAGE" -fi - -case "$1" in - -h|--h|--he|--hel|--help) - echo "$LONG_USAGE" - exit -esac - # Make sure we are in a valid repository of a vintage we understand. if [ -z "$SUBDIRECTORY_OK" ] then diff --git a/git-stash.sh b/git-stash.sh index 5bbda47b7b..f16fd9c3c0 100755 --- a/git-stash.sh +++ b/git-stash.sh @@ -1,9 +1,10 @@ #!/bin/sh # Copyright (c) 2007, Nanako Shiraishi -USAGE='[ | list | show | apply | clear]' +USAGE='[ | save | list | show | apply | clear | create ]' SUBDIRECTORY_OK=Yes +OPTIONS_SPEC= . git-sh-setup require_work_tree cd_to_toplevel @@ -14,35 +15,29 @@ trap 'rm -f "$TMP-*"' 0 ref_stash=refs/stash no_changes () { - git diff-index --quiet --cached HEAD && + git diff-index --quiet --cached HEAD -- && git diff-files --quiet } clear_stash () { if current=$(git rev-parse --verify $ref_stash 2>/dev/null) then - git update-ref -d refs/stash $current + git update-ref -d $ref_stash $current fi } -save_stash () { +create_stash () { stash_msg="$1" if no_changes then - echo >&2 'No local changes to save' exit 0 fi - test -f "$GIT_DIR/logs/$ref_stash" || - clear_stash || die "Cannot initialize stash" - - # Make sure the reflog for stash is kept. - : >>"$GIT_DIR/logs/$ref_stash" # state of the base commit if b_commit=$(git rev-parse --verify HEAD) then - head=$(git log --abbrev-commit --pretty=oneline -n 1 HEAD) + head=$(git log --no-color --abbrev-commit --pretty=oneline -n 1 HEAD --) else die "You do not have the initial commit yet" fi @@ -84,6 +79,23 @@ save_stash () { w_commit=$(printf '%s\n' "$stash_msg" | git commit-tree $w_tree -p $b_commit -p $i_commit) || die "Cannot record working tree state" +} + +save_stash () { + stash_msg="$1" + + if no_changes + then + echo >&2 'No local changes to save' + exit 0 + fi + test -f "$GIT_DIR/logs/$ref_stash" || + clear_stash || die "Cannot initialize stash" + + create_stash "$stash_msg" + + # Make sure the reflog for stash is kept. + : >>"$GIT_DIR/logs/$ref_stash" git update-ref -m "$stash_msg" $ref_stash $w_commit || die "Cannot save the current status" @@ -96,7 +108,7 @@ have_stash () { list_stash () { have_stash || return 0 - git log --pretty=oneline -g "$@" $ref_stash | + git log --no-color --pretty=oneline -g "$@" $ref_stash -- | sed -n -e 's/^[.0-9a-f]* refs\///p' } @@ -195,6 +207,10 @@ show) shift show_stash "$@" ;; +save) + shift + save_stash "$*" && git-reset --hard + ;; apply) shift apply_stash "$@" @@ -202,14 +218,19 @@ apply) clear) clear_stash ;; -help | usage) - usage +create) + if test $# -gt 0 && test "$1" = create + then + shift + fi + create_stash "$*" && echo "$w_commit" ;; *) - if test $# -gt 0 && test "$1" = save + if test $# -eq 0 then - shift + save_stash && git-reset --hard + else + usage fi - save_stash "$*" && git-reset --hard ;; esac diff --git a/git-submodule.sh b/git-submodule.sh index 4aaaaab0d8..ad9fe628fd 100755 --- a/git-submodule.sh +++ b/git-submodule.sh @@ -5,6 +5,7 @@ # Copyright (c) 2007 Lars Hjemli USAGE='[--quiet] [--cached] [add <repo> [-b branch]|status|init|update] [--] [<path>...]' +OPTIONS_SPEC= . git-sh-setup require_work_tree @@ -73,7 +74,7 @@ resolve_relative_url () module_name() { # Do we have "submodule.<something>.path = $1" defined in .gitmodules file? - re=$(printf '%s' "$1" | sed -e 's/\([^a-zA-Z0-9_]\)/\\\1/g') + re=$(printf '%s' "$1" | sed -e 's/[].[^$\\*]/\\&/g') name=$( GIT_CONFIG=.gitmodules \ git config --get-regexp '^submodule\..*\.path$' | sed -n -e 's|^submodule\.\(.*\)\.path '"$re"'$|\1|p' ) @@ -157,7 +158,7 @@ module_add() die "'$path' already exists in the index" module_clone "$path" "$realrepo" || exit - (unset GIT_DIR && cd "$path" && git checkout -q ${branch:+-b "$branch" "origin/$branch"}) || + (unset GIT_DIR; cd "$path" && git checkout -q ${branch:+-b "$branch" "origin/$branch"}) || die "Unable to checkout submodule '$path'" git add "$path" || die "Failed to add submodule '$path'" @@ -227,14 +228,14 @@ modules_update() module_clone "$path" "$url" || exit subsha1= else - subsha1=$(unset GIT_DIR && cd "$path" && + subsha1=$(unset GIT_DIR; cd "$path" && git rev-parse --verify HEAD) || die "Unable to find current revision in submodule path '$path'" fi if test "$subsha1" != "$sha1" then - (unset GIT_DIR && cd "$path" && git-fetch && + (unset GIT_DIR; cd "$path" && git-fetch && git-checkout -q "$sha1") || die "Unable to checkout '$sha1' in submodule path '$path'" @@ -245,7 +246,7 @@ modules_update() set_name_rev () { revname=$( ( - unset GIT_DIR && + unset GIT_DIR cd "$1" && { git describe "$2" 2>/dev/null || git describe --tags "$2" 2>/dev/null || @@ -284,7 +285,7 @@ modules_list() else if test -z "$cached" then - sha1=$(unset GIT_DIR && cd "$path" && git rev-parse --verify HEAD) + sha1=$(unset GIT_DIR; cd "$path" && git rev-parse --verify HEAD) set_name_rev "$path" "$sha1" fi say "+$sha1 $path$revname" diff --git a/git-svn.perl b/git-svn.perl index 22bb47b34d..9f884eb213 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -35,6 +35,7 @@ push @Git::SVN::Ra::ISA, 'SVN::Ra'; push @SVN::Git::Editor::ISA, 'SVN::Delta::Editor'; push @SVN::Git::Fetcher::ISA, 'SVN::Delta::Editor'; use Carp qw/croak/; +use Digest::MD5; use IO::File qw//; use File::Basename qw/dirname basename/; use File::Path qw/mkpath/; @@ -64,7 +65,7 @@ my ($_stdin, $_help, $_edit, $_template, $_shared, $_version, $_fetch_all, $_no_rebase, $_merge, $_strategy, $_dry_run, $_local, - $_prefix, $_no_checkout, $_verbose); + $_prefix, $_no_checkout, $_url, $_verbose); $Git::SVN::_follow_parent = 1; my %remote_opts = ( 'username=s' => \$Git::SVN::Prompt::_username, 'config-dir=s' => \$Git::SVN::Ra::config_dir, @@ -80,6 +81,7 @@ my %fc_opts = ( 'follow-parent|follow!' => \$Git::SVN::_follow_parent, 'quiet|q' => \$_q, 'repack-flags|repack-args|repack-opts=s' => \$Git::SVN::_repack_flags, + 'use-log-author' => \$Git::SVN::_use_log_author, %remote_opts ); my ($_trunk, $_tags, $_branches, $_stdlayout); @@ -141,6 +143,9 @@ my %cmd = ( 'show-ignore' => [ \&cmd_show_ignore, "Show svn:ignore listings", { 'revision|r=i' => \$_revision } ], + 'show-externals' => [ \&cmd_show_externals, "Show svn:externals listings", + { 'revision|r=i' => \$_revision + } ], 'multi-fetch' => [ \&cmd_multi_fetch, "Deprecated alias for $0 fetch --all", { 'revision|r=s' => \$_revision, %fc_opts } ], @@ -177,6 +182,10 @@ my %cmd = ( 'file|F=s' => \$_file, 'revision|r=s' => \$_revision, %cmt_opts } ], + 'info' => [ \&cmd_info, + "Show info about the latest SVN revision + on the current branch", + { 'url' => \$_url, } ], ); my $cmd; @@ -188,23 +197,6 @@ for (my $i = 0; $i < @ARGV; $i++) { } }; -my %opts = %{$cmd{$cmd}->[2]} if (defined $cmd); - -read_repo_config(\%opts); -Getopt::Long::Configure('pass_through') if ($cmd && $cmd eq 'log'); -my $rv = GetOptions(%opts, 'help|H|h' => \$_help, 'version|V' => \$_version, - 'minimize-connections' => \$Git::SVN::Migration::_minimize, - 'id|i=s' => \$Git::SVN::default_ref_id, - 'svn-remote|remote|R=s' => sub { - $Git::SVN::no_reuse_existing = 1; - $Git::SVN::default_repo_id = $_[1] }); -exit 1 if (!$rv && $cmd && $cmd ne 'log'); - -usage(0) if $_help; -version() if $_version; -usage(1) unless defined $cmd; -load_authors() if $_authors; - # make sure we're always running unless ($cmd =~ /(?:clone|init|multi-init)$/) { unless (-d $ENV{GIT_DIR}) { @@ -226,6 +218,24 @@ unless ($cmd =~ /(?:clone|init|multi-init)$/) { $ENV{GIT_DIR} = $git_dir; } } + +my %opts = %{$cmd{$cmd}->[2]} if (defined $cmd); + +read_repo_config(\%opts); +Getopt::Long::Configure('pass_through') if ($cmd && $cmd eq 'log'); +my $rv = GetOptions(%opts, 'help|H|h' => \$_help, 'version|V' => \$_version, + 'minimize-connections' => \$Git::SVN::Migration::_minimize, + 'id|i=s' => \$Git::SVN::default_ref_id, + 'svn-remote|remote|R=s' => sub { + $Git::SVN::no_reuse_existing = 1; + $Git::SVN::default_repo_id = $_[1] }); +exit 1 if (!$rv && $cmd && $cmd ne 'log'); + +usage(0) if $_help; +version() if $_version; +usage(1) unless defined $cmd; +load_authors() if $_authors; + unless ($cmd =~ /^(?:clone|init|multi-init|commit-diff)$/) { Git::SVN::Migration::migration_check(); } @@ -252,7 +262,7 @@ Usage: $0 <command> [options] [arguments]\n next if $cmd && $cmd ne $_; next if /^multi-/; # don't show deprecated commands print $fd ' ',pack('A17',$_),$cmd{$_}->[1],"\n"; - foreach (keys %{$cmd{$_}->[2]}) { + foreach (sort keys %{$cmd{$_}->[2]}) { # mixed-case options are for .git/config only next if /[A-Z]/ && /^[a-z]+$/i; # prints out arguments as they should be passed: @@ -390,6 +400,9 @@ sub cmd_set_tree { sub cmd_dcommit { my $head = shift; + git_cmd_try { command_oneline(qw/diff-index --quiet HEAD/) } + 'Cannot dcommit with a dirty index. Commit your changes first, ' + . "or stash them with `git stash'.\n"; $head ||= 'HEAD'; my @refs; my ($url, $rev, $uuid, $gs) = working_head_info($head, \@refs); @@ -406,7 +419,8 @@ sub cmd_dcommit { "If these changes depend on each other, re-running ", "without --no-rebase will be required." } - foreach my $d (@$linear_refs) { + while (1) { + my $d = shift @$linear_refs or last; unless (defined $last_rev) { (undef, $last_rev, undef) = cmt_metadata("$d~1"); unless (defined $last_rev) { @@ -421,6 +435,9 @@ sub cmd_dcommit { my %ed_opts = ( r => $last_rev, log => get_commit_entry($d)->{log}, ra => Git::SVN::Ra->new($gs->full_url), + config => SVN::Core::config_get_config( + $Git::SVN::Ra::config_dir + ), tree_a => "$d~1", tree_b => $d, editor_cb => sub { @@ -439,14 +456,14 @@ sub cmd_dcommit { # we always want to rebase against the current HEAD, # not any head that was passed to us - my @diff = command('diff-tree', 'HEAD', + my @diff = command('diff-tree', $d, $gs->refname, '--'); my @finish; if (@diff) { @finish = rebase_cmd(); - print STDERR "W: HEAD and ", $gs->refname, + print STDERR "W: $d and ", $gs->refname, " differ, using @finish:\n", - "@diff"; + join("\n", @diff), "\n"; } else { print "No changes between current HEAD and ", $gs->refname, @@ -455,6 +472,45 @@ sub cmd_dcommit { @finish = qw/reset --mixed/; } command_noisy(@finish, $gs->refname); + if (@diff) { + @refs = (); + my ($url_, $rev_, $uuid_, $gs_) = + working_head_info($head, \@refs); + my ($linear_refs_, $parents_) = + linearize_history($gs_, \@refs); + if (scalar(@$linear_refs) != + scalar(@$linear_refs_)) { + fatal "# of revisions changed ", + "\nbefore:\n", + join("\n", @$linear_refs), + "\n\nafter:\n", + join("\n", @$linear_refs_), "\n", + 'If you are attempting to commit ', + "merges, try running:\n\t", + 'git rebase --interactive', + '--preserve-merges ', + $gs->refname, + "\nBefore dcommitting"; + } + if ($url_ ne $url) { + fatal "URL mismatch after rebase: ", + "$url_ != $url"; + } + if ($uuid_ ne $uuid) { + fatal "uuid mismatch after rebase: ", + "$uuid_ != $uuid"; + } + # remap parents + my (%p, @l, $i); + for ($i = 0; $i < scalar @$linear_refs; $i++) { + my $new = $linear_refs_->[$i] or next; + $p{$new} = + $parents->{$linear_refs->[$i]}; + push @l, $new; + } + $parents = \%p; + $linear_refs = \@l; + } $last_rev = $cmt_rev; } } @@ -494,6 +550,8 @@ sub cmd_rebase { exit 1; } unless ($_local) { + # rebase will checkout for us, so no need to do it explicitly + $_no_checkout = 'true'; $_fetch_all ? $gs->fetch_all : $gs->fetch; } command_noisy(rebase_cmd(), $gs->refname); @@ -514,6 +572,21 @@ sub cmd_show_ignore { }); } +sub cmd_show_externals { + my ($url, $rev, $uuid, $gs) = working_head_info('HEAD'); + $gs ||= Git::SVN->new; + my $r = (defined $_revision ? $_revision : $gs->ra->get_latest_revnum); + $gs->prop_walk($gs->{path}, $r, sub { + my ($gs, $path, $props) = @_; + print STDOUT "\n# $path\n"; + my $s = $props->{'svn:externals'} or return; + $s =~ s/[\r\n]+/\n/g; + chomp $s; + $s =~ s#^#$path#gm; + print STDOUT "$s\n"; + }); +} + sub cmd_create_ignore { my ($url, $rev, $uuid, $gs) = working_head_info('HEAD'); $gs ||= Git::SVN->new; @@ -537,6 +610,23 @@ sub cmd_create_ignore { }); } +sub canonicalize_path { + my ($path) = @_; + my $dot_slash_added = 0; + if (substr($path, 0, 1) ne "/") { + $path = "./" . $path; + $dot_slash_added = 1; + } + # File::Spec->canonpath doesn't collapse x/../y into y (for a + # good reason), so let's do this manually. + $path =~ s#/+#/#g; + $path =~ s#/\.(?:/|$)#/#g; + $path =~ s#/[^/]+/\.\.##g; + $path =~ s#/$##g; + $path =~ s#^\./## if $dot_slash_added; + return $path; +} + # get_svnprops(PATH) # ------------------ # Helper for cmd_propget and cmd_proplist below. @@ -554,12 +644,7 @@ sub get_svnprops { # canonicalize the path (otherwise libsvn will abort or fail to # find the file) - # File::Spec->canonpath doesn't collapse x/../y into y (for a - # good reason), so let's do this manually. - $path =~ s#/+#/#g; - $path =~ s#/\.(?:/|$)#/#g; - $path =~ s#/[^/]+/\.\.##g; - $path =~ s#/$##g; + $path = canonicalize_path($path); my $r = (defined $_revision ? $_revision : $gs->ra->get_latest_revnum); my $props; @@ -690,6 +775,114 @@ sub cmd_commit_diff { } } +sub cmd_info { + my $path = canonicalize_path(shift or "."); + unless (scalar(@_) == 0) { + die "Too many arguments specified\n"; + } + + my ($file_type, $diff_status) = find_file_type_and_diff_status($path); + + if (!$file_type && !$diff_status) { + print STDERR "$path: (Not a versioned resource)\n\n"; + return; + } + + my ($url, $rev, $uuid, $gs) = working_head_info('HEAD'); + unless ($gs) { + die "Unable to determine upstream SVN information from ", + "working tree history\n"; + } + my $full_url = $url . ($path eq "." ? "" : "/$path"); + + if ($_url) { + print $full_url, "\n"; + return; + } + + my $result = "Path: $path\n"; + $result .= "Name: " . basename($path) . "\n" if $file_type ne "dir"; + $result .= "URL: " . $full_url . "\n"; + + eval { + my $repos_root = $gs->repos_root; + Git::SVN::remove_username($repos_root); + $result .= "Repository Root: $repos_root\n"; + }; + if ($@) { + $result .= "Repository Root: (offline)\n"; + } + $result .= "Repository UUID: $uuid\n" unless $diff_status eq "A"; + $result .= "Revision: " . ($diff_status eq "A" ? 0 : $rev) . "\n"; + + $result .= "Node Kind: " . + ($file_type eq "dir" ? "directory" : "file") . "\n"; + + my $schedule = $diff_status eq "A" + ? "add" + : ($diff_status eq "D" ? "delete" : "normal"); + $result .= "Schedule: $schedule\n"; + + if ($diff_status eq "A") { + print $result, "\n"; + return; + } + + my ($lc_author, $lc_rev, $lc_date_utc); + my @args = Git::SVN::Log::git_svn_log_cmd($rev, $rev, "--", $path); + my $log = command_output_pipe(@args); + my $esc_color = qr/(?:\033\[(?:(?:\d+;)*\d*)?m)*/; + while (<$log>) { + if (/^${esc_color}author (.+) <[^>]+> (\d+) ([\-\+]?\d+)$/o) { + $lc_author = $1; + $lc_date_utc = Git::SVN::Log::parse_git_date($2, $3); + } elsif (/^${esc_color} (git-svn-id:.+)$/o) { + (undef, $lc_rev, undef) = ::extract_metadata($1); + } + } + close $log; + + Git::SVN::Log::set_local_timezone(); + + $result .= "Last Changed Author: $lc_author\n"; + $result .= "Last Changed Rev: $lc_rev\n"; + $result .= "Last Changed Date: " . + Git::SVN::Log::format_svn_date($lc_date_utc) . "\n"; + + if ($file_type ne "dir") { + my $text_last_updated_date = + ($diff_status eq "D" ? $lc_date_utc : (stat $path)[9]); + $result .= + "Text Last Updated: " . + Git::SVN::Log::format_svn_date($text_last_updated_date) . + "\n"; + my $checksum; + if ($diff_status eq "D") { + my ($fh, $ctx) = + command_output_pipe(qw(cat-file blob), "HEAD:$path"); + if ($file_type eq "link") { + my $file_name = <$fh>; + $checksum = md5sum("link $file_name"); + } else { + $checksum = md5sum($fh); + } + command_close_pipe($fh, $ctx); + } elsif ($file_type eq "link") { + my $file_name = + command(qw(cat-file blob), "HEAD:$path"); + $checksum = + md5sum("link " . $file_name); + } else { + open FILE, "<", $path or die $!; + $checksum = md5sum(\*FILE); + close FILE or die $!; + } + $result .= "Checksum: " . $checksum . "\n"; + } + + print $result, "\n"; +} + ########################### utility functions ######################### sub rebase_cmd { @@ -997,12 +1190,54 @@ sub linearize_history { (\@linear_refs, \%parents); } +sub find_file_type_and_diff_status { + my ($path) = @_; + return ('dir', '') if $path eq '.'; + + my $diff_output = + command_oneline(qw(diff --cached --name-status --), $path) || ""; + my $diff_status = (split(' ', $diff_output))[0] || ""; + + my $ls_tree = command_oneline(qw(ls-tree HEAD), $path) || ""; + + return (undef, undef) if !$diff_status && !$ls_tree; + + if ($diff_status eq "A") { + return ("link", $diff_status) if -l $path; + return ("dir", $diff_status) if -d $path; + return ("file", $diff_status); + } + + my $mode = (split(' ', $ls_tree))[0] || ""; + + return ("link", $diff_status) if $mode eq "120000"; + return ("dir", $diff_status) if $mode eq "040000"; + return ("file", $diff_status); +} + +sub md5sum { + my $arg = shift; + my $ref = ref $arg; + my $md5 = Digest::MD5->new(); + if ($ref eq 'GLOB' || $ref eq 'IO::File') { + $md5->addfile($arg) or croak $!; + } elsif ($ref eq 'SCALAR') { + $md5->add($$arg) or croak $!; + } elsif (!$ref) { + $md5->add($arg) or croak $!; + } else { + ::fatal "Can't provide MD5 hash for unknown ref type: '", $ref, "'"; + } + return $md5->hexdigest(); +} + package Git::SVN; use strict; use warnings; use vars qw/$default_repo_id $default_ref_id $_no_metadata $_follow_parent $_repack $_repack_flags $_use_svm_props $_head - $_use_svnsync_props $no_reuse_existing $_minimize_url/; + $_use_svnsync_props $no_reuse_existing $_minimize_url + $_use_log_author/; use Carp qw/croak/; use File::Path qw/mkpath/; use File::Copy qw/copy/; @@ -1561,9 +1796,24 @@ sub ra_uuid { $self->{ra_uuid}; } +sub _set_repos_root { + my ($self, $repos_root) = @_; + my $k = "svn-remote.$self->{repo_id}.reposRoot"; + $repos_root ||= $self->ra->{repos_root}; + tmp_config($k, $repos_root); + $repos_root; +} + +sub repos_root { + my ($self) = @_; + my $k = "svn-remote.$self->{repo_id}.reposRoot"; + eval { tmp_config('--get', $k) } || $self->_set_repos_root; +} + sub ra { my ($self) = shift; my $ra = Git::SVN::Ra->new($self->{url}); + $self->_set_repos_root($ra->{repos_root}); if ($self->use_svm_props && !$self->{svm}) { if ($self->no_metadata) { die "Can't have both 'noMetadata' and ", @@ -1827,11 +2077,17 @@ sub do_git_commit { croak "$log_entry->{revision} = $c already exists! ", "Why are we refetching it?\n"; } - $ENV{GIT_AUTHOR_NAME} = $ENV{GIT_COMMITTER_NAME} = $log_entry->{name}; - $ENV{GIT_AUTHOR_EMAIL} = $ENV{GIT_COMMITTER_EMAIL} = - $log_entry->{email}; + $ENV{GIT_AUTHOR_NAME} = $log_entry->{name}; + $ENV{GIT_AUTHOR_EMAIL} = $log_entry->{email}; $ENV{GIT_AUTHOR_DATE} = $ENV{GIT_COMMITTER_DATE} = $log_entry->{date}; + $ENV{GIT_COMMITTER_NAME} = (defined $log_entry->{commit_name}) + ? $log_entry->{commit_name} + : $log_entry->{name}; + $ENV{GIT_COMMITTER_EMAIL} = (defined $log_entry->{commit_email}) + ? $log_entry->{commit_email} + : $log_entry->{email}; + my $tree = $log_entry->{tree}; if (!defined $tree) { $tree = $self->tmp_index_do(sub { @@ -2119,7 +2375,17 @@ sub make_log_entry { $log_entry{log} .= "\n"; my $author = $log_entry{author} = check_author($log_entry{author}); my ($name, $email) = defined $::users{$author} ? @{$::users{$author}} - : ($author, undef); + : ($author, undef); + + my ($commit_name, $commit_email) = ($name, $email); + if ($_use_log_author) { + if ($log_entry{log} =~ /From:\s+(.*?)\s+<(.*)>\s*\n/) { + ($name, $email) = ($1, $2); + } elsif ($log_entry{log} =~ + /Signed-off-by:\s+(.*?)\s+<(.*)>\s*\n/) { + ($name, $email) = ($1, $2); + } + } if (defined $headrev && $self->use_svm_props) { if ($self->rewrite_root) { die "Can't have both 'useSvmProps' and 'rewriteRoot' ", @@ -2142,23 +2408,28 @@ sub make_log_entry { remove_username($full_url); $log_entry{metadata} = "$full_url\@$r $uuid"; $log_entry{svm_revision} = $r; - $email ||= "$author\@$uuid" + $email ||= "$author\@$uuid"; + $commit_email ||= "$author\@$uuid"; } elsif ($self->use_svnsync_props) { my $full_url = $self->svnsync->{url}; $full_url .= "/$self->{path}" if length $self->{path}; remove_username($full_url); my $uuid = $self->svnsync->{uuid}; $log_entry{metadata} = "$full_url\@$rev $uuid"; - $email ||= "$author\@$uuid" + $email ||= "$author\@$uuid"; + $commit_email ||= "$author\@$uuid"; } else { my $url = $self->metadata_url; remove_username($url); $log_entry{metadata} = "$url\@$rev " . $self->ra->get_uuid; $email ||= "$author\@" . $self->ra->get_uuid; + $commit_email ||= "$author\@" . $self->ra->get_uuid; } $log_entry{name} = $name; $log_entry{email} = $email; + $log_entry{commit_name} = $commit_name; + $log_entry{commit_email} = $commit_email; \%log_entry; } @@ -2353,10 +2624,15 @@ sub rev_db_get { $ret; } +# Finds the first svn revision that exists on (if $eq_ok is true) or +# before $rev for the current branch. It will not search any lower +# than $min_rev. Returns the git commit hash and svn revision number +# if found, else (undef, undef). sub find_rev_before { - my ($self, $rev, $eq_ok) = @_; + my ($self, $rev, $eq_ok, $min_rev) = @_; --$rev unless $eq_ok; - while ($rev > 0) { + $min_rev ||= 1; + while ($rev >= $min_rev) { if (my $c = $self->rev_db_get($rev)) { return ($rev, $c); } @@ -2365,6 +2641,23 @@ sub find_rev_before { return (undef, undef); } +# Finds the first svn revision that exists on (if $eq_ok is true) or +# after $rev for the current branch. It will not search any higher +# than $max_rev. Returns the git commit hash and svn revision number +# if found, else (undef, undef). +sub find_rev_after { + my ($self, $rev, $eq_ok, $max_rev) = @_; + ++$rev unless $eq_ok; + $max_rev ||= $self->rev_db_max(); + while ($rev <= $max_rev) { + if (my $c = $self->rev_db_get($rev)) { + return ($rev, $c); + } + ++$rev; + } + return (undef, undef); +} + sub _new { my ($class, $repo_id, $ref_id, $path) = @_; unless (defined $repo_id && length $repo_id) { @@ -2542,7 +2835,6 @@ use strict; use warnings; use Carp qw/croak/; use IO::File qw//; -use Digest::MD5; # file baton members: path, mode_a, mode_b, pool, fh, blob, base sub new { @@ -2694,9 +2986,7 @@ sub apply_textdelta { if (defined $exp) { seek $base, 0, 0 or croak $!; - my $md5 = Digest::MD5->new; - $md5->addfile($base); - my $got = $md5->hexdigest; + my $got = ::md5sum($base); die "Checksum mismatch: $fb->{path} $fb->{blob}\n", "expected: $exp\n", " got: $got\n" if ($got ne $exp); @@ -2715,9 +3005,7 @@ sub close_file { if (my $fh = $fb->{fh}) { if (defined $exp) { seek($fh, 0, 0) or croak $!; - my $md5 = Digest::MD5->new; - $md5->addfile($fh); - my $got = $md5->hexdigest; + my $got = ::md5sum($fh); if ($got ne $exp) { die "Checksum mismatch: $path\n", "expected: $exp\n got: $got\n"; @@ -2769,7 +3057,6 @@ use strict; use warnings; use Carp qw/croak/; use IO::File; -use Digest::MD5; sub new { my ($class, $opts) = @_; @@ -3073,11 +3360,9 @@ sub chg_file { $fh->flush == 0 or croak $!; seek $fh, 0, 0 or croak $!; - my $md5 = Digest::MD5->new; - $md5->addfile($fh) or croak $!; + my $exp = ::md5sum($fh); seek $fh, 0, 0 or croak $!; - my $exp = $md5->hexdigest; my $pool = SVN::Pool->new; my $atd = $self->apply_textdelta($fbat, undef, $pool); my $got = SVN::TxDelta::send_stream($fh, @$atd, $pool); @@ -3180,6 +3465,25 @@ sub _auth_providers () { ] } +sub escape_uri_only { + my ($uri) = @_; + my @tmp; + foreach (split m{/}, $uri) { + s/([^\w.-])/sprintf("%%%02X",ord($1))/eg; + push @tmp, $_; + } + join('/', @tmp); +} + +sub escape_url { + my ($url) = @_; + if ($url =~ m#^(https?)://([^/]+)(.*)$#) { + my ($scheme, $domain, $uri) = ($1, $2, escape_uri_only($3)); + $url = "$scheme://$domain$uri"; + } + $url; +} + sub new { my ($class, $url) = @_; $url =~ s!/+$!!; @@ -3212,10 +3516,11 @@ sub new { $Git::SVN::Prompt::_no_auth_cache = 1; } } # no warnings 'once' - my $self = SVN::Ra->new(url => $url, auth => $baton, + my $self = SVN::Ra->new(url => escape_url($url), auth => $baton, config => $config, pool => SVN::Pool->new, auth_provider_callbacks => $callbacks); + $self->{url} = $url; $self->{svn_path} = $url; $self->{repos_root} = $self->get_repos_root; $self->{svn_path} =~ s#^\Q$self->{repos_root}\E(/|$)##; @@ -3341,7 +3646,7 @@ sub gs_do_switch { my $full_url = $self->{url}; my $old_url = $full_url; - $full_url .= "/$path" if length $path; + $full_url .= '/' . escape_uri_only($path) if length $path; my ($ra, $reparented); if ($old_url ne $full_url) { if ($old_url !~ m#^svn(\+ssh)?://#) { @@ -3635,6 +3940,7 @@ package Git::SVN::Log; use strict; use warnings; use POSIX qw/strftime/; +use constant commit_log_separator => ('-' x 72) . "\n"; use vars qw/$TZ $limit $color $pager $non_recursive $verbose $oneline %rusers $show_commit $incremental/; my $l_fmt; @@ -3728,19 +4034,19 @@ sub git_svn_log_cmd { push @cmd, $c; } } elsif (defined $r_max) { - my ($c_min, $c_max); - $c_max = $gs->rev_db_get($r_max); - $c_min = $gs->rev_db_get($r_min); - if (defined $c_min && defined $c_max) { - if ($r_max > $r_max) { - push @cmd, "$c_min..$c_max"; - } else { - push @cmd, "$c_max..$c_min"; - } - } elsif ($r_max > $r_min) { - push @cmd, $c_max; + if ($r_max < $r_min) { + ($r_min, $r_max) = ($r_max, $r_min); + } + my (undef, $c_max) = $gs->find_rev_before($r_max, 1, $r_min); + my (undef, $c_min) = $gs->find_rev_after($r_min, 1, $r_max); + # If there are no commits in the range, both $c_max and $c_min + # will be undefined. If there is at least 1 commit in the + # range, both will be defined. + return () if !defined $c_min || !defined $c_max; + if ($c_min eq $c_max) { + push @cmd, '--max-count=1', $c_min; } else { - push @cmd, $c_min; + push @cmd, '--boundary', "$c_min..$c_max"; } } return (@cmd, @files); @@ -3770,6 +4076,29 @@ sub run_pager { exec $pager or ::fatal "Can't run pager: $! ($pager)"; } +sub format_svn_date { + return strftime("%Y-%m-%d %H:%M:%S %z (%a, %d %b %Y)", localtime(shift)); +} + +sub parse_git_date { + my ($t, $tz) = @_; + # Date::Parse isn't in the standard Perl distro :( + if ($tz =~ s/^\+//) { + $t += tz_to_s_offset($tz); + } elsif ($tz =~ s/^\-//) { + $t -= tz_to_s_offset($tz); + } + return $t; +} + +sub set_local_timezone { + if (defined $TZ) { + $ENV{TZ} = $TZ; + } else { + delete $ENV{TZ}; + } +} + sub tz_to_s_offset { my ($tz) = @_; $tz =~ s/(\d\d)$//; @@ -3790,13 +4119,7 @@ sub get_author_info { $dest->{t} = $t; $dest->{tz} = $tz; $dest->{a} = $au; - # Date::Parse isn't in the standard Perl distro :( - if ($tz =~ s/^\+//) { - $t += tz_to_s_offset($tz); - } elsif ($tz =~ s/^\-//) { - $t -= tz_to_s_offset($tz); - } - $dest->{t_utc} = $t; + $dest->{t_utc} = parse_git_date($t, $tz); } sub process_commit { @@ -3848,10 +4171,9 @@ sub show_commit_changed_paths { sub show_commit_normal { my ($c) = @_; - print '-' x72, "\nr$c->{r} | "; + print commit_log_separator, "r$c->{r} | "; print "$c->{c} | " if $show_commit; - print "$c->{a} | ", strftime("%Y-%m-%d %H:%M:%S %z (%a, %d %b %Y)", - localtime($c->{t_utc})), ' | '; + print "$c->{a} | ", format_svn_date($c->{t_utc}), ' | '; my $nr_line = 0; if (my $l = $c->{l}) { @@ -3891,11 +4213,7 @@ sub cmd_show_log { my (@args) = @_; my ($r_min, $r_max); my $r_last = -1; # prevent dupes - if (defined $TZ) { - $ENV{TZ} = $TZ; - } else { - delete $ENV{TZ}; - } + set_local_timezone(); if (defined $::_revision) { if ($::_revision =~ /^(\d+):(\d+)$/) { ($r_min, $r_max) = ($1, $2); @@ -3909,12 +4227,16 @@ sub cmd_show_log { config_pager(); @args = git_svn_log_cmd($r_min, $r_max, @args); + if (!@args) { + print commit_log_separator unless $incremental || $oneline; + return; + } my $log = command_output_pipe(@args); run_pager(); my (@k, $c, $d, $stat); my $esc_color = qr/(?:\033\[(?:(?:\d+;)*\d*)?m)*/; while (<$log>) { - if (/^${esc_color}commit ($::sha1_short)/o) { + if (/^${esc_color}commit -?($::sha1_short)/o) { my $cmt = $1; if ($c && cmt_showable($c) && $c->{r} != $r_last) { $r_last = $c->{r}; @@ -3957,14 +4279,12 @@ sub cmd_show_log { process_commit($c, $r_min, $r_max, \@k); } if (@k) { - my $swap = $r_max; - $r_max = $r_min; - $r_min = $swap; + ($r_min, $r_max) = ($r_max, $r_min); process_commit($_, $r_min, $r_max) foreach reverse @k; } out: close $log; - print '-' x72,"\n" unless $incremental || $oneline; + print commit_log_separator unless $incremental || $oneline; } package Git::SVN::Migration; @@ -169,7 +169,7 @@ static int handle_alias(int *argcp, const char ***argv) strbuf_init(&buf, PATH_MAX); strbuf_addstr(&buf, alias_string); - sq_quote_argv(&buf, (*argv) + 1, *argcp - 1, PATH_MAX); + sq_quote_argv(&buf, (*argv) + 1, PATH_MAX); free(alias_string); alias_string = buf.buf; } @@ -198,7 +198,7 @@ static int handle_alias(int *argcp, const char ***argv) if (!strcmp(alias_command, new_argv[0])) die("recursive alias: %s", alias_command); - trace_argv_printf(new_argv, count, + trace_argv_printf(new_argv, "trace: alias expansion: %s =>", alias_command); @@ -249,19 +249,14 @@ static int run_command(struct cmd_struct *p, int argc, const char **argv) prefix = setup_git_directory(); if (p->option & USE_PAGER) setup_pager(); - if (p->option & NEED_WORK_TREE) { - const char *work_tree = get_git_work_tree(); - const char *git_dir = get_git_dir(); - if (!is_absolute_path(git_dir)) - set_git_dir(make_absolute_path(git_dir)); - if (!work_tree || chdir(work_tree)) - die("%s must be run in a work tree", p->cmd); - } - trace_argv_printf(argv, argc, "trace: built-in: git"); + if (p->option & NEED_WORK_TREE) + setup_work_tree(); + + trace_argv_printf(argv, "trace: built-in: git"); status = p->fn(argc, argv, prefix); if (status) - return status; + return status & 0xff; /* Somebody closed stdout? */ if (fstat(fileno(stdout), &st)) @@ -298,6 +293,8 @@ static void handle_internal_command(int argc, const char **argv) { "check-attr", cmd_check_attr, RUN_SETUP | NEED_WORK_TREE }, { "cherry", cmd_cherry, RUN_SETUP }, { "cherry-pick", cmd_cherry_pick, RUN_SETUP | NEED_WORK_TREE }, + { "clean", cmd_clean, RUN_SETUP | NEED_WORK_TREE }, + { "commit", cmd_commit, RUN_SETUP | NEED_WORK_TREE }, { "commit-tree", cmd_commit_tree, RUN_SETUP }, { "config", cmd_config }, { "count-objects", cmd_count_objects, RUN_SETUP }, @@ -306,6 +303,7 @@ static void handle_internal_command(int argc, const char **argv) { "diff-files", cmd_diff_files }, { "diff-index", cmd_diff_index, RUN_SETUP }, { "diff-tree", cmd_diff_tree, RUN_SETUP }, + { "fast-export", cmd_fast_export, RUN_SETUP }, { "fetch", cmd_fetch, RUN_SETUP }, { "fetch-pack", cmd_fetch_pack, RUN_SETUP }, { "fetch--tool", cmd_fetch__tool, RUN_SETUP }, @@ -326,13 +324,16 @@ static void handle_internal_command(int argc, const char **argv) { "log", cmd_log, RUN_SETUP | USE_PAGER }, { "ls-files", cmd_ls_files, RUN_SETUP }, { "ls-tree", cmd_ls_tree, RUN_SETUP }, + { "ls-remote", cmd_ls_remote }, { "mailinfo", cmd_mailinfo }, { "mailsplit", cmd_mailsplit }, { "merge-base", cmd_merge_base, RUN_SETUP }, { "merge-file", cmd_merge_file }, + { "merge-ours", cmd_merge_ours, RUN_SETUP }, { "mv", cmd_mv, RUN_SETUP | NEED_WORK_TREE }, { "name-rev", cmd_name_rev, RUN_SETUP }, { "pack-objects", cmd_pack_objects, RUN_SETUP }, + { "peek-remote", cmd_ls_remote }, { "pickaxe", cmd_blame, RUN_SETUP }, { "prune", cmd_prune, RUN_SETUP }, { "prune-packed", cmd_prune_packed, RUN_SETUP }, @@ -343,13 +344,14 @@ static void handle_internal_command(int argc, const char **argv) { "rerere", cmd_rerere, RUN_SETUP }, { "reset", cmd_reset, RUN_SETUP }, { "rev-list", cmd_rev_list, RUN_SETUP }, - { "rev-parse", cmd_rev_parse, RUN_SETUP }, + { "rev-parse", cmd_rev_parse }, { "revert", cmd_revert, RUN_SETUP | NEED_WORK_TREE }, - { "rm", cmd_rm, RUN_SETUP | NEED_WORK_TREE }, - { "runstatus", cmd_runstatus, RUN_SETUP | NEED_WORK_TREE }, + { "rm", cmd_rm, RUN_SETUP }, + { "send-pack", cmd_send_pack, RUN_SETUP }, { "shortlog", cmd_shortlog, RUN_SETUP | USE_PAGER }, { "show-branch", cmd_show_branch, RUN_SETUP }, { "show", cmd_show, RUN_SETUP | USE_PAGER }, + { "status", cmd_status, RUN_SETUP | NEED_WORK_TREE }, { "stripspace", cmd_stripspace }, { "symbolic-ref", cmd_symbolic_ref, RUN_SETUP }, { "tag", cmd_tag, RUN_SETUP }, diff --git a/gitk-git/Makefile b/gitk-git/Makefile new file mode 100644 index 0000000000..9bc1e24082 --- /dev/null +++ b/gitk-git/Makefile @@ -0,0 +1,29 @@ +# The default target of this Makefile is... +all:: + +prefix ?= $(HOME) +bindir ?= $(prefix)/bin +TCLTK_PATH ?= wish +INSTALL ?= install +RM ?= rm -f + +DESTDIR_SQ = $(subst ','\'',$(DESTDIR)) +bindir_SQ = $(subst ','\'',$(bindir)) +TCLTK_PATH_SQ = $(subst ','\'',$(TCLTK_PATH)) + +ifndef V + QUIET = @ + QUIET_GEN = $(QUIET)echo ' ' GEN $@ && +endif + +all:: gitk-wish +install:: all + $(INSTALL) gitk-wish '$(DESTDIR_SQ)$(bindir_SQ)'/gitk +clean:: + $(RM) gitk-wish + +gitk-wish: gitk + $(QUIET_GEN)$(RM) $@ $@+ && \ + sed -e '1,3s|^exec .* "$$0"|exec $(subst |,'\|',$(TCLTK_PATH_SQ)) "$$0"|' <gitk >$@+ && \ + chmod +x $@+ && \ + mv -f $@+ $@ diff --git a/gitk b/gitk-git/gitk index 1da0b0af1d..1da0b0af1d 100755..100644 --- a/gitk +++ b/gitk-git/gitk diff --git a/gitweb/README b/gitweb/README index 7186cede2f..b28f59f574 100644 --- a/gitweb/README +++ b/gitweb/README @@ -10,28 +10,96 @@ From the git version 1.4.0 gitweb is bundled with git. How to configure gitweb for your local system --------------------------------------------- +See also "Build time configuration" section in INSTALL +file for gitweb (in gitweb/INSTALL). + You can specify the following configuration variables when building GIT: + * GIT_BINDIR + Points out where to find git executable. You should set up it to + the place where git binary was installed (usually /usr/bin) if you + don't install git from sources together with gitweb. [Default: $(bindir)] * GITWEB_SITENAME - Shown in the title of all generated pages, defaults to the servers name. + Shown in the title of all generated pages, defaults to the server name + (SERVER_NAME CGI environment variable) if not set. [No default] * GITWEB_PROJECTROOT - The root directory for all projects shown by gitweb. + The root directory for all projects shown by gitweb. Must be set + correctly for gitweb to find repositories to display. See also + "Gitweb repositories" in INSTALL file for gitweb. [Default: /pub/git] + * GITWEB_PROJECT_MAXDEPTH + The filesystem traversing limit for getting projects list; the number + is taken as depth relative to the projectroot. It is used when + GITWEB_LIST is a directory (or is not set; then project root is used). + Is is meant to speed up project listing on large work trees by limiting + find depth. [Default: 2007] * GITWEB_LIST - points to a directory to scan for projects (defaults to project root) - or to a file for explicit listing of projects. + Points to a directory to scan for projects (defaults to project root + if not set / if empty) or to a file with explicit listing of projects + (together with projects' ownership). See "Generating projects list + using gitweb" in INSTALL file for gitweb to find out how to generate + such file from scan of a directory. [No default, which means use root + directory for projects] + * GITWEB_EXPORT_OK + Show repository only if this file exists (in repository). Only + effective if this variable evaluates to true. [No default / Not set] + * GITWEB_STRICT_EXPORT + Only allow viewing of repositories also shown on the overview page. + This for example makes GITWEB_EXPORT_OK to decide if repository is + available and not only if it is shown. If GITWEB_LIST points to + file with list of project, only those repositories listed would be + available for gitweb. [No default] * GITWEB_HOMETEXT - points to an .html file which is included on the gitweb project - overview page. + Points to an .html file which is included on the gitweb project + overview page ('projects_list' view), if it exists. Relative to + gitweb.cgi script. [Default: indextext.html] + * GITWEB_SITE_HEADER + Filename of html text to include at top of each page. Relative to + gitweb.cgi script. [No default] + * GITWEB_SITE_FOOTER + Filename of html text to include at bottom of each page. Relative to + gitweb.cgi script. [No default] + * GITWEB_HOME_LINK_STR + String of the home link on top of all pages, leading to $home_link + (usually main gitweb page, which means projects list). Used as first + part of gitweb view "breadcrumb trail": <home> / <project> / <view>. + [Default: projects] + * GITWEB_SITENAME + Name of your site or organization to appear in page titles. Set it + to something descriptive for clearer bookmarks etc. If not set + (if empty) gitweb uses "$SERVER_NAME Git", or "Untitled Git" if + SERVER_NAME CGI environment variable is not set (e.g. if running + gitweb as standalone script). [No default] + * GITWEB_BASE_URL + Git base URLs used for URL to where fetch project from, i.e. full + URL is "$git_base_url/$project". Shown on projects summary page. + Repository URL for project can be also configured per repository; this + takes precendence over URL composed from base URL and project name. + Note that you can setup multiple base URLs (for example one for + git:// protocol access, one for http:// access) from gitweb config + file. [No default] * GITWEB_CSS - Points to the location where you put gitweb.css on your web server. + Points to the location where you put gitweb.css on your web server + (or to be more generic URI of gitweb stylesheet). Relative to base + URI of gitweb. Note that you can setup multiple stylesheets from + gitweb config file. [Default: gitweb.css] * GITWEB_LOGO - Points to the location where you put git-logo.png on your web server. + Points to the location where you put git-logo.png on your web server + (or to be more generic URI of logo, 72x27 size, displayed in top right + corner of each gitweb page, and used as logo for Atom feed). Relative + to base URI of gitweb. [Default: git-logo.png] + * GITWEB_FAVICON + Points to the location where you put git-favicon.png on your web server + (or to be more generic URI of favicon, assumed to be image/png type; + web browsers that support favicons (website icons) may display them + in the browser's URL bar and next to site name in bookmarks). Relative + to base URI of gitweb. [Default: git-favicon.png] * GITWEB_CONFIG - This file will be loaded using 'require' and can be used to override any - of the options above as well as some other options - see the top of - 'gitweb.cgi' for their full list and description. If the environment - $GITWEB_CONFIG is set when gitweb.cgi is executed the file in the - environment variable will be loaded instead of the file - specified when gitweb.cgi was created. + This Perl file will be loaded using 'do' and can be used to override any + of the options above as well as some other options -- see the "Runtime + gitweb configuration" section below, and top of 'gitweb.cgi' for their + full list and description. If the environment variable GITWEB_CONFIG + is set when gitweb.cgi is executed, then the file specified in the + environment variable will be loaded instead of the file specified + when gitweb.cgi was created. [Default: gitweb_config.perl] Runtime gitweb configuration @@ -39,11 +107,122 @@ Runtime gitweb configuration You can adjust gitweb behaviour using the file specified in `GITWEB_CONFIG` (defaults to 'gitweb_config.perl' in the same directory as the CGI). -See the top of 'gitweb.cgi' for the list of variables and some description. The most notable thing that is not configurable at compile time are the -optional features, stored in the '%features' variable. You can find further -description on how to reconfigure the default features setting in your -`GITWEB_CONFIG` or per-project in `project.git/config` inside 'gitweb.cgi'. +optional features, stored in the '%features' variable. + +Ultimate description on how to reconfigure the default features setting +in your `GITWEB_CONFIG` or per-project in `project.git/config` can be found +as comments inside 'gitweb.cgi'. + +See also "Gitweb config file" (with example of gitweb config file), and +"Gitweb repositories" sections in INSTALL file for gitweb. + + +Gitweb config file is [fragment] of perl code. You can set variables +using "our $variable = value"; text from "#" character until the end +of a line is ignored. See perlsyn(1) man page for details. + +Below there is list of vaiables which you might want to set in gitweb config. +See the top of 'gitweb.cgi' for the full list of variables and their +descriptions. + +Gitweb config file variables +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can set, among others, the following variables in gitweb config files: + * $GIT + Cure git executable to use. By default set to "$GIT_BINDIR/git", which + in turn is by default set to "$(bindir)/git". If you use git from binary + package, set this to "/usr/bin/git". This can just be "git" if your + webserver has a sensible PATH. If you have multiple git versions + installed it is / can be used to choose which one to use. + * $version + Gitweb version, set automatically when creating gitweb.cgi from + gitweb.perl. You might want to modify it if you are running modified + gitweb. + * $my_url, $my_uri + URL and absolute URL of gitweb script; you might need to set those + variables if you are using 'pathinfo' feature: see also below. + * $home_link + Target of the home link on top of all pages (the first part of view + "breadcrumbs"). By default set to absolute URI of a page; you might + need to set it up to [base] gitweb URI if you use 'pathinfo' feature + (alternative format of the URLs, with project name embedded directly + in the path part of URL). + * @stylesheets + List of URIs of stylesheets (relative to base URI of a page). You + might specify more than one stylesheet, for example use gitweb.css + as base, with site specific modifications in separate stylesheet + to make it easier to upgrade gitweb. You can add 'site' stylesheet + for example by using + push @stylesheets, "gitweb-site.css"; + in gitweb config file. + * $logo_url, $logo_label + URI and label (title) of GIT logo link (or your site logo, if you choose + to use different logo image). By default they point to git homepage; + in the past they pointed to git documentation at www.kernel.org. + * $projects_list_description_width + The width (in characters) of the projects list "Description" column. + Longer descriptions will be cut (trying to cut at word boundary); + full description is available as 'title' attribute (usually shown on + mouseover). By default set to 25, which might be too small if you + use long project descriptions. + * @git_base_url_list + List of git base URLs used for URL to where fetch project from, shown + in project summary page. Full URL is "$git_base_url/$project". + You can setup multiple base URLs (for example one for git:// protocol + access, and one for http:// "dumb" protocol access). Note that per + repository configuration in 'cloneurl' file, or as values of gitweb.url + project config. + * $default_blob_plain_mimetype + Default mimetype for blob_plain (raw) view, if mimetype checking + doesn't result in some other type; by default 'text/plain'. + * $default_text_plain_charset + Default charset for text files. If not set, web serwer configuration + would be used. + * $mimetypes_file + File to use for (filename extension based) guessing of MIME types before + trying /etc/mime.types. Path, if relative, is taken currently as taken + relative to current git repositoy. + * $fallback_encoding + Gitweb assumes this charset if line contains non-UTF-8 characters. + Fallback decoding is used without error checking, so it can be even + 'utf-8'. Value mist be valid encodig; see Encoding::Supported(3pm) man + page for a list. By default 'latin1', aka. 'iso-8859-1'. + * @diff_opts + Rename detection options for git-diff and git-diff-tree. By default + ('-M'); set it to ('-C') or ('-C', '-C') to also detect copies, or + set it to () if you don't want to have renames detection. + +Per-repository gitweb configuration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can also configure individual repositories shown in gitweb by creating +file in the GIT_DIR of git repository, or by setting some repo configuration +variable (in GIT_DIR/config). + +You can use the following files in repository: + * README.html + A .html file (HTML fragment) which is included on the gitweb project + summary page inside <div> block element. You can use it for longer + description of a project, to provide links for example to projects + homepage, etc. + * description (or gitweb.description) + Short (shortened by default to 25 characters in the projects list page) + single line description of a project (of a repository). Plain text file; + HTML will be escaped. By default set to + Unnamed repository; edit this file to name it for gitweb. + from the template during creating repository. You can use + gitweb.description repo configuration variable, but the file takes + precendence. + * cloneurl (or multiple-valued gitweb.url) + File with repository URL (used for clone and fetch), one per line. + Displayed in the project summary page. You can use multiple-valued + gitweb.url repository configuration variable for that, but the file + takes precendence. + * various gitweb.* config variables (in config) + Read description of %feature hash for detailed list, and some + descriptions. Webserver configuration diff --git a/gitweb/gitweb.css b/gitweb/gitweb.css index 1b8887987f..446a1c333b 100644 --- a/gitweb/gitweb.css +++ b/gitweb/gitweb.css @@ -85,6 +85,10 @@ div.title, a.title { color: #000000; } +div.readme { + padding: 8px; +} + a.title:hover { background-color: #d9d8d1; } @@ -170,14 +174,10 @@ a.text:hover { table { padding: 8px 4px; -} - -table.project_list { border-spacing: 0; } table.diff_tree { - border-spacing: 0; font-family: monospace; } diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 2e00756276..24b31582af 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -611,6 +611,15 @@ sub href(%) { ); my %mapping = @mapping; + if ($params{-replay}) { + while (my ($name, $symbol) = each %mapping) { + if (!exists $params{$name}) { + # to allow for multivalued params we use arrayref form + $params{$name} = [ $cgi->param($symbol) ]; + } + } + } + $params{'project'} = $project unless exists $params{'project'}; my ($use_pathinfo) = gitweb_check_feature('pathinfo'); @@ -686,10 +695,9 @@ sub validate_refname { # in utf-8 thanks to "binmode STDOUT, ':utf8'" at beginning sub to_utf8 { my $str = shift; - my $res; - eval { $res = decode_utf8($str, Encode::FB_CROAK); }; - if (defined $res) { - return $res; + if (utf8::valid($str)) { + utf8::decode($str); + return $str; } else { return decode($fallback_encoding, $str, Encode::FB_DEFAULT); } @@ -1423,20 +1431,121 @@ sub git_get_type { return $type; } +# repository configuration +our $config_file = ''; +our %config; + +# store multiple values for single key as anonymous array reference +# single values stored directly in the hash, not as [ <value> ] +sub hash_set_multi { + my ($hash, $key, $value) = @_; + + if (!exists $hash->{$key}) { + $hash->{$key} = $value; + } elsif (!ref $hash->{$key}) { + $hash->{$key} = [ $hash->{$key}, $value ]; + } else { + push @{$hash->{$key}}, $value; + } +} + +# return hash of git project configuration +# optionally limited to some section, e.g. 'gitweb' +sub git_parse_project_config { + my $section_regexp = shift; + my %config; + + local $/ = "\0"; + + open my $fh, "-|", git_cmd(), "config", '-z', '-l', + or return; + + while (my $keyval = <$fh>) { + chomp $keyval; + my ($key, $value) = split(/\n/, $keyval, 2); + + hash_set_multi(\%config, $key, $value) + if (!defined $section_regexp || $key =~ /^(?:$section_regexp)\./o); + } + close $fh; + + return %config; +} + +# convert config value to boolean, 'true' or 'false' +# no value, number > 0, 'true' and 'yes' values are true +# rest of values are treated as false (never as error) +sub config_to_bool { + my $val = shift; + + # strip leading and trailing whitespace + $val =~ s/^\s+//; + $val =~ s/\s+$//; + + return (!defined $val || # section.key + ($val =~ /^\d+$/ && $val) || # section.key = 1 + ($val =~ /^(?:true|yes)$/i)); # section.key = true +} + +# convert config value to simple decimal number +# an optional value suffix of 'k', 'm', or 'g' will cause the value +# to be multiplied by 1024, 1048576, or 1073741824 +sub config_to_int { + my $val = shift; + + # strip leading and trailing whitespace + $val =~ s/^\s+//; + $val =~ s/\s+$//; + + if (my ($num, $unit) = ($val =~ /^([0-9]*)([kmg])$/i)) { + $unit = lc($unit); + # unknown unit is treated as 1 + return $num * ($unit eq 'g' ? 1073741824 : + $unit eq 'm' ? 1048576 : + $unit eq 'k' ? 1024 : 1); + } + return $val; +} + +# convert config value to array reference, if needed +sub config_to_multi { + my $val = shift; + + return ref($val) ? $val : [ $val ]; +} + sub git_get_project_config { my ($key, $type) = @_; + # key sanity check return unless ($key); $key =~ s/^gitweb\.//; return if ($key =~ m/\W/); - my @x = (git_cmd(), 'config'); - if (defined $type) { push @x, $type; } - push @x, "--get"; - push @x, "gitweb.$key"; - my $val = qx(@x); - chomp $val; - return ($val); + # type sanity check + if (defined $type) { + $type =~ s/^--//; + $type = undef + unless ($type eq 'bool' || $type eq 'int'); + } + + # get config + if (!defined $config_file || + $config_file ne "$git_dir/config") { + %config = git_parse_project_config('gitweb'); + $config_file = "$git_dir/config"; + } + + # ensure given type + if (!defined $type) { + return $config{"gitweb.$key"}; + } elsif ($type eq 'bool') { + # backward compatibility: 'git config --bool' returns true/false + return config_to_bool($config{"gitweb.$key"}) ? 'true' : 'false'; + } elsif ($type eq 'int') { + return config_to_int($config{"gitweb.$key"}); + } + return $config{"gitweb.$key"}; } # get hash of given path at given ref @@ -1496,7 +1605,9 @@ sub git_get_path_by_hash { sub git_get_project_description { my $path = shift; - open my $fd, "$projectroot/$path/description" or return undef; + $git_dir = "$projectroot/$path"; + open my $fd, "$projectroot/$path/description" + or return git_get_project_config('description'); my $descr = <$fd>; close $fd; if (defined $descr) { @@ -1508,7 +1619,11 @@ sub git_get_project_description { sub git_get_project_url_list { my $path = shift; - open my $fd, "$projectroot/$path/cloneurl" or return; + $git_dir = "$projectroot/$path"; + open my $fd, "$projectroot/$path/cloneurl" + or return wantarray ? + @{ config_to_multi(git_get_project_config('url')) } : + config_to_multi(git_get_project_config('url')); my @git_project_url_list = map { chomp; $_ } <$fd>; close $fd; @@ -1740,7 +1855,7 @@ sub parse_date { $date{'mday-time'} = sprintf "%d %s %02d:%02d", $mday, $months[$mon], $hour ,$min; $date{'iso-8601'} = sprintf "%04d-%02d-%02dT%02d:%02d:%02dZ", - 1900+$year, $mon, $mday, $hour ,$min, $sec; + 1900+$year, 1+$mon, $mday, $hour ,$min, $sec; $tz =~ m/^([+\-][0-9][0-9])([0-9][0-9])$/; my $local = $epoch + ((int $1 + ($2/60)) * 3600); @@ -1990,12 +2105,12 @@ sub parse_difftree_raw_line { $res{'to_mode'} = $2; $res{'from_id'} = $3; $res{'to_id'} = $4; - $res{'status'} = $5; + $res{'status'} = $res{'status_str'} = $5; $res{'similarity'} = $6; if ($res{'status'} eq 'R' || $res{'status'} eq 'C') { # renamed or copied ($res{'from_file'}, $res{'to_file'}) = map { unquote($_) } split("\t", $7); } else { - $res{'file'} = unquote($7); + $res{'from_file'} = $res{'to_file'} = $res{'file'} = unquote($7); } } # '::100755 100755 100755 60e79ca1b01bc8b057abe17ddab484699a7f5fdb 94067cc5f73388f33722d52ae02f44692bc07490 94067cc5f73388f33722d52ae02f44692bc07490 MR git-gui/git-gui.sh' @@ -2006,6 +2121,7 @@ sub parse_difftree_raw_line { $res{'to_mode'} = pop @{$res{'from_mode'}}; $res{'from_id'} = [ split(' ', $3) ]; $res{'to_id'} = pop @{$res{'from_id'}}; + $res{'status_str'} = $4; $res{'status'} = [ split('', $4) ]; $res{'to_file'} = unquote($5); } @@ -2062,7 +2178,10 @@ sub parse_from_to_diffinfo { fill_from_file_info($diffinfo, @parents) unless exists $diffinfo->{'from_file'}; for (my $i = 0; $i < $diffinfo->{'nparents'}; $i++) { - $from->{'file'}[$i] = $diffinfo->{'from_file'}[$i] || $diffinfo->{'to_file'}; + $from->{'file'}[$i] = + defined $diffinfo->{'from_file'}[$i] ? + $diffinfo->{'from_file'}[$i] : + $diffinfo->{'to_file'}; if ($diffinfo->{'status'}[$i] ne "A") { # not new (added) file $from->{'href'}[$i] = href(action=>"blob", hash_base=>$parents[$i], @@ -2074,7 +2193,7 @@ sub parse_from_to_diffinfo { } } else { # ordinary (not combined) diff - $from->{'file'} = $diffinfo->{'from_file'} || $diffinfo->{'file'}; + $from->{'file'} = $diffinfo->{'from_file'}; if ($diffinfo->{'status'} ne "A") { # not new (added) file $from->{'href'} = href(action=>"blob", hash_base=>$hash_parent, hash=>$diffinfo->{'from_id'}, @@ -2084,7 +2203,7 @@ sub parse_from_to_diffinfo { } } - $to->{'file'} = $diffinfo->{'to_file'} || $diffinfo->{'file'}; + $to->{'file'} = $diffinfo->{'to_file'}; if (!is_deleted($diffinfo)) { # file exists in result $to->{'href'} = href(action=>"blob", hash_base=>$hash, hash=>$diffinfo->{'to_id'}, @@ -2505,7 +2624,7 @@ sub format_paging_nav { if ($page > 0) { $paging_nav .= " ⋅ " . - $cgi->a({-href => href(action=>$action, hash=>$hash, page=>$page-1), + $cgi->a({-href => href(-replay=>1, page=>$page-1), -accesskey => "p", -title => "Alt-p"}, "prev"); } else { $paging_nav .= " ⋅ prev"; @@ -2513,7 +2632,7 @@ sub format_paging_nav { if ($nrevs >= (100 * ($page+1)-1)) { $paging_nav .= " ⋅ " . - $cgi->a({-href => href(action=>$action, hash=>$hash, page=>$page+1), + $cgi->a({-href => href(-replay=>1, page=>$page+1), -accesskey => "n", -title => "Alt-n"}, "next"); } else { $paging_nav .= " ⋅ next"; @@ -2818,7 +2937,7 @@ sub fill_from_file_info { sub is_deleted { my $diffinfo = shift; - return $diffinfo->{'to_id'} eq ('0' x 40); + return $diffinfo->{'status_str'} =~ /D/; } # does patch correspond to [previous] difftree raw line @@ -2829,7 +2948,7 @@ sub is_patch_split { my ($diffinfo, $patchinfo) = @_; return defined $diffinfo && defined $patchinfo - && ($diffinfo->{'to_file'} || $diffinfo->{'file'}) eq $patchinfo->{'to_file'}; + && $diffinfo->{'to_file'} eq $patchinfo->{'to_file'}; } @@ -3424,7 +3543,7 @@ sub git_shortlog_body { $from = 0 unless defined $from; $to = $#{$commitlist} if (!defined $to || $#{$commitlist} < $to); - print "<table class=\"shortlog\" cellspacing=\"0\">\n"; + print "<table class=\"shortlog\">\n"; my $alternate = 1; for (my $i = $from; $i <= $to; $i++) { my %co = %{$commitlist->[$i]}; @@ -3470,7 +3589,7 @@ sub git_history_body { $from = 0 unless defined $from; $to = $#{$commitlist} unless (defined $to && $to <= $#{$commitlist}); - print "<table class=\"history\" cellspacing=\"0\">\n"; + print "<table class=\"history\">\n"; my $alternate = 1; for (my $i = $from; $i <= $to; $i++) { my %co = %{$commitlist->[$i]}; @@ -3530,7 +3649,7 @@ sub git_tags_body { $from = 0 unless defined $from; $to = $#{$taglist} if (!defined $to || $#{$taglist} < $to); - print "<table class=\"tags\" cellspacing=\"0\">\n"; + print "<table class=\"tags\">\n"; my $alternate = 1; for (my $i = $from; $i <= $to; $i++) { my $entry = $taglist->[$i]; @@ -3593,7 +3712,7 @@ sub git_heads_body { $from = 0 unless defined $from; $to = $#{$headlist} if (!defined $to || $#{$headlist} < $to); - print "<table class=\"heads\" cellspacing=\"0\">\n"; + print "<table class=\"heads\">\n"; my $alternate = 1; for (my $i = $from; $i <= $to; $i++) { my $entry = $headlist->[$i]; @@ -3630,7 +3749,7 @@ sub git_search_grep_body { $from = 0 unless defined $from; $to = $#{$commitlist} if (!defined $to || $#{$commitlist} < $to); - print "<table class=\"grep\" cellspacing=\"0\">\n"; + print "<table class=\"commit_search\">\n"; my $alternate = 1; for (my $i = $from; $i <= $to; $i++) { my %co = %{$commitlist->[$i]}; @@ -3666,6 +3785,8 @@ sub git_search_grep_body { "<td class=\"link\">" . $cgi->a({-href => href(action=>"commit", hash=>$co{'id'})}, "commit") . " | " . + $cgi->a({-href => href(action=>"commitdiff", hash=>$co{'id'})}, "commitdiff") . + " | " . $cgi->a({-href => href(action=>"tree", hash=>$co{'tree'}, hash_base=>$co{'id'})}, "tree"); print "</td>\n" . "</tr>\n"; @@ -3771,7 +3892,7 @@ sub git_summary { git_print_page_nav('summary','', $head); print "<div class=\"title\"> </div>\n"; - print "<table cellspacing=\"0\">\n" . + print "<table class=\"projects_list\">\n" . "<tr><td>description</td><td>" . esc_html($descr) . "</td></tr>\n" . "<tr><td>owner</td><td>" . esc_html($owner) . "</td></tr>\n"; if (defined $cd{'rfc2822'}) { @@ -3792,8 +3913,10 @@ sub git_summary { if (-s "$projectroot/$project/README.html") { if (open my $fd, "$projectroot/$project/README.html") { - print "<div class=\"title\">readme</div>\n"; + print "<div class=\"title\">readme</div>\n" . + "<div class=\"readme\">\n"; print $_ while (<$fd>); + print "\n</div>\n"; # class="readme" close $fd; } } @@ -3845,7 +3968,7 @@ sub git_tag { git_print_header_div('commit', esc_html($tag{'name'}), $hash); print "<div class=\"title_text\">\n" . - "<table cellspacing=\"0\">\n" . + "<table class=\"object_header\">\n" . "<tr>\n" . "<td>object</td>\n" . "<td>" . $cgi->a({-class => "list", -href => href(action=>$tag{'type'}, hash=>$tag{'object'})}, @@ -3898,11 +4021,11 @@ sub git_blame2 { or die_error(undef, "Open git-blame failed"); git_header_html(); my $formats_nav = - $cgi->a({-href => href(action=>"blob", hash=>$hash, hash_base=>$hash_base, file_name=>$file_name)}, + $cgi->a({-href => href(action=>"blob", -replay=>1)}, "blob") . " | " . - $cgi->a({-href => href(action=>"history", hash=>$hash, hash_base=>$hash_base, file_name=>$file_name)}, - "history") . + $cgi->a({-href => href(action=>"history", -replay=>1)}, + "history") . " | " . $cgi->a({-href => href(action=>"blame", file_name=>$file_name)}, "HEAD"); @@ -4178,18 +4301,15 @@ sub git_blob { if (defined $file_name) { if ($have_blame) { $formats_nav .= - $cgi->a({-href => href(action=>"blame", hash_base=>$hash_base, - hash=>$hash, file_name=>$file_name)}, + $cgi->a({-href => href(action=>"blame", -replay=>1)}, "blame") . " | "; } $formats_nav .= - $cgi->a({-href => href(action=>"history", hash_base=>$hash_base, - hash=>$hash, file_name=>$file_name)}, + $cgi->a({-href => href(action=>"history", -replay=>1)}, "history") . " | " . - $cgi->a({-href => href(action=>"blob_plain", - hash=>$hash, file_name=>$file_name)}, + $cgi->a({-href => href(action=>"blob_plain", -replay=>1)}, "raw") . " | " . $cgi->a({-href => href(action=>"blob", @@ -4197,7 +4317,8 @@ sub git_blob { "HEAD"); } else { $formats_nav .= - $cgi->a({-href => href(action=>"blob_plain", hash=>$hash)}, "raw"); + $cgi->a({-href => href(action=>"blob_plain", -replay=>1)}, + "raw"); } git_print_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav); git_print_header_div('commit', esc_html($co{'title'}), $hash_base); @@ -4260,8 +4381,7 @@ sub git_tree { my @views_nav = (); if (defined $file_name) { push @views_nav, - $cgi->a({-href => href(action=>"history", hash_base=>$hash_base, - hash=>$hash, file_name=>$file_name)}, + $cgi->a({-href => href(action=>"history", -replay=>1)}, "history"), $cgi->a({-href => href(action=>"tree", hash_base=>"HEAD", file_name=>$file_name)}, @@ -4288,7 +4408,7 @@ sub git_tree { } git_print_page_path($file_name, 'tree', $hash_base); print "<div class=\"page_body\">\n"; - print "<table cellspacing=\"0\">\n"; + print "<table class=\"tree\">\n"; my $alternate = 1; # '..' (top directory) link if possible if (defined $hash_base && @@ -4435,7 +4555,7 @@ sub git_log { } if ($#commitlist >= 100) { print "<div class=\"page_nav\">\n"; - print $cgi->a({-href => href(action=>"log", hash=>$hash, page=>$page+1), + print $cgi->a({-href => href(-replay=>1, page=>$page+1), -accesskey => "n", -title => "Alt-n"}, "next"); print "</div>\n"; } @@ -4510,7 +4630,7 @@ sub git_commit { git_print_header_div('tree', esc_html($co{'title'}) . $ref, $co{'tree'}, $hash); } print "<div class=\"title_text\">\n" . - "<table cellspacing=\"0\">\n"; + "<table class=\"object_header\">\n"; print "<tr><td>author</td><td>" . esc_html($co{'author'}) . "</td></tr>\n". "<tr>" . "<td></td><td> $ad{'rfc2822'}"; @@ -4667,8 +4787,8 @@ sub git_blobdiff { } %diffinfo = parse_difftree_raw_line($difftree[0]); - $file_parent ||= $diffinfo{'from_file'} || $file_name || $diffinfo{'file'}; - $file_name ||= $diffinfo{'to_file'} || $diffinfo{'file'}; + $file_parent ||= $diffinfo{'from_file'} || $file_name; + $file_name ||= $diffinfo{'to_file'}; $hash_parent ||= $diffinfo{'from_id'}; $hash ||= $diffinfo{'to_id'}; @@ -4729,10 +4849,7 @@ sub git_blobdiff { # header if ($format eq 'html') { my $formats_nav = - $cgi->a({-href => href(action=>"blobdiff_plain", - hash=>$hash, hash_parent=>$hash_parent, - hash_base=>$hash_base, hash_parent_base=>$hash_parent_base, - file_name=>$file_name, file_parent=>$file_parent)}, + $cgi->a({-href => href(action=>"blobdiff_plain", -replay=>1)}, "raw"); git_header_html(undef, $expires); if (defined $hash_base && (my %co = parse_commit($hash_base))) { @@ -4806,8 +4923,7 @@ sub git_commitdiff { my $formats_nav; if ($format eq 'html') { $formats_nav = - $cgi->a({-href => href(action=>"commitdiff_plain", - hash=>$hash, hash_parent=>$hash_parent)}, + $cgi->a({-href => href(action=>"commitdiff_plain", -replay=>1)}, "raw"); if (defined $hash_parent && @@ -5002,27 +5118,20 @@ sub git_history { file_name=>$file_name)}, "first"); $paging_nav .= " ⋅ " . - $cgi->a({-href => href(action=>"history", hash=>$hash, hash_base=>$hash_base, - file_name=>$file_name, page=>$page-1), + $cgi->a({-href => href(-replay=>1, page=>$page-1), -accesskey => "p", -title => "Alt-p"}, "prev"); } else { $paging_nav .= "first"; $paging_nav .= " ⋅ prev"; } - if ($#commitlist >= 100) { - $paging_nav .= " ⋅ " . - $cgi->a({-href => href(action=>"history", hash=>$hash, hash_base=>$hash_base, - file_name=>$file_name, page=>$page+1), - -accesskey => "n", -title => "Alt-n"}, "next"); - } else { - $paging_nav .= " ⋅ next"; - } my $next_link = ''; if ($#commitlist >= 100) { $next_link = - $cgi->a({-href => href(action=>"history", hash=>$hash, hash_base=>$hash_base, - file_name=>$file_name, page=>$page+1), + $cgi->a({-href => href(-replay=>1, page=>$page+1), -accesskey => "n", -title => "Alt-n"}, "next"); + $paging_nav .= " ⋅ $next_link"; + } else { + $paging_nav .= " ⋅ next"; } git_header_html(); @@ -5092,30 +5201,23 @@ sub git_search { searchtext=>$searchtext, searchtype=>$searchtype)}, "first"); $paging_nav .= " ⋅ " . - $cgi->a({-href => href(action=>"search", hash=>$hash, - searchtext=>$searchtext, searchtype=>$searchtype, - page=>$page-1), + $cgi->a({-href => href(-replay=>1, page=>$page-1), -accesskey => "p", -title => "Alt-p"}, "prev"); } else { $paging_nav .= "first"; $paging_nav .= " ⋅ prev"; } + my $next_link = ''; if ($#commitlist >= 100) { - $paging_nav .= " ⋅ " . - $cgi->a({-href => href(action=>"search", hash=>$hash, - searchtext=>$searchtext, searchtype=>$searchtype, - page=>$page+1), + $next_link = + $cgi->a({-href => href(-replay=>1, page=>$page+1), -accesskey => "n", -title => "Alt-n"}, "next"); + $paging_nav .= " ⋅ $next_link"; } else { $paging_nav .= " ⋅ next"; } - my $next_link = ''; + if ($#commitlist >= 100) { - $next_link = - $cgi->a({-href => href(action=>"search", hash=>$hash, - searchtext=>$searchtext, searchtype=>$searchtype, - page=>$page+1), - -accesskey => "n", -title => "Alt-n"}, "next"); } git_print_page_nav('','', $hash,$co{'tree'},$hash, $paging_nav); @@ -5127,7 +5229,7 @@ sub git_search { git_print_page_nav('','', $hash,$co{'tree'},$hash); git_print_header_div('commit', esc_html($co{'title'}), $hash); - print "<table cellspacing=\"0\">\n"; + print "<table class=\"pickaxe search\">\n"; my $alternate = 1; $/ = "\n"; my $git_command = git_cmd_str(); @@ -5194,7 +5296,7 @@ sub git_search { git_print_page_nav('','', $hash,$co{'tree'},$hash); git_print_header_div('commit', esc_html($co{'title'}), $hash); - print "<table cellspacing=\"0\">\n"; + print "<table class=\"grep_search\">\n"; my $alternate = 1; my $matches = 0; $/ = "\n"; @@ -5314,7 +5416,7 @@ sub git_shortlog { my $next_link = ''; if ($#commitlist >= 100) { $next_link = - $cgi->a({-href => href(action=>"shortlog", hash=>$hash, page=>$page+1), + $cgi->a({-href => href(-replay=>1, page=>$page+1), -accesskey => "n", -title => "Alt-n"}, "next"); } diff --git a/hash-object.c b/hash-object.c index 18f5017f51..0a58f3f126 100644 --- a/hash-object.c +++ b/hash-object.c @@ -42,6 +42,8 @@ int main(int argc, char **argv) int prefix_length = -1; int no_more_flags = 0; + git_config(git_default_config); + for (i = 1 ; i < argc; i++) { if (!no_more_flags && argv[i][0] == '-') { if (!strcmp(argv[i], "-t")) { @@ -7,7 +7,6 @@ #include "builtin.h" #include "exec_cmd.h" #include "common-cmds.h" -#include <sys/ioctl.h> /* most GUI terminals set COLUMNS (although some don't export it) */ static int term_columns(void) @@ -79,7 +78,8 @@ static void uniq(struct cmdnames *cmds) cmds->cnt = j; } -static void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes) { +static void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes) +{ int ci, cj, ei; int cmp; @@ -237,7 +237,27 @@ void list_common_cmds_help(void) mput_char(' ', longest - strlen(common_cmds[i].name)); puts(common_cmds[i].help); } - puts("(use 'git help -a' to get a list of all installed git commands)"); +} + +static void setup_man_path(void) +{ + struct strbuf new_path; + const char *old_path = getenv("MANPATH"); + + strbuf_init(&new_path, 0); + + /* We should always put ':' after our path. If there is no + * old_path, the ':' at the end will let 'man' to try + * system-wide paths after ours to find the manual page. If + * there is old_path, we need ':' as delimiter. */ + strbuf_addstr(&new_path, GIT_MAN_PATH); + strbuf_addch(&new_path, ':'); + if (old_path) + strbuf_addstr(&new_path, old_path); + + setenv("MANPATH", new_path.buf, 1); + + strbuf_release(&new_path); } static void show_man_page(const char *git_cmd) @@ -255,6 +275,7 @@ static void show_man_page(const char *git_cmd) page = p; } + setup_man_path(); execlp("man", "man", page, NULL); } diff --git a/http-push.c b/http-push.c index c02a3af634..78283b4de3 100644 --- a/http-push.c +++ b/http-push.c @@ -78,7 +78,7 @@ static struct curl_slist *no_pragma_header; static struct curl_slist *default_headers; static int push_verbosely; -static int push_all; +static int push_all = MATCH_REFS_NONE; static int force_all; static int dry_run; @@ -433,7 +433,7 @@ static void start_fetch_packed(struct transfer_request *request) packfile = fopen(request->tmpfile, "a"); if (!packfile) { fprintf(stderr, "Unable to open local file %s for pack", - filename); + request->tmpfile); remote->can_update_info_refs = 0; free(url); return; @@ -941,7 +941,7 @@ static int fetch_index(unsigned char *sha1) indexfile = fopen(tmpfile, "a"); if (!indexfile) return error("Unable to open local file %s for pack index", - filename); + tmpfile); slot = get_active_slot(); slot->results = &results; @@ -2241,7 +2241,11 @@ static int delete_remote_branch(char *pattern, int force) /* Remote branch must be an ancestor of remote HEAD */ if (!verify_merge_base(head_sha1, remote_ref->old_sha1)) { - return error("The branch '%s' is not a strict subset of your current HEAD.\nIf you are sure you want to delete it, run:\n\t'git http-push -D %s %s'", remote_ref->name, remote->url, pattern); + return error("The branch '%s' is not an ancestor " + "of your current HEAD.\n" + "If you are sure you want to delete it," + " run:\n\t'git http-push -D %s %s'", + remote_ref->name, remote->url, pattern); } } @@ -2296,7 +2300,7 @@ int main(int argc, char **argv) if (*arg == '-') { if (!strcmp(arg, "--all")) { - push_all = 1; + push_all = MATCH_REFS_ALL; continue; } if (!strcmp(arg, "--force")) { @@ -2389,7 +2393,7 @@ int main(int argc, char **argv) if (!remote_tail) remote_tail = &remote_refs; if (match_refs(local_refs, remote_refs, &remote_tail, - nr_refspec, refspec, push_all)) + nr_refspec, (const char **) refspec, push_all)) return -1; if (!remote_refs) { fprintf(stderr, "No refs in common and none specified; doing nothing.\n"); @@ -2417,16 +2421,17 @@ int main(int argc, char **argv) if (!has_sha1_file(ref->old_sha1) || !ref_newer(ref->peer_ref->new_sha1, ref->old_sha1)) { - /* We do not have the remote ref, or + /* + * We do not have the remote ref, or * we know that the remote ref is not * an ancestor of what we are trying to * push. Either way this can be losing * commits at the remote end and likely * we were not up to date to begin with. */ - error("remote '%s' is not a strict " - "subset of local ref '%s'. " - "maybe you are not up-to-date and " + error("remote '%s' is not an ancestor of\n" + "local '%s'.\n" + "Maybe you are not up-to-date and " "need to pull first?", ref->name, ref->peer_ref->name); diff --git a/http-walker.c b/http-walker.c index 444aebf526..a3fb596542 100644 --- a/http-walker.c +++ b/http-walker.c @@ -405,7 +405,7 @@ static int fetch_index(struct walker *walker, struct alt_base *repo, unsigned ch indexfile = fopen(tmpfile, "a"); if (!indexfile) return error("Unable to open local file %s for pack index", - filename); + tmpfile); slot = get_active_slot(); slot->results = &results; @@ -770,7 +770,7 @@ static int fetch_pack(struct walker *walker, struct alt_base *repo, unsigned cha packfile = fopen(tmpfile, "a"); if (!packfile) return error("Unable to open local file %s for pack", - filename); + tmpfile); slot = get_active_slot(); slot->results = &results; @@ -24,6 +24,7 @@ char *ssl_cainfo = NULL; long curl_low_speed_limit = -1; long curl_low_speed_time = -1; int curl_ftp_no_epsv = 0; +char *curl_http_proxy = NULL; struct curl_slist *pragma_header; @@ -160,6 +161,13 @@ static int http_options(const char *var, const char *value) curl_ftp_no_epsv = git_config_bool(var, value); return 0; } + if (!strcmp("http.proxy", var)) { + if (curl_http_proxy == NULL) { + curl_http_proxy = xmalloc(strlen(value)+1); + strcpy(curl_http_proxy, value); + } + return 0; + } /* Fall back on the default ones */ return git_default_config(var, value); @@ -205,6 +213,9 @@ static CURL* get_curl_handle(void) if (curl_ftp_no_epsv) curl_easy_setopt(result, CURLOPT_FTP_USE_EPSV, 0); + if (curl_http_proxy) + curl_easy_setopt(result, CURLOPT_PROXY, curl_http_proxy); + return result; } @@ -113,25 +113,15 @@ static int add_raw(char *buf, size_t size, int offset, const char *str) static int crud(unsigned char c) { - static char crud_array[256]; - static int crud_array_initialized = 0; - - if (!crud_array_initialized) { - int k; - - for (k = 0; k <= 31; ++k) crud_array[k] = 1; - crud_array[' '] = 1; - crud_array['.'] = 1; - crud_array[','] = 1; - crud_array[':'] = 1; - crud_array[';'] = 1; - crud_array['<'] = 1; - crud_array['>'] = 1; - crud_array['"'] = 1; - crud_array['\''] = 1; - crud_array_initialized = 1; - } - return crud_array[c]; + return c <= 32 || + c == '.' || + c == ',' || + c == ':' || + c == ';' || + c == '<' || + c == '>' || + c == '"' || + c == '\''; } /* @@ -185,7 +175,7 @@ static const char *env_hint = "\n" "Run\n" "\n" -" git config --global user.email \"you@email.com\"\n" +" git config --global user.email \"you@example.com\"\n" " git config --global user.name \"Your Name\"\n" "\n" "to set your account\'s default identity.\n" @@ -193,11 +183,14 @@ static const char *env_hint = "\n"; const char *fmt_ident(const char *name, const char *email, - const char *date_str, int error_on_no_name) + const char *date_str, int flag) { static char buffer[1000]; char date[50]; int i; + int error_on_no_name = (flag & IDENT_ERROR_ON_NO_NAME); + int warn_on_no_name = (flag & IDENT_WARN_ON_NO_NAME); + int name_addr_only = (flag & IDENT_NO_DATE); setup_ident(); if (!name) @@ -208,12 +201,12 @@ const char *fmt_ident(const char *name, const char *email, if (!*name) { struct passwd *pw; - if (0 <= error_on_no_name && + if ((warn_on_no_name || error_on_no_name) && name == git_default_name && env_hint) { fprintf(stderr, env_hint, au_env, co_env); env_hint = NULL; /* warn only once, for "git-var -l" */ } - if (0 < error_on_no_name) + if (error_on_no_name) die("empty ident %s <%s> not allowed", name, email); pw = getpwuid(getuid()); if (!pw) @@ -224,32 +217,41 @@ const char *fmt_ident(const char *name, const char *email, } strcpy(date, git_default_date); - if (date_str) + if (!name_addr_only && date_str) parse_date(date_str, date, sizeof(date)); i = copy(buffer, sizeof(buffer), 0, name); i = add_raw(buffer, sizeof(buffer), i, " <"); i = copy(buffer, sizeof(buffer), i, email); - i = add_raw(buffer, sizeof(buffer), i, "> "); - i = copy(buffer, sizeof(buffer), i, date); + if (!name_addr_only) { + i = add_raw(buffer, sizeof(buffer), i, "> "); + i = copy(buffer, sizeof(buffer), i, date); + } else { + i = add_raw(buffer, sizeof(buffer), i, ">"); + } if (i >= sizeof(buffer)) die("Impossibly long personal identifier"); buffer[i] = 0; return buffer; } -const char *git_author_info(int error_on_no_name) +const char *fmt_name(const char *name, const char *email) +{ + return fmt_ident(name, email, NULL, IDENT_ERROR_ON_NO_NAME | IDENT_NO_DATE); +} + +const char *git_author_info(int flag) { return fmt_ident(getenv("GIT_AUTHOR_NAME"), getenv("GIT_AUTHOR_EMAIL"), getenv("GIT_AUTHOR_DATE"), - error_on_no_name); + flag); } -const char *git_committer_info(int error_on_no_name) +const char *git_committer_info(int flag) { return fmt_ident(getenv("GIT_COMMITTER_NAME"), getenv("GIT_COMMITTER_EMAIL"), getenv("GIT_COMMITTER_DATE"), - error_on_no_name); + flag); } diff --git a/index-pack.c b/index-pack.c index 61ea7621be..9fd6982a97 100644 --- a/index-pack.c +++ b/index-pack.c @@ -87,9 +87,9 @@ static void *fill(int min) die("early EOF"); die("read error on input: %s", strerror(errno)); } - if (from_stdin) - display_throughput(progress, ret); input_len += ret; + if (from_stdin) + display_throughput(progress, consumed_bytes + input_len); } while (input_len < min); return input_buffer; } @@ -256,7 +256,7 @@ static void *unpack_raw_entry(struct object_entry *obj, union delta_base *delta_ static void *get_data_from_pack(struct object_entry *obj) { - unsigned long from = obj[0].idx.offset + obj[0].hdr_size; + off_t from = obj[0].idx.offset + obj[0].hdr_size; unsigned long len = obj[1].idx.offset - from; unsigned long rdy = 0; unsigned char *src, *data; @@ -683,6 +683,17 @@ static void final(const char *final_pack_name, const char *curr_pack_name, } } +static int git_index_pack_config(const char *k, const char *v) +{ + if (!strcmp(k, "pack.indexversion")) { + pack_idx_default_version = git_config_int(k, v); + if (pack_idx_default_version > 2) + die("bad pack.indexversion=%d", pack_idx_default_version); + return 0; + } + return git_default_config(k, v); +} + int main(int argc, char **argv) { int i, fix_thin_pack = 0; @@ -693,6 +704,8 @@ int main(int argc, char **argv) struct pack_idx_entry **idx_objects; unsigned char sha1[20]; + git_config(git_index_pack_config); + for (i = 1; i < argc; i++) { char *arg = argv[i]; @@ -779,6 +792,7 @@ int main(int argc, char **argv) flush(); } else { if (fix_thin_pack) { + char msg[48]; int nr_unresolved = nr_deltas - nr_resolved_deltas; int nr_objects_initial = nr_objects; if (nr_unresolved <= 0) @@ -787,12 +801,11 @@ int main(int argc, char **argv) (nr_objects + nr_unresolved + 1) * sizeof(*objects)); fix_unresolved_deltas(nr_unresolved); - stop_progress(&progress); - if (verbose) - fprintf(stderr, "%d objects were added to complete this thin pack.\n", - nr_objects - nr_objects_initial); + sprintf(msg, "completed with %d local objects", + nr_objects - nr_objects_initial); + stop_progress_msg(&progress, msg); fixup_pack_header_footer(output_fd, sha1, - curr_pack, nr_objects); + curr_pack, nr_objects); } if (nr_deltas != nr_resolved_deltas) die("pack has %d unresolved deltas", diff --git a/list-objects.c b/list-objects.c index e5c88c278f..4ef58e7ec0 100644 --- a/list-objects.c +++ b/list-objects.c @@ -170,4 +170,11 @@ void traverse_commit_list(struct rev_info *revs, } for (i = 0; i < objects.nr; i++) show_object(&objects.objects[i]); + free(objects.objects); + if (revs->pending.nr) { + free(revs->pending.objects); + revs->pending.nr = 0; + revs->pending.alloc = 0; + revs->pending.objects = NULL; + } } diff --git a/lockfile.c b/lockfile.c index 9a1f64d8d7..f45d3ed544 100644 --- a/lockfile.c +++ b/lockfile.c @@ -12,8 +12,10 @@ static void remove_lock_file(void) while (lock_file_list) { if (lock_file_list->owner == me && - lock_file_list->filename[0]) + lock_file_list->filename[0]) { + close(lock_file_list->fd); unlink(lock_file_list->filename); + } lock_file_list = lock_file_list->next; } } @@ -92,7 +94,7 @@ static char *resolve_symlink(char *p, size_t s) return p; } - if (link[0] == '/') { + if (is_absolute_path(link)) { /* absolute path simply replaces p */ if (link_len < s) strcpy(p, link); @@ -120,8 +122,6 @@ static char *resolve_symlink(char *p, size_t s) static int lock_file(struct lock_file *lk, const char *path) { - int fd; - if (strlen(path) >= sizeof(lk->filename)) return -1; strcpy(lk->filename, path); /* @@ -130,8 +130,8 @@ static int lock_file(struct lock_file *lk, const char *path) */ resolve_symlink(lk->filename, sizeof(lk->filename)-5); strcat(lk->filename, ".lock"); - fd = open(lk->filename, O_RDWR | O_CREAT | O_EXCL, 0666); - if (0 <= fd) { + lk->fd = open(lk->filename, O_RDWR | O_CREAT | O_EXCL, 0666); + if (0 <= lk->fd) { if (!lock_file_list) { signal(SIGINT, remove_lock_file_on_signal); atexit(remove_lock_file); @@ -148,7 +148,7 @@ static int lock_file(struct lock_file *lk, const char *path) } else lk->filename[0] = 0; - return fd; + return lk->fd; } int hold_lock_file_for_update(struct lock_file *lk, const char *path, int die_on_error) @@ -163,6 +163,7 @@ int commit_lock_file(struct lock_file *lk) { char result_file[PATH_MAX]; int i; + close(lk->fd); strcpy(result_file, lk->filename); i = strlen(result_file) - 5; /* .lock */ result_file[i] = 0; @@ -194,7 +195,9 @@ int commit_locked_index(struct lock_file *lk) void rollback_lock_file(struct lock_file *lk) { - if (lk->filename[0]) + if (lk->filename[0]) { + close(lk->fd); unlink(lk->filename); + } lk->filename[0] = 0; } diff --git a/log-tree.c b/log-tree.c index 3763ce94fc..1f3fcf16ad 100644 --- a/log-tree.c +++ b/log-tree.c @@ -125,6 +125,18 @@ static unsigned int digits_in_number(unsigned int number) return result; } +static int has_non_ascii(const char *s) +{ + int ch; + if (!s) + return 0; + while ((ch = *s++) != '\0') { + if (non_ascii(ch)) + return 1; + } + return 0; +} + void show_log(struct rev_info *opt, const char *sep) { struct strbuf msgbuf; @@ -233,8 +245,7 @@ void show_log(struct rev_info *opt, const char *sep) opt->diffopt.stat_sep = buffer; } } else if (opt->commit_format != CMIT_FMT_USERFORMAT) { - fputs(diff_get_color(opt->diffopt.color_diff, DIFF_COMMIT), - stdout); + fputs(diff_get_color_opt(&opt->diffopt, DIFF_COMMIT), stdout); if (opt->commit_format != CMIT_FMT_ONELINE) fputs("commit ", stdout); if (commit->object.flags & BOUNDARY) @@ -254,8 +265,7 @@ void show_log(struct rev_info *opt, const char *sep) diff_unique_abbrev(parent->object.sha1, abbrev_commit)); show_decorations(commit); - printf("%s", - diff_get_color(opt->diffopt.color_diff, DIFF_RESET)); + printf("%s", diff_get_color_opt(&opt->diffopt, DIFF_RESET)); putchar(opt->commit_format == CMIT_FMT_ONELINE ? ' ' : '\n'); if (opt->reflog_info) { show_reflog_message(opt->reflog_info, @@ -273,7 +283,8 @@ void show_log(struct rev_info *opt, const char *sep) */ strbuf_init(&msgbuf, 0); pretty_print_commit(opt->commit_format, commit, &msgbuf, - abbrev, subject, extra_headers, opt->date_mode); + abbrev, subject, extra_headers, opt->date_mode, + has_non_ascii(opt->add_signoff)); if (opt->add_signoff) append_signoff(&msgbuf, opt->add_signoff); @@ -42,9 +42,10 @@ int read_mailmap(struct path_list *map, const char *filename, char **repo_abbrev continue; if (right_bracket == left_bracket + 1) continue; - for (end_of_name = left_bracket; end_of_name != buffer - && isspace(end_of_name[-1]); end_of_name--) - /* keep on looking */ + for (end_of_name = left_bracket; + end_of_name != buffer && isspace(end_of_name[-1]); + end_of_name--) + ; /* keep on looking */ if (end_of_name == buffer) continue; name = xmalloc(end_of_name - buffer + 1); diff --git a/merge-recursive.c b/merge-recursive.c index 6c6f595fbc..9a1e2f269d 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -366,7 +366,7 @@ static struct path_list *get_renames(struct tree *tree, renames = xcalloc(1, sizeof(struct path_list)); diff_setup(&opts); - opts.recursive = 1; + DIFF_OPT_SET(&opts, RECURSIVE); opts.detect_rename = DIFF_DETECT_RENAME; opts.rename_limit = rename_limit; opts.output_format = DIFF_FORMAT_NO_OUTPUT; @@ -1,7 +1,5 @@ #include "cache.h" -#include <sys/select.h> - /* * This is split up from the rest of git so that we might do * something different on Windows, for example. diff --git a/parse-options.c b/parse-options.c index cc09c98ec3..e12b428c0a 100644 --- a/parse-options.c +++ b/parse-options.c @@ -40,24 +40,53 @@ static int get_value(struct optparse_t *p, const struct option *opt, int flags) { const char *s, *arg; - arg = p->opt ? p->opt : (p->argc > 1 ? p->argv[1] : NULL); + const int unset = flags & OPT_UNSET; - if (p->opt && (flags & OPT_UNSET)) + if (unset && p->opt) return opterror(opt, "takes no value", flags); + if (unset && (opt->flags & PARSE_OPT_NONEG)) + return opterror(opt, "isn't available", flags); - switch (opt->type) { - case OPTION_BOOLEAN: - if (!(flags & OPT_SHORT) && p->opt) + if (!(flags & OPT_SHORT) && p->opt) { + switch (opt->type) { + case OPTION_CALLBACK: + if (!(opt->flags & PARSE_OPT_NOARG)) + break; + /* FALLTHROUGH */ + case OPTION_BOOLEAN: + case OPTION_BIT: + case OPTION_SET_INT: + case OPTION_SET_PTR: return opterror(opt, "takes no value", flags); - if (flags & OPT_UNSET) - *(int *)opt->value = 0; + default: + break; + } + } + + arg = p->opt ? p->opt : (p->argc > 1 ? p->argv[1] : NULL); + switch (opt->type) { + case OPTION_BIT: + if (unset) + *(int *)opt->value &= ~opt->defval; else - (*(int *)opt->value)++; + *(int *)opt->value |= opt->defval; + return 0; + + case OPTION_BOOLEAN: + *(int *)opt->value = unset ? 0 : *(int *)opt->value + 1; + return 0; + + case OPTION_SET_INT: + *(int *)opt->value = unset ? 0 : opt->defval; + return 0; + + case OPTION_SET_PTR: + *(void **)opt->value = unset ? NULL : (void *)opt->defval; return 0; case OPTION_STRING: - if (flags & OPT_UNSET) { - *(const char **)opt->value = (const char *)NULL; + if (unset) { + *(const char **)opt->value = NULL; return 0; } if (opt->flags & PARSE_OPT_OPTARG && (!arg || *arg == '-')) { @@ -70,13 +99,10 @@ static int get_value(struct optparse_t *p, return 0; case OPTION_CALLBACK: - if (flags & OPT_UNSET) + if (unset) return (*opt->callback)(opt, NULL, 1); - if (opt->flags & PARSE_OPT_NOARG) { - if (p->opt && !(flags & OPT_SHORT)) - return opterror(opt, "takes no value", flags); + if (opt->flags & PARSE_OPT_NOARG) return (*opt->callback)(opt, NULL, 0); - } if (opt->flags & PARSE_OPT_OPTARG && (!arg || *arg == '-')) return (*opt->callback)(opt, NULL, 0); if (!arg) @@ -84,7 +110,7 @@ static int get_value(struct optparse_t *p, return (*opt->callback)(opt, get_arg(p), 0); case OPTION_INTEGER: - if (flags & OPT_UNSET) { + if (unset) { *(int *)opt->value = 0; return 0; } @@ -119,8 +145,8 @@ static int parse_long_opt(struct optparse_t *p, const char *arg, const struct option *options) { const char *arg_end = strchr(arg, '='); - const struct option *abbrev_option = NULL; - int abbrev_flags = 0; + const struct option *abbrev_option = NULL, *ambiguous_option = NULL; + int abbrev_flags = 0, ambiguous_flags = 0; if (!arg_end) arg_end = arg + strlen(arg); @@ -137,16 +163,16 @@ static int parse_long_opt(struct optparse_t *p, const char *arg, /* abbreviated? */ if (!strncmp(options->long_name, arg, arg_end - arg)) { is_abbreviated: - if (abbrev_option) - return error("Ambiguous option: %s " - "(could be --%s%s or --%s%s)", - arg, - (flags & OPT_UNSET) ? - "no-" : "", - options->long_name, - (abbrev_flags & OPT_UNSET) ? - "no-" : "", - abbrev_option->long_name); + if (abbrev_option) { + /* + * If this is abbreviated, it is + * ambiguous. So when there is no + * exact match later, we need to + * error out. + */ + ambiguous_option = abbrev_option; + ambiguous_flags = abbrev_flags; + } if (!(flags & OPT_UNSET) && *arg_end) p->opt = arg_end + 1; abbrev_option = options; @@ -176,11 +202,23 @@ is_abbreviated: } return get_value(p, options, flags); } + + if (ambiguous_option) + return error("Ambiguous option: %s " + "(could be --%s%s or --%s%s)", + arg, + (ambiguous_flags & OPT_UNSET) ? "no-" : "", + ambiguous_option->long_name, + (abbrev_flags & OPT_UNSET) ? "no-" : "", + abbrev_option->long_name); if (abbrev_option) return get_value(p, abbrev_option, abbrev_flags); return error("unknown option `%s'", arg); } +static NORETURN void usage_with_options_internal(const char * const *, + const struct option *, int); + int parse_options(int argc, const char **argv, const struct option *options, const char * const usagestr[], int flags) { @@ -214,6 +252,8 @@ int parse_options(int argc, const char **argv, const struct option *options, break; } + if (!strcmp(arg + 2, "help-all")) + usage_with_options_internal(usagestr, options, 1); if (!strcmp(arg + 2, "help")) usage_with_options(usagestr, options); if (parse_long_opt(&args, arg + 2, options)) @@ -228,8 +268,8 @@ int parse_options(int argc, const char **argv, const struct option *options, #define USAGE_OPTS_WIDTH 24 #define USAGE_GAP 2 -void usage_with_options(const char * const *usagestr, - const struct option *opts) +void usage_with_options_internal(const char * const *usagestr, + const struct option *opts, int full) { fprintf(stderr, "usage: %s\n", *usagestr++); while (*usagestr && **usagestr) @@ -250,6 +290,8 @@ void usage_with_options(const char * const *usagestr, fprintf(stderr, "%s\n", opts->help); continue; } + if (!full && (opts->flags & PARSE_OPT_HIDDEN)) + continue; pos = fprintf(stderr, " "); if (opts->short_name) @@ -283,7 +325,7 @@ void usage_with_options(const char * const *usagestr, pos += fprintf(stderr, " ..."); } break; - default: + default: /* OPTION_{BIT,BOOLEAN,SET_INT,SET_PTR} */ break; } @@ -300,8 +342,15 @@ void usage_with_options(const char * const *usagestr, exit(129); } +void usage_with_options(const char * const *usagestr, + const struct option *opts) +{ + usage_with_options_internal(usagestr, opts, 0); +} + /*----- some often used options -----*/ #include "cache.h" + int parse_opt_abbrev_cb(const struct option *opt, const char *arg, int unset) { int v; diff --git a/parse-options.h b/parse-options.h index 3a470e5eb8..102ac31fb7 100644 --- a/parse-options.h +++ b/parse-options.h @@ -2,9 +2,15 @@ #define PARSE_OPTIONS_H enum parse_opt_type { + /* special types */ OPTION_END, OPTION_GROUP, - OPTION_BOOLEAN, + /* options with no arguments */ + OPTION_BIT, + OPTION_BOOLEAN, /* _INCR would have been a better name */ + OPTION_SET_INT, + OPTION_SET_PTR, + /* options with arguments (usually) */ OPTION_STRING, OPTION_INTEGER, OPTION_CALLBACK, @@ -17,11 +23,53 @@ enum parse_opt_flags { enum parse_opt_option_flags { PARSE_OPT_OPTARG = 1, PARSE_OPT_NOARG = 2, + PARSE_OPT_NONEG = 4, + PARSE_OPT_HIDDEN = 8, }; struct option; typedef int parse_opt_cb(const struct option *, const char *arg, int unset); +/* + * `type`:: + * holds the type of the option, you must have an OPTION_END last in your + * array. + * + * `short_name`:: + * the character to use as a short option name, '\0' if none. + * + * `long_name`:: + * the long option name, without the leading dashes, NULL if none. + * + * `value`:: + * stores pointers to the values to be filled. + * + * `argh`:: + * token to explain the kind of argument this option wants. Keep it + * homogenous across the repository. + * + * `help`:: + * the short help associated to what the option does. + * Must never be NULL (except for OPTION_END). + * OPTION_GROUP uses this pointer to store the group header. + * + * `flags`:: + * mask of parse_opt_option_flags. + * PARSE_OPT_OPTARG: says that the argument is optionnal (not for BOOLEANs) + * PARSE_OPT_NOARG: says that this option takes no argument, for CALLBACKs + * PARSE_OPT_NONEG: says that this option cannot be negated + * PARSE_OPT_HIDDEN this option is skipped in the default usage, showed in + * the long one. + * + * `callback`:: + * pointer to the callback to use for OPTION_CALLBACK. + * + * `defval`:: + * default value to fill (*->value) with for PARSE_OPT_OPTARG. + * OPTION_{BIT,SET_INT,SET_PTR} store the {mask,integer,pointer} to put in + * the value when met. + * CALLBACKS can use it like they want. + */ struct option { enum parse_opt_type type; int short_name; @@ -32,14 +80,15 @@ struct option { int flags; parse_opt_cb *callback; - /* holds default value for PARSE_OPT_OPTARG, - though callbacks can use it like they want */ intptr_t defval; }; #define OPT_END() { OPTION_END } #define OPT_GROUP(h) { OPTION_GROUP, 0, NULL, NULL, NULL, (h) } +#define OPT_BIT(s, l, v, h, b) { OPTION_BIT, (s), (l), (v), NULL, (h), 0, NULL, (b) } #define OPT_BOOLEAN(s, l, v, h) { OPTION_BOOLEAN, (s), (l), (v), NULL, (h) } +#define OPT_SET_INT(s, l, v, h, i) { OPTION_SET_INT, (s), (l), (v), NULL, (h), 0, NULL, (i) } +#define OPT_SET_PTR(s, l, v, h, p) { OPTION_SET_PTR, (s), (l), (v), NULL, (h), 0, NULL, (p) } #define OPT_INTEGER(s, l, v, h) { OPTION_INTEGER, (s), (l), (v), NULL, (h) } #define OPT_STRING(s, l, v, a, h) { OPTION_STRING, (s), (l), (v), (a), (h) } #define OPT_CALLBACK(s, l, v, a, h, f) \ diff --git a/patch-ids.c b/patch-ids.c index a288fac992..3be5d3165e 100644 --- a/patch-ids.c +++ b/patch-ids.c @@ -121,7 +121,7 @@ int init_patch_ids(struct patch_ids *ids) { memset(ids, 0, sizeof(*ids)); diff_setup(&ids->diffopts); - ids->diffopts.recursive = 1; + DIFF_OPT_SET(&ids->diffopts, RECURSIVE); if (diff_setup_done(&ids->diffopts) < 0) return error("diff_setup_done failed"); return 0; diff --git a/peek-remote.c b/peek-remote.c deleted file mode 100644 index 8d20f7c9c6..0000000000 --- a/peek-remote.c +++ /dev/null @@ -1,73 +0,0 @@ -#include "cache.h" -#include "refs.h" -#include "pkt-line.h" - -static const char peek_remote_usage[] = -"git-peek-remote [--upload-pack=<git-upload-pack>] [<host>:]<directory>"; -static const char *uploadpack = "git-upload-pack"; - -static int peek_remote(int fd[2], unsigned flags) -{ - struct ref *ref; - - get_remote_heads(fd[0], &ref, 0, NULL, flags); - packet_flush(fd[1]); - - while (ref) { - printf("%s %s\n", sha1_to_hex(ref->old_sha1), ref->name); - ref = ref->next; - } - return 0; -} - -int main(int argc, char **argv) -{ - int i, ret; - char *dest = NULL; - int fd[2]; - struct child_process *conn; - int nongit = 0; - unsigned flags = 0; - - setup_git_directory_gently(&nongit); - - for (i = 1; i < argc; i++) { - char *arg = argv[i]; - - if (*arg == '-') { - if (!prefixcmp(arg, "--upload-pack=")) { - uploadpack = arg + 14; - continue; - } - if (!prefixcmp(arg, "--exec=")) { - uploadpack = arg + 7; - continue; - } - if (!strcmp("--tags", arg)) { - flags |= REF_TAGS; - continue; - } - if (!strcmp("--heads", arg)) { - flags |= REF_HEADS; - continue; - } - if (!strcmp("--refs", arg)) { - flags |= REF_NORMAL; - continue; - } - usage(peek_remote_usage); - } - dest = arg; - break; - } - - if (!dest || i != argc - 1) - usage(peek_remote_usage); - - conn = git_connect(fd, dest, uploadpack, 0); - ret = peek_remote(fd, flags); - close(fd[0]); - close(fd[1]); - ret |= finish_connect(conn); - return !!ret; -} diff --git a/perl/Git.pm b/perl/Git.pm index 3f4080cbf8..a2812ea612 100644 --- a/perl/Git.pm +++ b/perl/Git.pm @@ -549,6 +549,72 @@ sub config_bool { }; } +=item config_int ( VARIABLE ) + +Retrieve the integer configuration C<VARIABLE>. The return value +is simple decimal number. An optional value suffix of 'k', 'm', +or 'g' in the config file will cause the value to be multiplied +by 1024, 1048576 (1024^2), or 1073741824 (1024^3) prior to output. +It would return C<undef> if configuration variable is not defined, + +Must be called on a repository instance. + +This currently wraps command('config') so it is not so fast. + +=cut + +sub config_int { + my ($self, $var) = @_; + $self->repo_path() + or throw Error::Simple("not a repository"); + + try { + return $self->command_oneline('config', '--int', '--get', $var); + } catch Git::Error::Command with { + my $E = shift; + if ($E->value() == 1) { + # Key not found. + return undef; + } else { + throw $E; + } + }; +} + +=item get_colorbool ( NAME ) + +Finds if color should be used for NAMEd operation from the configuration, +and returns boolean (true for "use color", false for "do not use color"). + +=cut + +sub get_colorbool { + my ($self, $var) = @_; + my $stdout_to_tty = (-t STDOUT) ? "true" : "false"; + my $use_color = $self->command_oneline('config', '--get-colorbool', + $var, $stdout_to_tty); + return ($use_color eq 'true'); +} + +=item get_color ( SLOT, COLOR ) + +Finds color for SLOT from the configuration, while defaulting to COLOR, +and returns the ANSI color escape sequence: + + print $repo->get_color("color.interactive.prompt", "underline blue white"); + print "some text"; + print $repo->get_color("", "normal"); + +=cut + +sub get_color { + my ($self, $slot, $default) = @_; + my $color = $self->command_oneline('config', '--get-color', $slot, $default); + if (!defined $color) { + $color = ""; + } + return $color; +} =item ident ( TYPE | IDENTSTR ) @@ -812,7 +878,7 @@ sub _cmd_exec { $self->wc_subdir() and chdir($self->wc_subdir()); } _execv_git_cmd(@args); - die "exec failed: $!"; + die qq[exec "@args" failed: $!]; } # Execute the given Git command ($_[0]) with arguments ($_[1..]) diff --git a/pretty.c b/pretty.c new file mode 100644 index 0000000000..9db75b4e4f --- /dev/null +++ b/pretty.c @@ -0,0 +1,825 @@ +#include "cache.h" +#include "commit.h" +#include "utf8.h" +#include "diff.h" +#include "revision.h" + +static struct cmt_fmt_map { + const char *n; + size_t cmp_len; + enum cmit_fmt v; +} cmt_fmts[] = { + { "raw", 1, CMIT_FMT_RAW }, + { "medium", 1, CMIT_FMT_MEDIUM }, + { "short", 1, CMIT_FMT_SHORT }, + { "email", 1, CMIT_FMT_EMAIL }, + { "full", 5, CMIT_FMT_FULL }, + { "fuller", 5, CMIT_FMT_FULLER }, + { "oneline", 1, CMIT_FMT_ONELINE }, + { "format:", 7, CMIT_FMT_USERFORMAT}, +}; + +static char *user_format; + +enum cmit_fmt get_commit_format(const char *arg) +{ + int i; + + if (!arg || !*arg) + return CMIT_FMT_DEFAULT; + if (*arg == '=') + arg++; + if (!prefixcmp(arg, "format:")) { + if (user_format) + free(user_format); + user_format = xstrdup(arg + 7); + return CMIT_FMT_USERFORMAT; + } + for (i = 0; i < ARRAY_SIZE(cmt_fmts); i++) { + if (!strncmp(arg, cmt_fmts[i].n, cmt_fmts[i].cmp_len) && + !strncmp(arg, cmt_fmts[i].n, strlen(arg))) + return cmt_fmts[i].v; + } + + die("invalid --pretty format: %s", arg); +} + +/* + * Generic support for pretty-printing the header + */ +static int get_one_line(const char *msg) +{ + int ret = 0; + + for (;;) { + char c = *msg++; + if (!c) + break; + ret++; + if (c == '\n') + break; + } + return ret; +} + +/* High bit set, or ISO-2022-INT */ +int non_ascii(int ch) +{ + ch = (ch & 0xff); + return ((ch & 0x80) || (ch == 0x1b)); +} + +static int is_rfc2047_special(char ch) +{ + return (non_ascii(ch) || (ch == '=') || (ch == '?') || (ch == '_')); +} + +static void add_rfc2047(struct strbuf *sb, const char *line, int len, + const char *encoding) +{ + int i, last; + + for (i = 0; i < len; i++) { + int ch = line[i]; + if (non_ascii(ch)) + goto needquote; + if ((i + 1 < len) && (ch == '=' && line[i+1] == '?')) + goto needquote; + } + strbuf_add(sb, line, len); + return; + +needquote: + strbuf_grow(sb, len * 3 + strlen(encoding) + 100); + strbuf_addf(sb, "=?%s?q?", encoding); + for (i = last = 0; i < len; i++) { + unsigned ch = line[i] & 0xFF; + /* + * We encode ' ' using '=20' even though rfc2047 + * allows using '_' for readability. Unfortunately, + * many programs do not understand this and just + * leave the underscore in place. + */ + if (is_rfc2047_special(ch) || ch == ' ') { + strbuf_add(sb, line + last, i - last); + strbuf_addf(sb, "=%02X", ch); + last = i + 1; + } + } + strbuf_add(sb, line + last, len - last); + strbuf_addstr(sb, "?="); +} + +static void add_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb, + const char *line, enum date_mode dmode, + const char *encoding) +{ + char *date; + int namelen; + unsigned long time; + int tz; + const char *filler = " "; + + if (fmt == CMIT_FMT_ONELINE) + return; + date = strchr(line, '>'); + if (!date) + return; + namelen = ++date - line; + time = strtoul(date, &date, 10); + tz = strtol(date, NULL, 10); + + if (fmt == CMIT_FMT_EMAIL) { + char *name_tail = strchr(line, '<'); + int display_name_length; + if (!name_tail) + return; + while (line < name_tail && isspace(name_tail[-1])) + name_tail--; + display_name_length = name_tail - line; + filler = ""; + strbuf_addstr(sb, "From: "); + add_rfc2047(sb, line, display_name_length, encoding); + strbuf_add(sb, name_tail, namelen - display_name_length); + strbuf_addch(sb, '\n'); + } else { + strbuf_addf(sb, "%s: %.*s%.*s\n", what, + (fmt == CMIT_FMT_FULLER) ? 4 : 0, + filler, namelen, line); + } + switch (fmt) { + case CMIT_FMT_MEDIUM: + strbuf_addf(sb, "Date: %s\n", show_date(time, tz, dmode)); + break; + case CMIT_FMT_EMAIL: + strbuf_addf(sb, "Date: %s\n", show_date(time, tz, DATE_RFC2822)); + break; + case CMIT_FMT_FULLER: + strbuf_addf(sb, "%sDate: %s\n", what, show_date(time, tz, dmode)); + break; + default: + /* notin' */ + break; + } +} + +static int is_empty_line(const char *line, int *len_p) +{ + int len = *len_p; + while (len && isspace(line[len-1])) + len--; + *len_p = len; + return !len; +} + +static void add_merge_info(enum cmit_fmt fmt, struct strbuf *sb, + const struct commit *commit, int abbrev) +{ + struct commit_list *parent = commit->parents; + + if ((fmt == CMIT_FMT_ONELINE) || (fmt == CMIT_FMT_EMAIL) || + !parent || !parent->next) + return; + + strbuf_addstr(sb, "Merge:"); + + while (parent) { + struct commit *p = parent->item; + const char *hex = NULL; + const char *dots; + if (abbrev) + hex = find_unique_abbrev(p->object.sha1, abbrev); + if (!hex) + hex = sha1_to_hex(p->object.sha1); + dots = (abbrev && strlen(hex) != 40) ? "..." : ""; + parent = parent->next; + + strbuf_addf(sb, " %s%s", hex, dots); + } + strbuf_addch(sb, '\n'); +} + +static char *get_header(const struct commit *commit, const char *key) +{ + int key_len = strlen(key); + const char *line = commit->buffer; + + for (;;) { + const char *eol = strchr(line, '\n'), *next; + + if (line == eol) + return NULL; + if (!eol) { + eol = line + strlen(line); + next = NULL; + } else + next = eol + 1; + if (eol - line > key_len && + !strncmp(line, key, key_len) && + line[key_len] == ' ') { + return xmemdupz(line + key_len + 1, eol - line - key_len - 1); + } + line = next; + } +} + +static char *replace_encoding_header(char *buf, const char *encoding) +{ + struct strbuf tmp; + size_t start, len; + char *cp = buf; + + /* guess if there is an encoding header before a \n\n */ + while (strncmp(cp, "encoding ", strlen("encoding "))) { + cp = strchr(cp, '\n'); + if (!cp || *++cp == '\n') + return buf; + } + start = cp - buf; + cp = strchr(cp, '\n'); + if (!cp) + return buf; /* should not happen but be defensive */ + len = cp + 1 - (buf + start); + + strbuf_init(&tmp, 0); + strbuf_attach(&tmp, buf, strlen(buf), strlen(buf) + 1); + if (is_encoding_utf8(encoding)) { + /* we have re-coded to UTF-8; drop the header */ + strbuf_remove(&tmp, start, len); + } else { + /* just replaces XXXX in 'encoding XXXX\n' */ + strbuf_splice(&tmp, start + strlen("encoding "), + len - strlen("encoding \n"), + encoding, strlen(encoding)); + } + return strbuf_detach(&tmp, NULL); +} + +static char *logmsg_reencode(const struct commit *commit, + const char *output_encoding) +{ + static const char *utf8 = "utf-8"; + const char *use_encoding; + char *encoding; + char *out; + + if (!*output_encoding) + return NULL; + encoding = get_header(commit, "encoding"); + use_encoding = encoding ? encoding : utf8; + if (!strcmp(use_encoding, output_encoding)) + if (encoding) /* we'll strip encoding header later */ + out = xstrdup(commit->buffer); + else + return NULL; /* nothing to do */ + else + out = reencode_string(commit->buffer, + output_encoding, use_encoding); + if (out) + out = replace_encoding_header(out, output_encoding); + + free(encoding); + return out; +} + +static void format_person_part(struct strbuf *sb, char part, + const char *msg, int len) +{ + int start, end, tz = 0; + unsigned long date; + char *ep; + + /* parse name */ + for (end = 0; end < len && msg[end] != '<'; end++) + ; /* do nothing */ + start = end + 1; + while (end > 0 && isspace(msg[end - 1])) + end--; + if (part == 'n') { /* name */ + strbuf_add(sb, msg, end); + return; + } + + if (start >= len) + return; + + /* parse email */ + for (end = start + 1; end < len && msg[end] != '>'; end++) + ; /* do nothing */ + + if (end >= len) + return; + + if (part == 'e') { /* email */ + strbuf_add(sb, msg + start, end - start); + return; + } + + /* parse date */ + for (start = end + 1; start < len && isspace(msg[start]); start++) + ; /* do nothing */ + if (start >= len) + return; + date = strtoul(msg + start, &ep, 10); + if (msg + start == ep) + return; + + if (part == 't') { /* date, UNIX timestamp */ + strbuf_add(sb, msg + start, ep - (msg + start)); + return; + } + + /* parse tz */ + for (start = ep - msg + 1; start < len && isspace(msg[start]); start++) + ; /* do nothing */ + if (start + 1 < len) { + tz = strtoul(msg + start + 1, NULL, 10); + if (msg[start] == '-') + tz = -tz; + } + + switch (part) { + case 'd': /* date */ + strbuf_addstr(sb, show_date(date, tz, DATE_NORMAL)); + return; + case 'D': /* date, RFC2822 style */ + strbuf_addstr(sb, show_date(date, tz, DATE_RFC2822)); + return; + case 'r': /* date, relative */ + strbuf_addstr(sb, show_date(date, tz, DATE_RELATIVE)); + return; + case 'i': /* date, ISO 8601 */ + strbuf_addstr(sb, show_date(date, tz, DATE_ISO8601)); + return; + } +} + +struct chunk { + size_t off; + size_t len; +}; + +struct format_commit_context { + const struct commit *commit; + + /* These offsets are relative to the start of the commit message. */ + int commit_header_parsed; + struct chunk subject; + struct chunk author; + struct chunk committer; + struct chunk encoding; + size_t body_off; + + /* The following ones are relative to the result struct strbuf. */ + struct chunk abbrev_commit_hash; + struct chunk abbrev_tree_hash; + struct chunk abbrev_parent_hashes; +}; + +static int add_again(struct strbuf *sb, struct chunk *chunk) +{ + if (chunk->len) { + strbuf_adddup(sb, chunk->off, chunk->len); + return 1; + } + + /* + * We haven't seen this chunk before. Our caller is surely + * going to add it the hard way now. Remember the most likely + * start of the to-be-added chunk: the current end of the + * struct strbuf. + */ + chunk->off = sb->len; + return 0; +} + +static void parse_commit_header(struct format_commit_context *context) +{ + const char *msg = context->commit->buffer; + int i; + enum { HEADER, SUBJECT, BODY } state; + + for (i = 0, state = HEADER; msg[i] && state < BODY; i++) { + int eol; + for (eol = i; msg[eol] && msg[eol] != '\n'; eol++) + ; /* do nothing */ + + if (state == SUBJECT) { + context->subject.off = i; + context->subject.len = eol - i; + i = eol; + } + if (i == eol) { + state++; + /* strip empty lines */ + while (msg[eol + 1] == '\n') + eol++; + } else if (!prefixcmp(msg + i, "author ")) { + context->author.off = i + 7; + context->author.len = eol - i - 7; + } else if (!prefixcmp(msg + i, "committer ")) { + context->committer.off = i + 10; + context->committer.len = eol - i - 10; + } else if (!prefixcmp(msg + i, "encoding ")) { + context->encoding.off = i + 9; + context->encoding.len = eol - i - 9; + } + i = eol; + } + context->body_off = i; + context->commit_header_parsed = 1; +} + +static void format_commit_item(struct strbuf *sb, const char *placeholder, + void *context) +{ + struct format_commit_context *c = context; + const struct commit *commit = c->commit; + const char *msg = commit->buffer; + struct commit_list *p; + + /* these are independent of the commit */ + switch (placeholder[0]) { + case 'C': + switch (placeholder[3]) { + case 'd': /* red */ + strbuf_addstr(sb, "\033[31m"); + return; + case 'e': /* green */ + strbuf_addstr(sb, "\033[32m"); + return; + case 'u': /* blue */ + strbuf_addstr(sb, "\033[34m"); + return; + case 's': /* reset color */ + strbuf_addstr(sb, "\033[m"); + return; + } + case 'n': /* newline */ + strbuf_addch(sb, '\n'); + return; + } + + /* these depend on the commit */ + if (!commit->object.parsed) + parse_object(commit->object.sha1); + + switch (placeholder[0]) { + case 'H': /* commit hash */ + strbuf_addstr(sb, sha1_to_hex(commit->object.sha1)); + return; + case 'h': /* abbreviated commit hash */ + if (add_again(sb, &c->abbrev_commit_hash)) + return; + strbuf_addstr(sb, find_unique_abbrev(commit->object.sha1, + DEFAULT_ABBREV)); + c->abbrev_commit_hash.len = sb->len - c->abbrev_commit_hash.off; + return; + case 'T': /* tree hash */ + strbuf_addstr(sb, sha1_to_hex(commit->tree->object.sha1)); + return; + case 't': /* abbreviated tree hash */ + if (add_again(sb, &c->abbrev_tree_hash)) + return; + strbuf_addstr(sb, find_unique_abbrev(commit->tree->object.sha1, + DEFAULT_ABBREV)); + c->abbrev_tree_hash.len = sb->len - c->abbrev_tree_hash.off; + return; + case 'P': /* parent hashes */ + for (p = commit->parents; p; p = p->next) { + if (p != commit->parents) + strbuf_addch(sb, ' '); + strbuf_addstr(sb, sha1_to_hex(p->item->object.sha1)); + } + return; + case 'p': /* abbreviated parent hashes */ + if (add_again(sb, &c->abbrev_parent_hashes)) + return; + for (p = commit->parents; p; p = p->next) { + if (p != commit->parents) + strbuf_addch(sb, ' '); + strbuf_addstr(sb, find_unique_abbrev( + p->item->object.sha1, DEFAULT_ABBREV)); + } + c->abbrev_parent_hashes.len = sb->len - + c->abbrev_parent_hashes.off; + return; + case 'm': /* left/right/bottom */ + strbuf_addch(sb, (commit->object.flags & BOUNDARY) + ? '-' + : (commit->object.flags & SYMMETRIC_LEFT) + ? '<' + : '>'); + return; + } + + /* For the rest we have to parse the commit header. */ + if (!c->commit_header_parsed) + parse_commit_header(c); + + switch (placeholder[0]) { + case 's': + strbuf_add(sb, msg + c->subject.off, c->subject.len); + return; + case 'a': + format_person_part(sb, placeholder[1], + msg + c->author.off, c->author.len); + return; + case 'c': + format_person_part(sb, placeholder[1], + msg + c->committer.off, c->committer.len); + return; + case 'e': + strbuf_add(sb, msg + c->encoding.off, c->encoding.len); + return; + case 'b': + strbuf_addstr(sb, msg + c->body_off); + return; + } +} + +void format_commit_message(const struct commit *commit, + const void *format, struct strbuf *sb) +{ + const char *placeholders[] = { + "H", /* commit hash */ + "h", /* abbreviated commit hash */ + "T", /* tree hash */ + "t", /* abbreviated tree hash */ + "P", /* parent hashes */ + "p", /* abbreviated parent hashes */ + "an", /* author name */ + "ae", /* author email */ + "ad", /* author date */ + "aD", /* author date, RFC2822 style */ + "ar", /* author date, relative */ + "at", /* author date, UNIX timestamp */ + "ai", /* author date, ISO 8601 */ + "cn", /* committer name */ + "ce", /* committer email */ + "cd", /* committer date */ + "cD", /* committer date, RFC2822 style */ + "cr", /* committer date, relative */ + "ct", /* committer date, UNIX timestamp */ + "ci", /* committer date, ISO 8601 */ + "e", /* encoding */ + "s", /* subject */ + "b", /* body */ + "Cred", /* red */ + "Cgreen", /* green */ + "Cblue", /* blue */ + "Creset", /* reset color */ + "n", /* newline */ + "m", /* left/right/bottom */ + NULL + }; + struct format_commit_context context; + + memset(&context, 0, sizeof(context)); + context.commit = commit; + strbuf_expand(sb, format, placeholders, format_commit_item, &context); +} + +static void pp_header(enum cmit_fmt fmt, + int abbrev, + enum date_mode dmode, + const char *encoding, + const struct commit *commit, + const char **msg_p, + struct strbuf *sb) +{ + int parents_shown = 0; + + for (;;) { + const char *line = *msg_p; + int linelen = get_one_line(*msg_p); + + if (!linelen) + return; + *msg_p += linelen; + + if (linelen == 1) + /* End of header */ + return; + + if (fmt == CMIT_FMT_RAW) { + strbuf_add(sb, line, linelen); + continue; + } + + if (!memcmp(line, "parent ", 7)) { + if (linelen != 48) + die("bad parent line in commit"); + continue; + } + + if (!parents_shown) { + struct commit_list *parent; + int num; + for (parent = commit->parents, num = 0; + parent; + parent = parent->next, num++) + ; + /* with enough slop */ + strbuf_grow(sb, num * 50 + 20); + add_merge_info(fmt, sb, commit, abbrev); + parents_shown = 1; + } + + /* + * MEDIUM == DEFAULT shows only author with dates. + * FULL shows both authors but not dates. + * FULLER shows both authors and dates. + */ + if (!memcmp(line, "author ", 7)) { + strbuf_grow(sb, linelen + 80); + add_user_info("Author", fmt, sb, line + 7, dmode, encoding); + } + if (!memcmp(line, "committer ", 10) && + (fmt == CMIT_FMT_FULL || fmt == CMIT_FMT_FULLER)) { + strbuf_grow(sb, linelen + 80); + add_user_info("Commit", fmt, sb, line + 10, dmode, encoding); + } + } +} + +static void pp_title_line(enum cmit_fmt fmt, + const char **msg_p, + struct strbuf *sb, + const char *subject, + const char *after_subject, + const char *encoding, + int plain_non_ascii) +{ + struct strbuf title; + + strbuf_init(&title, 80); + + for (;;) { + const char *line = *msg_p; + int linelen = get_one_line(line); + + *msg_p += linelen; + if (!linelen || is_empty_line(line, &linelen)) + break; + + strbuf_grow(&title, linelen + 2); + if (title.len) { + if (fmt == CMIT_FMT_EMAIL) { + strbuf_addch(&title, '\n'); + } + strbuf_addch(&title, ' '); + } + strbuf_add(&title, line, linelen); + } + + strbuf_grow(sb, title.len + 1024); + if (subject) { + strbuf_addstr(sb, subject); + add_rfc2047(sb, title.buf, title.len, encoding); + } else { + strbuf_addbuf(sb, &title); + } + strbuf_addch(sb, '\n'); + + if (plain_non_ascii) { + const char *header_fmt = + "MIME-Version: 1.0\n" + "Content-Type: text/plain; charset=%s\n" + "Content-Transfer-Encoding: 8bit\n"; + strbuf_addf(sb, header_fmt, encoding); + } + if (after_subject) { + strbuf_addstr(sb, after_subject); + } + if (fmt == CMIT_FMT_EMAIL) { + strbuf_addch(sb, '\n'); + } + strbuf_release(&title); +} + +static void pp_remainder(enum cmit_fmt fmt, + const char **msg_p, + struct strbuf *sb, + int indent) +{ + int first = 1; + for (;;) { + const char *line = *msg_p; + int linelen = get_one_line(line); + *msg_p += linelen; + + if (!linelen) + break; + + if (is_empty_line(line, &linelen)) { + if (first) + continue; + if (fmt == CMIT_FMT_SHORT) + break; + } + first = 0; + + strbuf_grow(sb, linelen + indent + 20); + if (indent) { + memset(sb->buf + sb->len, ' ', indent); + strbuf_setlen(sb, sb->len + indent); + } + strbuf_add(sb, line, linelen); + strbuf_addch(sb, '\n'); + } +} + +void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, + struct strbuf *sb, int abbrev, + const char *subject, const char *after_subject, + enum date_mode dmode, int plain_non_ascii) +{ + unsigned long beginning_of_body; + int indent = 4; + const char *msg = commit->buffer; + char *reencoded; + const char *encoding; + + if (fmt == CMIT_FMT_USERFORMAT) { + format_commit_message(commit, user_format, sb); + return; + } + + encoding = (git_log_output_encoding + ? git_log_output_encoding + : git_commit_encoding); + if (!encoding) + encoding = "utf-8"; + reencoded = logmsg_reencode(commit, encoding); + if (reencoded) { + msg = reencoded; + } + + if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL) + indent = 0; + + /* After-subject is used to pass in Content-Type: multipart + * MIME header; in that case we do not have to do the + * plaintext content type even if the commit message has + * non 7-bit ASCII character. Otherwise, check if we need + * to say this is not a 7-bit ASCII. + */ + if (fmt == CMIT_FMT_EMAIL && !after_subject) { + int i, ch, in_body; + + for (in_body = i = 0; (ch = msg[i]); i++) { + if (!in_body) { + /* author could be non 7-bit ASCII but + * the log may be so; skip over the + * header part first. + */ + if (ch == '\n' && msg[i+1] == '\n') + in_body = 1; + } + else if (non_ascii(ch)) { + plain_non_ascii = 1; + break; + } + } + } + + pp_header(fmt, abbrev, dmode, encoding, commit, &msg, sb); + if (fmt != CMIT_FMT_ONELINE && !subject) { + strbuf_addch(sb, '\n'); + } + + /* Skip excess blank lines at the beginning of body, if any... */ + for (;;) { + int linelen = get_one_line(msg); + int ll = linelen; + if (!linelen) + break; + if (!is_empty_line(msg, &ll)) + break; + msg += linelen; + } + + /* These formats treat the title line specially. */ + if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL) + pp_title_line(fmt, &msg, sb, subject, + after_subject, encoding, plain_non_ascii); + + beginning_of_body = sb->len; + if (fmt != CMIT_FMT_ONELINE) + pp_remainder(fmt, &msg, sb, indent); + strbuf_rtrim(sb); + + /* Make sure there is an EOLN for the non-oneline case */ + if (fmt != CMIT_FMT_ONELINE) + strbuf_addch(sb, '\n'); + + /* + * The caller may append additional body text in e-mail + * format. Make sure we did not strip the blank line + * between the header and the body. + */ + if (fmt == CMIT_FMT_EMAIL && sb->len <= beginning_of_body) + strbuf_addch(sb, '\n'); + free(reencoded); +} diff --git a/progress.c b/progress.c index 3f6a602a53..d19f80c0bb 100644 --- a/progress.c +++ b/progress.c @@ -14,12 +14,12 @@ #define TP_IDX_MAX 8 struct throughput { + off_t curr_total; + off_t prev_total; struct timeval prev_tv; - off_t total; - unsigned long count; - unsigned long avg_bytes; - unsigned long last_bytes[TP_IDX_MAX]; + unsigned int avg_bytes; unsigned int avg_misecs; + unsigned int last_bytes[TP_IDX_MAX]; unsigned int last_misecs[TP_IDX_MAX]; unsigned int idx; char display[32]; @@ -69,9 +69,9 @@ static void clear_progress_signal(void) progress_update = 0; } -static int display(struct progress *progress, unsigned n, int done) +static int display(struct progress *progress, unsigned n, const char *done) { - char *eol, *tp; + const char *eol, *tp; if (progress->delay) { if (!progress_update || --progress->delay) @@ -90,7 +90,7 @@ static int display(struct progress *progress, unsigned n, int done) progress->last_value = n; tp = (progress->throughput) ? progress->throughput->display : ""; - eol = done ? ", done. \n" : " \r"; + eol = done ? done : " \r"; if (progress->total) { unsigned percent = n * 100 / progress->total; if (percent != progress->last_percent || progress_update) { @@ -98,11 +98,13 @@ static int display(struct progress *progress, unsigned n, int done) fprintf(stderr, "%s: %3u%% (%u/%u)%s%s", progress->title, percent, n, progress->total, tp, eol); + fflush(stderr); progress_update = 0; return 1; } } else if (progress_update) { fprintf(stderr, "%s: %u%s%s", progress->title, n, tp, eol); + fflush(stderr); progress_update = 0; return 1; } @@ -110,7 +112,31 @@ static int display(struct progress *progress, unsigned n, int done) return 0; } -void display_throughput(struct progress *progress, unsigned long n) +static void throughput_string(struct throughput *tp, off_t total, + unsigned int rate) +{ + int l = sizeof(tp->display); + if (total > 1 << 30) { + l -= snprintf(tp->display, l, ", %u.%2.2u GiB", + (int)(total >> 30), + (int)(total & ((1 << 30) - 1)) / 10737419); + } else if (total > 1 << 20) { + l -= snprintf(tp->display, l, ", %u.%2.2u MiB", + (int)(total >> 20), + ((int)(total & ((1 << 20) - 1)) * 100) >> 20); + } else if (total > 1 << 10) { + l -= snprintf(tp->display, l, ", %u.%2.2u KiB", + (int)(total >> 10), + ((int)(total & ((1 << 10) - 1)) * 100) >> 10); + } else { + l -= snprintf(tp->display, l, ", %u bytes", (int)total); + } + if (rate) + snprintf(tp->display + sizeof(tp->display) - l, l, + " | %u KiB/s", rate); +} + +void display_throughput(struct progress *progress, off_t total) { struct throughput *tp; struct timeval tv; @@ -124,13 +150,13 @@ void display_throughput(struct progress *progress, unsigned long n) if (!tp) { progress->throughput = tp = calloc(1, sizeof(*tp)); - if (tp) + if (tp) { + tp->prev_total = tp->curr_total = total; tp->prev_tv = tv; + } return; } - - tp->total += n; - tp->count += n; + tp->curr_total = total; /* * We have x = bytes and y = microsecs. We want z = KiB/s: @@ -151,47 +177,29 @@ void display_throughput(struct progress *progress, unsigned long n) misecs += (int)(tv.tv_usec - tp->prev_tv.tv_usec) / 977; if (misecs > 512) { - int l = sizeof(tp->display); + unsigned int count, rate; + + count = total - tp->prev_total; + tp->prev_total = total; tp->prev_tv = tv; - tp->avg_bytes += tp->count; + tp->avg_bytes += count; tp->avg_misecs += misecs; - - if (tp->total > 1 << 30) { - l -= snprintf(tp->display, l, ", %u.%2.2u GiB", - (int)(tp->total >> 30), - (int)(tp->total & ((1 << 30) - 1)) / 10737419); - } else if (tp->total > 1 << 20) { - l -= snprintf(tp->display, l, ", %u.%2.2u MiB", - (int)(tp->total >> 20), - ((int)(tp->total & ((1 << 20) - 1)) - * 100) >> 20); - } else if (tp->total > 1 << 10) { - l -= snprintf(tp->display, l, ", %u.%2.2u KiB", - (int)(tp->total >> 10), - ((int)(tp->total & ((1 << 10) - 1)) - * 100) >> 10); - } else { - l -= snprintf(tp->display, l, ", %u bytes", - (int)tp->total); - } - snprintf(tp->display + sizeof(tp->display) - l, l, - " | %lu KiB/s", tp->avg_bytes / tp->avg_misecs); - + rate = tp->avg_bytes / tp->avg_misecs; tp->avg_bytes -= tp->last_bytes[tp->idx]; tp->avg_misecs -= tp->last_misecs[tp->idx]; - tp->last_bytes[tp->idx] = tp->count; + tp->last_bytes[tp->idx] = count; tp->last_misecs[tp->idx] = misecs; tp->idx = (tp->idx + 1) % TP_IDX_MAX; - tp->count = 0; + throughput_string(tp, total, rate); if (progress->last_value != -1 && progress_update) - display(progress, progress->last_value, 0); + display(progress, progress->last_value, NULL); } } int display_progress(struct progress *progress, unsigned n) { - return progress ? display(progress, n, 0) : 0; + return progress ? display(progress, n, NULL) : 0; } struct progress *start_progress_delay(const char *title, unsigned total, @@ -201,6 +209,7 @@ struct progress *start_progress_delay(const char *title, unsigned total, if (!progress) { /* unlikely, but here's a good fallback */ fprintf(stderr, "%s...\n", title); + fflush(stderr); return NULL; } progress->title = title; @@ -221,14 +230,27 @@ struct progress *start_progress(const char *title, unsigned total) void stop_progress(struct progress **p_progress) { + stop_progress_msg(p_progress, "done"); +} + +void stop_progress_msg(struct progress **p_progress, const char *msg) +{ struct progress *progress = *p_progress; if (!progress) return; *p_progress = NULL; if (progress->last_value != -1) { /* Force the last update */ + char buf[strlen(msg) + 5]; + struct throughput *tp = progress->throughput; + if (tp) { + unsigned int rate = !tp->avg_misecs ? 0 : + tp->avg_bytes / tp->avg_misecs; + throughput_string(tp, tp->curr_total, rate); + } progress_update = 1; - display(progress, progress->last_value, 1); + sprintf(buf, ", %s.\n", msg); + display(progress, progress->last_value, buf); } clear_progress_signal(); free(progress->throughput); diff --git a/progress.h b/progress.h index 61cb68dfa5..611e4c4d42 100644 --- a/progress.h +++ b/progress.h @@ -3,11 +3,12 @@ struct progress; -void display_throughput(struct progress *progress, unsigned long n); +void display_throughput(struct progress *progress, off_t total); int display_progress(struct progress *progress, unsigned n); struct progress *start_progress(const char *title, unsigned total); struct progress *start_progress_delay(const char *title, unsigned total, unsigned percent_treshold, unsigned delay); void stop_progress(struct progress **progress); +void stop_progress_msg(struct progress **progress, const char *msg); #endif @@ -26,7 +26,7 @@ void sq_quote_buf(struct strbuf *dst, const char *src) strbuf_addch(dst, '\''); while (*src) { - size_t len = strcspn(src, "'\\"); + size_t len = strcspn(src, "'!"); strbuf_add(dst, src, len); src += len; while (need_bs_quote(*src)) { @@ -56,20 +56,13 @@ void sq_quote_print(FILE *stream, const char *src) fputc('\'', stream); } -void sq_quote_argv(struct strbuf *dst, const char** argv, int count, - size_t maxlen) +void sq_quote_argv(struct strbuf *dst, const char** argv, size_t maxlen) { int i; - /* Count argv if needed. */ - if (count < 0) { - for (count = 0; argv[count]; count++) - ; /* just counting */ - } - /* Copy into destination buffer. */ - strbuf_grow(dst, 32 * count); - for (i = 0; i < count; ++i) { + strbuf_grow(dst, 255); + for (i = 0; argv[i]; ++i) { strbuf_addch(dst, ' '); sq_quote_buf(dst, argv[i]); if (maxlen && dst->len > maxlen) @@ -131,7 +124,8 @@ static signed char const sq_lookup[256] = { /* 0x80 */ /* set to 0 */ }; -static inline int sq_must_quote(char c) { +static inline int sq_must_quote(char c) +{ return sq_lookup[(unsigned char)c] + quote_path_fully > 0; } @@ -31,8 +31,7 @@ extern void sq_quote_print(FILE *stream, const char *src); extern void sq_quote_buf(struct strbuf *, const char *src); -extern void sq_quote_argv(struct strbuf *, const char **argv, int count, - size_t maxlen); +extern void sq_quote_argv(struct strbuf *, const char **argv, size_t maxlen); /* This unwraps what sq_quote() produces in place, but returns * NULL if the input does not look like what sq_quote would have diff --git a/read-cache.c b/read-cache.c index 056b322fb0..7db55883d6 100644 --- a/read-cache.c +++ b/read-cache.c @@ -194,11 +194,12 @@ static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st) } int ie_match_stat(struct index_state *istate, - struct cache_entry *ce, struct stat *st, int options) + struct cache_entry *ce, struct stat *st, + unsigned int options) { unsigned int changed; - int ignore_valid = options & 01; - int assume_racy_is_modified = options & 02; + int ignore_valid = options & CE_MATCH_IGNORE_VALID; + int assume_racy_is_modified = options & CE_MATCH_RACY_IS_DIRTY; /* * If it's marked as always valid in the index, it's @@ -238,10 +239,11 @@ int ie_match_stat(struct index_state *istate, } int ie_modified(struct index_state *istate, - struct cache_entry *ce, struct stat *st, int really) + struct cache_entry *ce, struct stat *st, unsigned int options) { int changed, changed_fs; - changed = ie_match_stat(istate, ce, st, really); + + changed = ie_match_stat(istate, ce, st, options); if (!changed) return 0; /* @@ -387,6 +389,7 @@ int add_file_to_index(struct index_state *istate, const char *path, int verbose) int size, namelen, pos; struct stat st; struct cache_entry *ce; + unsigned ce_option = CE_MATCH_IGNORE_VALID|CE_MATCH_RACY_IS_DIRTY; if (lstat(path, &st)) die("%s: unable to stat (%s)", path, strerror(errno)); @@ -421,7 +424,7 @@ int add_file_to_index(struct index_state *istate, const char *path, int verbose) pos = index_name_pos(istate, ce->name, namelen); if (0 <= pos && !ce_stage(istate->cache[pos]) && - !ie_modified(istate, istate->cache[pos], &st, 1)) { + !ie_match_stat(istate, istate->cache[pos], &st, ce_option)) { /* Nothing changed, really */ free(ce); return 0; @@ -783,11 +786,13 @@ int add_index_entry(struct index_state *istate, struct cache_entry *ce, int opti * to link up the stat cache details with the proper files. */ static struct cache_entry *refresh_cache_ent(struct index_state *istate, - struct cache_entry *ce, int really, int *err) + struct cache_entry *ce, + unsigned int options, int *err) { struct stat st; struct cache_entry *updated; int changed, size; + int ignore_valid = options & CE_MATCH_IGNORE_VALID; if (lstat(ce->name, &st) < 0) { if (err) @@ -795,16 +800,23 @@ static struct cache_entry *refresh_cache_ent(struct index_state *istate, return NULL; } - changed = ie_match_stat(istate, ce, &st, really); + changed = ie_match_stat(istate, ce, &st, options); if (!changed) { - if (really && assume_unchanged && + /* + * The path is unchanged. If we were told to ignore + * valid bit, then we did the actual stat check and + * found that the entry is unmodified. If the entry + * is not marked VALID, this is the place to mark it + * valid again, under "assume unchanged" mode. + */ + if (ignore_valid && assume_unchanged && !(ce->ce_flags & htons(CE_VALID))) ; /* mark this one VALID again */ else return ce; } - if (ie_modified(istate, ce, &st, really)) { + if (ie_modified(istate, ce, &st, options)) { if (err) *err = EINVAL; return NULL; @@ -815,13 +827,14 @@ static struct cache_entry *refresh_cache_ent(struct index_state *istate, memcpy(updated, ce, size); fill_stat_cache_info(updated, &st); - /* In this case, if really is not set, we should leave - * CE_VALID bit alone. Otherwise, paths marked with - * --no-assume-unchanged (i.e. things to be edited) will - * reacquire CE_VALID bit automatically, which is not - * really what we want. + /* + * If ignore_valid is not set, we should leave CE_VALID bit + * alone. Otherwise, paths marked with --no-assume-unchanged + * (i.e. things to be edited) will reacquire CE_VALID bit + * automatically, which is not really what we want. */ - if (!really && assume_unchanged && !(ce->ce_flags & htons(CE_VALID))) + if (!ignore_valid && assume_unchanged && + !(ce->ce_flags & htons(CE_VALID))) updated->ce_flags &= ~htons(CE_VALID); return updated; @@ -835,6 +848,7 @@ int refresh_index(struct index_state *istate, unsigned int flags, const char **p int allow_unmerged = (flags & REFRESH_UNMERGED) != 0; int quiet = (flags & REFRESH_QUIET) != 0; int not_new = (flags & REFRESH_IGNORE_MISSING) != 0; + unsigned int options = really ? CE_MATCH_IGNORE_VALID : 0; for (i = 0; i < istate->cache_nr; i++) { struct cache_entry *ce, *new; @@ -856,7 +870,7 @@ int refresh_index(struct index_state *istate, unsigned int flags, const char **p if (pathspec && !match_pathspec(pathspec, ce->name, strlen(ce->name), 0, seen)) continue; - new = refresh_cache_ent(istate, ce, really, &cache_errno); + new = refresh_cache_ent(istate, ce, options, &cache_errno); if (new == ce) continue; if (!new) { diff --git a/receive-pack.c b/receive-pack.c index 38e35c06b9..fba4cf8235 100644 --- a/receive-pack.c +++ b/receive-pack.c @@ -200,12 +200,14 @@ static const char *update(struct command *cmd) } if (is_null_sha1(new_sha1)) { + if (!parse_object(old_sha1)) { + warning ("Allowing deletion of corrupt ref."); + old_sha1 = NULL; + } if (delete_ref(name, old_sha1)) { error("failed to delete %s", name); return "failed to delete"; } - fprintf(stderr, "%s: %s -> deleted\n", name, - sha1_to_hex(old_sha1)); return NULL; /* good */ } else { @@ -217,8 +219,6 @@ static const char *update(struct command *cmd) if (write_ref_sha1(lock, new_sha1, "push")) { return "failed to write"; /* error() already called */ } - fprintf(stderr, "%s: %s -> %s\n", name, - sha1_to_hex(old_sha1), sha1_to_hex(new_sha1)); return NULL; /* good */ } } @@ -580,18 +580,6 @@ int for_each_remote_ref(each_ref_fn fn, void *cb_data) return do_for_each_ref("refs/remotes/", fn, 13, cb_data); } -/* NEEDSWORK: This is only used by ssh-upload and it should go; the - * caller should do resolve_ref or read_ref like everybody else. Or - * maybe everybody else should use get_ref_sha1() instead of doing - * read_ref(). - */ -int get_ref_sha1(const char *ref, unsigned char *sha1) -{ - if (check_ref_format(ref)) - return -1; - return read_ref(mkpath("refs/%s", ref), sha1); -} - /* * Make sure "ref" is something reasonable to have under ".git/refs/"; * We do not like it if: @@ -655,6 +643,37 @@ int check_ref_format(const char *ref) } } +const char *ref_rev_parse_rules[] = { + "%.*s", + "refs/%.*s", + "refs/tags/%.*s", + "refs/heads/%.*s", + "refs/remotes/%.*s", + "refs/remotes/%.*s/HEAD", + NULL +}; + +const char *ref_fetch_rules[] = { + "%.*s", + "refs/%.*s", + "refs/heads/%.*s", + NULL +}; + +int refname_match(const char *abbrev_name, const char *full_name, const char **rules) +{ + const char **p; + const int abbrev_name_len = strlen(abbrev_name); + + for (p = rules; *p; p++) { + if (!strcmp(full_name, mkpath(*p, abbrev_name_len, abbrev_name))) { + return 1; + } + } + + return 0; +} + static struct ref_lock *verify_lock(struct ref_lock *lock, const unsigned char *old_sha1, int mustexist) { @@ -1075,7 +1094,7 @@ static int log_ref_write(const char *ref_name, const unsigned char *old_sha1, adjust_shared_perm(log_file); msglen = msg ? strlen(msg) : 0; - committer = git_committer_info(-1); + committer = git_committer_info(0); maxlen = strlen(committer) + msglen + 100; logrec = xmalloc(maxlen); len = sprintf(logrec, "%s %s %s\n", @@ -1445,3 +1464,11 @@ int update_ref(const char *action, const char *refname, } return 0; } + +struct ref *find_ref_by_name(struct ref *list, const char *name) +{ + for ( ; list; list = list->next) + if (!strcmp(list->name, name)) + return list; + return NULL; +} @@ -26,9 +26,6 @@ extern int for_each_remote_ref(each_ref_fn, void *); extern int peel_ref(const char *, unsigned char *); -/** Reads the refs file specified into sha1 **/ -extern int get_ref_sha1(const char *ref, unsigned char *sha1); - /** Locks a "refs/" ref returning the lock on success and NULL on failure. **/ extern struct ref_lock *lock_ref_sha1(const char *ref, const unsigned char *old_sha1); @@ -278,6 +278,8 @@ static int handle_config(const char *key, const char *value) } else if (!strcmp(subkey, ".tagopt")) { if (!strcmp(value, "--no-tags")) remote->fetch_tags = -1; + } else if (!strcmp(subkey, ".proxy")) { + remote->http_proxy = xstrdup(value); } return 0; } @@ -417,25 +419,6 @@ int remote_has_url(struct remote *remote, const char *url) return 0; } -/* - * Returns true if, under the matching rules for fetching, name is the - * same as the given full name. - */ -static int ref_matches_abbrev(const char *name, const char *full) -{ - if (!prefixcmp(name, "refs/") || !strcmp(name, "HEAD")) - return !strcmp(name, full); - if (prefixcmp(full, "refs/")) - return 0; - if (!prefixcmp(name, "heads/") || - !prefixcmp(name, "tags/") || - !prefixcmp(name, "remotes/")) - return !strcmp(name, full + 5); - if (prefixcmp(full + 5, "heads/")) - return 0; - return !strcmp(full + 11, name); -} - int remote_find_tracking(struct remote *remote, struct refspec *refspec) { int find_src = refspec->src == NULL; @@ -485,7 +468,7 @@ struct ref *alloc_ref(unsigned namelen) return ret; } -static struct ref *copy_ref(struct ref *ref) +static struct ref *copy_ref(const struct ref *ref) { struct ref *ret = xmalloc(sizeof(struct ref) + strlen(ref->name) + 1); memcpy(ret, ref, sizeof(struct ref) + strlen(ref->name) + 1); @@ -493,6 +476,18 @@ static struct ref *copy_ref(struct ref *ref) return ret; } +struct ref *copy_ref_list(const struct ref *ref) +{ + struct ref *ret = NULL; + struct ref **tail = &ret; + while (ref) { + *tail = copy_ref(ref); + ref = ref->next; + tail = &((*tail)->next); + } + return ret; +} + void free_refs(struct ref *ref) { struct ref *next; @@ -519,10 +514,7 @@ static int count_refspec_match(const char *pattern, char *name = refs->name; int namelen = strlen(name); - if (namelen < patlen || - memcmp(name + namelen - patlen, pattern, patlen)) - continue; - if (namelen != patlen && name[namelen - patlen - 1] != '/') + if (!refname_match(pattern, name, ref_rev_parse_rules)) continue; /* A match is "weak" if it is with refs outside @@ -684,14 +676,6 @@ static int match_explicit_refs(struct ref *src, struct ref *dst, return -errs; } -static struct ref *find_ref_by_name(struct ref *list, const char *name) -{ - for ( ; list; list = list->next) - if (!strcmp(list->name, name)) - return list; - return NULL; -} - static const struct refspec *check_pattern_match(const struct refspec *rs, int rs_nr, const struct ref *src) @@ -710,10 +694,12 @@ static const struct refspec *check_pattern_match(const struct refspec *rs, * without thinking. */ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail, - int nr_refspec, char **refspec, int all) + int nr_refspec, const char **refspec, int flags) { struct refspec *rs = parse_ref_spec(nr_refspec, (const char **) refspec); + int send_all = flags & MATCH_REFS_ALL; + int send_mirror = flags & MATCH_REFS_MIRROR; if (match_explicit_refs(src, dst, dst_tail, rs, nr_refspec)) return -1; @@ -730,7 +716,7 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail, if (!pat) continue; } - else if (prefixcmp(src->name, "refs/heads/")) + else if (!send_mirror && prefixcmp(src->name, "refs/heads/")) /* * "matching refs"; traditionally we pushed everything * including refs outside refs/heads/ hierarchy, but @@ -751,10 +737,13 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail, if (dst_peer && dst_peer->peer_ref) /* We're already sending something to this ref. */ goto free_name; - if (!dst_peer && !nr_refspec && !all) - /* Remote doesn't have it, and we have no + + if (!dst_peer && !nr_refspec && !(send_all || send_mirror)) + /* + * Remote doesn't have it, and we have no * explicit pattern, and we don't have - * --all. */ + * --all nor --mirror. + */ goto free_name; if (!dst_peer) { /* Create a new one and link it */ @@ -807,13 +796,13 @@ int branch_merge_matches(struct branch *branch, { if (!branch || i < 0 || i >= branch->merge_nr) return 0; - return ref_matches_abbrev(branch->merge[i]->src, refname); + return refname_match(branch->merge[i]->src, refname, ref_fetch_rules); } -static struct ref *get_expanded_map(struct ref *remote_refs, +static struct ref *get_expanded_map(const struct ref *remote_refs, const struct refspec *refspec) { - struct ref *ref; + const struct ref *ref; struct ref *ret = NULL; struct ref **tail = &ret; @@ -824,7 +813,7 @@ static struct ref *get_expanded_map(struct ref *remote_refs, if (strchr(ref->name, '^')) continue; /* a dereference item */ if (!prefixcmp(ref->name, refspec->src)) { - char *match; + const char *match; struct ref *cpy = copy_ref(ref); match = ref->name + remote_prefix_len; @@ -842,19 +831,19 @@ static struct ref *get_expanded_map(struct ref *remote_refs, return ret; } -static struct ref *find_ref_by_name_abbrev(struct ref *refs, const char *name) +static const struct ref *find_ref_by_name_abbrev(const struct ref *refs, const char *name) { - struct ref *ref; + const struct ref *ref; for (ref = refs; ref; ref = ref->next) { - if (ref_matches_abbrev(name, ref->name)) + if (refname_match(name, ref->name, ref_fetch_rules)) return ref; } return NULL; } -struct ref *get_remote_ref(struct ref *remote_refs, const char *name) +struct ref *get_remote_ref(const struct ref *remote_refs, const char *name) { - struct ref *ref = find_ref_by_name_abbrev(remote_refs, name); + const struct ref *ref = find_ref_by_name_abbrev(remote_refs, name); if (!ref) return NULL; @@ -887,7 +876,7 @@ static struct ref *get_local_ref(const char *name) return ret; } -int get_fetch_map(struct ref *remote_refs, +int get_fetch_map(const struct ref *remote_refs, const struct refspec *refspec, struct ref ***tail, int missing_ok) @@ -25,6 +25,11 @@ struct remote { const char *receivepack; const char *uploadpack; + + /* + * for curl remotes only + */ + char *http_proxy; }; struct remote *remote_get(const char *name); @@ -44,6 +49,10 @@ struct refspec { struct ref *alloc_ref(unsigned namelen); +struct ref *copy_ref_list(const struct ref *ref); + +int check_ref_type(const struct ref *ref, int flags); + /* * Frees the entire list and peers of elements. */ @@ -57,7 +66,7 @@ void ref_remove_duplicates(struct ref *ref_map); struct refspec *parse_ref_spec(int nr_refspec, const char **refspec); int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail, - int nr_refspec, char **refspec, int all); + int nr_refspec, const char **refspec, int all); /* * Given a list of the remote refs and the specification of things to @@ -71,10 +80,10 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail, * missing_ok is usually false, but when we are adding branch.$name.merge * it is Ok if the branch is not at the remote anymore. */ -int get_fetch_map(struct ref *remote_refs, const struct refspec *refspec, +int get_fetch_map(const struct ref *remote_refs, const struct refspec *refspec, struct ref ***tail, int missing_ok); -struct ref *get_remote_ref(struct ref *remote_refs, const char *name); +struct ref *get_remote_ref(const struct ref *remote_refs, const char *name); /* * For the given remote, reads the refspec's src and sets the other fields. @@ -98,4 +107,11 @@ struct branch *branch_get(const char *name); int branch_has_merge_config(struct branch *branch); int branch_merge_matches(struct branch *, int n, const char *); +/* Flags to match_refs. */ +enum match_refs_flags { + MATCH_REFS_NONE = 0, + MATCH_REFS_ALL = (1 << 0), + MATCH_REFS_MIRROR = (1 << 1), +}; + #endif diff --git a/revision.c b/revision.c index e76da0d448..2a59035192 100644 --- a/revision.c +++ b/revision.c @@ -10,6 +10,8 @@ #include "reflog-walk.h" #include "patch-ids.h" +volatile show_early_output_fn_t show_early_output; + static char *path_name(struct name_path *path, const char *name) { struct name_path *p; @@ -65,10 +67,17 @@ void mark_tree_uninteresting(struct tree *tree) init_tree_desc(&desc, tree->buffer, tree->size); while (tree_entry(&desc, &entry)) { - if (S_ISDIR(entry.mode)) + switch (object_type(entry.mode)) { + case OBJ_TREE: mark_tree_uninteresting(lookup_tree(entry.sha1)); - else + break; + case OBJ_BLOB: mark_blob_uninteresting(lookup_blob(entry.sha1)); + break; + default: + /* Subproject commit - not in this repository */ + break; + } } /* @@ -250,7 +259,7 @@ static void file_add_remove(struct diff_options *options, } tree_difference = diff; if (tree_difference == REV_TREE_DIFFERENT) - options->has_changes = 1; + DIFF_OPT_SET(options, HAS_CHANGES); } static void file_change(struct diff_options *options, @@ -260,7 +269,7 @@ static void file_change(struct diff_options *options, const char *base, const char *path) { tree_difference = REV_TREE_DIFFERENT; - options->has_changes = 1; + DIFF_OPT_SET(options, HAS_CHANGES); } static int rev_compare_tree(struct rev_info *revs, struct tree *t1, struct tree *t2) @@ -270,7 +279,7 @@ static int rev_compare_tree(struct rev_info *revs, struct tree *t1, struct tree if (!t2) return REV_TREE_DIFFERENT; tree_difference = REV_TREE_SAME; - revs->pruning.has_changes = 0; + DIFF_OPT_CLR(&revs->pruning, HAS_CHANGES); if (diff_tree_sha1(t1->object.sha1, t2->object.sha1, "", &revs->pruning) < 0) return REV_TREE_DIFFERENT; @@ -294,7 +303,7 @@ static int rev_same_tree_as_empty(struct rev_info *revs, struct tree *t1) init_tree_desc(&empty, "", 0); tree_difference = REV_TREE_SAME; - revs->pruning.has_changes = 0; + DIFF_OPT_CLR(&revs->pruning, HAS_CHANGES); retval = diff_tree(&empty, &real, "", &revs->pruning); free(tree); @@ -306,15 +315,28 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit) struct commit_list **pp, *parent; int tree_changed = 0, tree_same = 0; + /* + * If we don't do pruning, everything is interesting + */ + if (!revs->prune) + return; + if (!commit->tree) return; if (!commit->parents) { - if (!rev_same_tree_as_empty(revs, commit->tree)) - commit->object.flags |= TREECHANGE; + if (rev_same_tree_as_empty(revs, commit->tree)) + commit->object.flags |= TREESAME; return; } + /* + * Normal non-merge commit? If we don't want to make the + * history dense, we consider it always to be a change.. + */ + if (!revs->dense && !commit->parents->next) + return; + pp = &commit->parents; while ((parent = *pp) != NULL) { struct commit *p = parent->item; @@ -338,6 +360,7 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit) } parent->next = NULL; commit->parents = parent; + commit->object.flags |= TREESAME; return; case REV_TREE_NEW: @@ -366,7 +389,8 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit) die("bad tree compare for commit %s", sha1_to_hex(commit->object.sha1)); } if (tree_changed && !tree_same) - commit->object.flags |= TREECHANGE; + return; + commit->object.flags |= TREESAME; } static int add_parents_to_list(struct rev_info *revs, struct commit *commit, struct commit_list **list) @@ -413,8 +437,7 @@ static int add_parents_to_list(struct rev_info *revs, struct commit *commit, str * simplify the commit history and find the parent * that has no differences in the path set if one exists. */ - if (revs->prune_fn) - revs->prune_fn(revs, commit); + try_to_simplify_commit(revs, commit); if (revs->no_walk) return 0; @@ -533,6 +556,7 @@ static int limit_list(struct rev_info *revs) struct commit_list *entry = list; struct commit *commit = list->item; struct object *obj = &commit->object; + show_early_output_fn_t show; list = list->next; free(entry); @@ -550,6 +574,13 @@ static int limit_list(struct rev_info *revs) if (revs->min_age != -1 && (commit->date > revs->min_age)) continue; p = &commit_list_insert(commit, p)->next; + + show = show_early_output; + if (!show) + continue; + + show(revs, newlist); + show_early_output = NULL; } if (revs->cherry_pick) cherry_pick_list(newlist, revs); @@ -662,8 +693,8 @@ void init_revisions(struct rev_info *revs, const char *prefix) revs->abbrev = DEFAULT_ABBREV; revs->ignore_merges = 1; revs->simplify_history = 1; - revs->pruning.recursive = 1; - revs->pruning.quiet = 1; + DIFF_OPT_SET(&revs->pruning, RECURSIVE); + DIFF_OPT_SET(&revs->pruning, QUIET); revs->pruning.add_remove = file_add_remove; revs->pruning.change = file_change; revs->lifo = 1; @@ -674,12 +705,6 @@ void init_revisions(struct rev_info *revs, const char *prefix) revs->skip_count = -1; revs->max_count = -1; - revs->prune_fn = NULL; - revs->prune_data = NULL; - - revs->topo_setter = topo_sort_default_setter; - revs->topo_getter = topo_sort_default_getter; - revs->commit_format = CMIT_FMT_DEFAULT; diff_setup(&revs->diffopt); @@ -994,6 +1019,18 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch revs->topo_order = 1; continue; } + if (!prefixcmp(arg, "--early-output")) { + int count = 100; + switch (arg[14]) { + case '=': + count = atoi(arg+15); + /* Fallthrough */ + case 0: + revs->topo_order = 1; + revs->early_output = count; + continue; + } + } if (!strcmp(arg, "--parents")) { revs->parents = 1; continue; @@ -1054,13 +1091,13 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch } if (!strcmp(arg, "-r")) { revs->diff = 1; - revs->diffopt.recursive = 1; + DIFF_OPT_SET(&revs->diffopt, RECURSIVE); continue; } if (!strcmp(arg, "-t")) { revs->diff = 1; - revs->diffopt.recursive = 1; - revs->diffopt.tree_in_recursive = 1; + DIFF_OPT_SET(&revs->diffopt, RECURSIVE); + DIFF_OPT_SET(&revs->diffopt, TREE_IN_RECURSIVE); continue; } if (!strcmp(arg, "-m")) { @@ -1242,7 +1279,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch revs->diff = 1; /* Pickaxe and rename following needs diffs */ - if (revs->diffopt.pickaxe || revs->diffopt.follow_renames) + if (revs->diffopt.pickaxe || DIFF_OPT_TST(&revs->diffopt, FOLLOW_RENAMES)) revs->diff = 1; if (revs->topo_order) @@ -1251,8 +1288,8 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch if (revs->prune_data) { diff_tree_setup_paths(revs->prune_data, &revs->pruning); /* Can't prune commits with rename following: the paths change.. */ - if (!revs->diffopt.follow_renames) - revs->prune_fn = try_to_simplify_commit; + if (!DIFF_OPT_TST(&revs->diffopt, FOLLOW_RENAMES)) + revs->prune = 1; if (!revs->full_diff) diff_tree_setup_paths(revs->prune_data, &revs->diffopt); } @@ -1303,9 +1340,7 @@ int prepare_revision_walk(struct rev_info *revs) if (limit_list(revs) < 0) return -1; if (revs->topo_order) - sort_in_topological_order_fn(&revs->commits, revs->lifo, - revs->topo_setter, - revs->topo_getter); + sort_in_topological_order(&revs->commits, revs->lifo); return 0; } @@ -1324,7 +1359,9 @@ static enum rewrite_result rewrite_one(struct rev_info *revs, struct commit **pp return rewrite_one_error; if (p->parents && p->parents->next) return rewrite_one_ok; - if (p->object.flags & (TREECHANGE | UNINTERESTING)) + if (p->object.flags & UNINTERESTING) + return rewrite_one_ok; + if (!(p->object.flags & TREESAME)) return rewrite_one_ok; if (!p->parents) return rewrite_one_noparents; @@ -1381,6 +1418,36 @@ static int commit_match(struct commit *commit, struct rev_info *opt) commit->buffer, strlen(commit->buffer)); } +enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit) +{ + if (commit->object.flags & SHOWN) + return commit_ignore; + if (revs->unpacked && has_sha1_pack(commit->object.sha1, revs->ignore_packed)) + return commit_ignore; + if (commit->object.flags & UNINTERESTING) + return commit_ignore; + if (revs->min_age != -1 && (commit->date > revs->min_age)) + return commit_ignore; + if (revs->no_merges && commit->parents && commit->parents->next) + return commit_ignore; + if (!commit_match(commit, revs)) + return commit_ignore; + if (revs->prune && revs->dense) { + /* Commit without changes? */ + if (commit->object.flags & TREESAME) { + /* drop merges unless we want parenthood */ + if (!revs->parents) + return commit_ignore; + /* non-merge - always ignore it */ + if (!commit->parents || !commit->parents->next) + return commit_ignore; + } + if (revs->parents && rewrite_parents(revs, commit) < 0) + return commit_error; + } + return commit_show; +} + static struct commit *get_revision_1(struct rev_info *revs) { if (!revs->commits) @@ -1408,36 +1475,15 @@ static struct commit *get_revision_1(struct rev_info *revs) if (add_parents_to_list(revs, commit, &revs->commits) < 0) return NULL; } - if (commit->object.flags & SHOWN) - continue; - if (revs->unpacked && has_sha1_pack(commit->object.sha1, - revs->ignore_packed)) - continue; - - if (commit->object.flags & UNINTERESTING) + switch (simplify_commit(revs, commit)) { + case commit_ignore: continue; - if (revs->min_age != -1 && (commit->date > revs->min_age)) - continue; - if (revs->no_merges && - commit->parents && commit->parents->next) - continue; - if (!commit_match(commit, revs)) - continue; - if (revs->prune_fn && revs->dense) { - /* Commit without changes? */ - if (!(commit->object.flags & TREECHANGE)) { - /* drop merges unless we want parenthood */ - if (!revs->parents) - continue; - /* non-merge - always ignore it */ - if (!commit->parents || !commit->parents->next) - continue; - } - if (revs->parents && rewrite_parents(revs, commit) < 0) - return NULL; + case commit_error: + return NULL; + default: + return commit; } - return commit; } while (revs->commits); return NULL; } diff --git a/revision.h b/revision.h index 98a0a8f3fa..992e1e9dd5 100644 --- a/revision.h +++ b/revision.h @@ -3,19 +3,18 @@ #define SEEN (1u<<0) #define UNINTERESTING (1u<<1) -#define TREECHANGE (1u<<2) +#define TREESAME (1u<<2) #define SHOWN (1u<<3) #define TMP_MARK (1u<<4) /* for isolated cases; clean after use */ #define BOUNDARY (1u<<5) #define CHILD_SHOWN (1u<<6) #define ADDED (1u<<7) /* Parents already parsed and added? */ #define SYMMETRIC_LEFT (1u<<8) +#define TOPOSORT (1u<<9) /* In the active toposort list.. */ struct rev_info; struct log_info; -typedef void (prune_fn_t)(struct rev_info *revs, struct commit *commit); - struct rev_info { /* Starting list */ struct commit_list *commits; @@ -27,10 +26,11 @@ struct rev_info { /* Basic information */ const char *prefix; void *prune_data; - prune_fn_t *prune_fn; + unsigned int early_output; /* Traversal flags */ unsigned int dense:1, + prune:1, no_merges:1, no_walk:1, remove_empty_trees:1, @@ -96,9 +96,6 @@ struct rev_info { struct diff_options diffopt; struct diff_options pruning; - topo_sort_set_fn_t topo_setter; - topo_sort_get_fn_t topo_getter; - struct reflog_walk_info *reflog_info; }; @@ -107,6 +104,8 @@ struct rev_info { #define REV_TREE_DIFFERENT 2 /* revision.c */ +typedef void (*show_early_output_fn_t)(struct rev_info *, struct commit_list *); +volatile show_early_output_fn_t show_early_output; extern void init_revisions(struct rev_info *revs, const char *prefix); extern int setup_revisions(int argc, const char **argv, struct rev_info *revs, const char *def); @@ -131,4 +130,12 @@ extern void add_object(struct object *obj, extern void add_pending_object(struct rev_info *revs, struct object *obj, const char *name); +enum commit_action { + commit_ignore, + commit_show, + commit_error +}; + +extern enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit); + #endif diff --git a/run-command.c b/run-command.c index d99a6c4ea7..476d00c218 100644 --- a/run-command.c +++ b/run-command.c @@ -41,7 +41,7 @@ int start_command(struct child_process *cmd) cmd->close_out = 1; } - need_err = cmd->err < 0; + need_err = !cmd->no_stderr && cmd->err < 0; if (need_err) { if (pipe(fderr) < 0) { if (need_in) @@ -87,7 +87,9 @@ int start_command(struct child_process *cmd) close(cmd->out); } - if (need_err) { + if (cmd->no_stderr) + dup_devnull(2); + else if (need_err) { dup2(fderr[1], 2); close_pair(fderr); } diff --git a/run-command.h b/run-command.h index 94e1e9d516..1fc781d766 100644 --- a/run-command.h +++ b/run-command.h @@ -23,6 +23,7 @@ struct child_process { unsigned close_out:1; unsigned no_stdin:1; unsigned no_stdout:1; + unsigned no_stderr:1; unsigned git_cmd:1; /* if this is to be git sub-command */ unsigned stdout_to_stderr:1; }; diff --git a/send-pack.c b/send-pack.c deleted file mode 100644 index 5e127a1b7b..0000000000 --- a/send-pack.c +++ /dev/null @@ -1,461 +0,0 @@ -#include "cache.h" -#include "commit.h" -#include "tag.h" -#include "refs.h" -#include "pkt-line.h" -#include "run-command.h" -#include "remote.h" - -static const char send_pack_usage[] = -"git-send-pack [--all] [--dry-run] [--force] [--receive-pack=<git-receive-pack>] [--verbose] [--thin] [<host>:]<directory> [<ref>...]\n" -" --all and explicit <ref> specification are mutually exclusive."; -static const char *receivepack = "git-receive-pack"; -static int verbose; -static int send_all; -static int force_update; -static int use_thin_pack; -static int dry_run; - -/* - * Make a pack stream and spit it out into file descriptor fd - */ -static int pack_objects(int fd, struct ref *refs) -{ - /* - * The child becomes pack-objects --revs; we feed - * the revision parameters to it via its stdin and - * let its stdout go back to the other end. - */ - const char *args[] = { - "pack-objects", - "--all-progress", - "--revs", - "--stdout", - NULL, - NULL, - }; - struct child_process po; - - if (use_thin_pack) - args[4] = "--thin"; - memset(&po, 0, sizeof(po)); - po.argv = args; - po.in = -1; - po.out = fd; - po.git_cmd = 1; - if (start_command(&po)) - die("git-pack-objects failed (%s)", strerror(errno)); - - /* - * We feed the pack-objects we just spawned with revision - * parameters by writing to the pipe. - */ - while (refs) { - char buf[42]; - - if (!is_null_sha1(refs->old_sha1) && - has_sha1_file(refs->old_sha1)) { - memcpy(buf + 1, sha1_to_hex(refs->old_sha1), 40); - buf[0] = '^'; - buf[41] = '\n'; - if (!write_or_whine(po.in, buf, 42, - "send-pack: send refs")) - break; - } - if (!is_null_sha1(refs->new_sha1)) { - memcpy(buf, sha1_to_hex(refs->new_sha1), 40); - buf[40] = '\n'; - if (!write_or_whine(po.in, buf, 41, - "send-pack: send refs")) - break; - } - refs = refs->next; - } - - if (finish_command(&po)) - return error("pack-objects died with strange error"); - return 0; -} - -static void unmark_and_free(struct commit_list *list, unsigned int mark) -{ - while (list) { - struct commit_list *temp = list; - temp->item->object.flags &= ~mark; - list = temp->next; - free(temp); - } -} - -static int ref_newer(const unsigned char *new_sha1, - const unsigned char *old_sha1) -{ - struct object *o; - struct commit *old, *new; - struct commit_list *list, *used; - int found = 0; - - /* Both new and old must be commit-ish and new is descendant of - * old. Otherwise we require --force. - */ - o = deref_tag(parse_object(old_sha1), NULL, 0); - if (!o || o->type != OBJ_COMMIT) - return 0; - old = (struct commit *) o; - - o = deref_tag(parse_object(new_sha1), NULL, 0); - if (!o || o->type != OBJ_COMMIT) - return 0; - new = (struct commit *) o; - - if (parse_commit(new) < 0) - return 0; - - used = list = NULL; - commit_list_insert(new, &list); - while (list) { - new = pop_most_recent_commit(&list, 1); - commit_list_insert(new, &used); - if (new == old) { - found = 1; - break; - } - } - unmark_and_free(list, 1); - unmark_and_free(used, 1); - return found; -} - -static struct ref *local_refs, **local_tail; -static struct ref *remote_refs, **remote_tail; - -static int one_local_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data) -{ - struct ref *ref; - int len = strlen(refname) + 1; - ref = xcalloc(1, sizeof(*ref) + len); - hashcpy(ref->new_sha1, sha1); - memcpy(ref->name, refname, len); - *local_tail = ref; - local_tail = &ref->next; - return 0; -} - -static void get_local_heads(void) -{ - local_tail = &local_refs; - for_each_ref(one_local_ref, NULL); -} - -static int receive_status(int in) -{ - char line[1000]; - int ret = 0; - int len = packet_read_line(in, line, sizeof(line)); - if (len < 10 || memcmp(line, "unpack ", 7)) { - fprintf(stderr, "did not receive status back\n"); - return -1; - } - if (memcmp(line, "unpack ok\n", 10)) { - fputs(line, stderr); - ret = -1; - } - while (1) { - len = packet_read_line(in, line, sizeof(line)); - if (!len) - break; - if (len < 3 || - (memcmp(line, "ok", 2) && memcmp(line, "ng", 2))) { - fprintf(stderr, "protocol error: %s\n", line); - ret = -1; - break; - } - if (!memcmp(line, "ok", 2)) - continue; - fputs(line, stderr); - ret = -1; - } - return ret; -} - -static void update_tracking_ref(struct remote *remote, struct ref *ref) -{ - struct refspec rs; - int will_delete_ref; - - rs.src = ref->name; - rs.dst = NULL; - - if (!ref->peer_ref) - return; - - will_delete_ref = is_null_sha1(ref->peer_ref->new_sha1); - - if (!will_delete_ref && - !hashcmp(ref->old_sha1, ref->peer_ref->new_sha1)) - return; - - if (!remote_find_tracking(remote, &rs)) { - fprintf(stderr, "updating local tracking ref '%s'\n", rs.dst); - if (is_null_sha1(ref->peer_ref->new_sha1)) { - if (delete_ref(rs.dst, NULL)) - error("Failed to delete"); - } else - update_ref("update by push", rs.dst, - ref->new_sha1, NULL, 0, 0); - free(rs.dst); - } -} - -static int send_pack(int in, int out, struct remote *remote, int nr_refspec, char **refspec) -{ - struct ref *ref; - int new_refs; - int ret = 0; - int ask_for_status_report = 0; - int allow_deleting_refs = 0; - int expect_status_report = 0; - - /* No funny business with the matcher */ - remote_tail = get_remote_heads(in, &remote_refs, 0, NULL, REF_NORMAL); - get_local_heads(); - - /* Does the other end support the reporting? */ - if (server_supports("report-status")) - ask_for_status_report = 1; - if (server_supports("delete-refs")) - allow_deleting_refs = 1; - - /* match them up */ - if (!remote_tail) - remote_tail = &remote_refs; - if (match_refs(local_refs, remote_refs, &remote_tail, - nr_refspec, refspec, send_all)) - return -1; - - if (!remote_refs) { - fprintf(stderr, "No refs in common and none specified; doing nothing.\n" - "Perhaps you should specify a branch such as 'master'.\n"); - return 0; - } - - /* - * Finally, tell the other end! - */ - new_refs = 0; - for (ref = remote_refs; ref; ref = ref->next) { - char old_hex[60], *new_hex; - int will_delete_ref; - - if (!ref->peer_ref) - continue; - - - will_delete_ref = is_null_sha1(ref->peer_ref->new_sha1); - if (will_delete_ref && !allow_deleting_refs) { - error("remote does not support deleting refs"); - ret = -2; - continue; - } - if (!will_delete_ref && - !hashcmp(ref->old_sha1, ref->peer_ref->new_sha1)) { - if (verbose) - fprintf(stderr, "'%s': up-to-date\n", ref->name); - continue; - } - - /* This part determines what can overwrite what. - * The rules are: - * - * (0) you can always use --force or +A:B notation to - * selectively force individual ref pairs. - * - * (1) if the old thing does not exist, it is OK. - * - * (2) if you do not have the old thing, you are not allowed - * to overwrite it; you would not know what you are losing - * otherwise. - * - * (3) if both new and old are commit-ish, and new is a - * descendant of old, it is OK. - * - * (4) regardless of all of the above, removing :B is - * always allowed. - */ - - if (!force_update && - !will_delete_ref && - !is_null_sha1(ref->old_sha1) && - !ref->force) { - if (!has_sha1_file(ref->old_sha1) || - !ref_newer(ref->peer_ref->new_sha1, - ref->old_sha1)) { - /* We do not have the remote ref, or - * we know that the remote ref is not - * an ancestor of what we are trying to - * push. Either way this can be losing - * commits at the remote end and likely - * we were not up to date to begin with. - */ - error("remote '%s' is not a strict " - "subset of local ref '%s'. " - "maybe you are not up-to-date and " - "need to pull first?", - ref->name, - ref->peer_ref->name); - ret = -2; - continue; - } - } - hashcpy(ref->new_sha1, ref->peer_ref->new_sha1); - if (!will_delete_ref) - new_refs++; - strcpy(old_hex, sha1_to_hex(ref->old_sha1)); - new_hex = sha1_to_hex(ref->new_sha1); - - if (!dry_run) { - if (ask_for_status_report) { - packet_write(out, "%s %s %s%c%s", - old_hex, new_hex, ref->name, 0, - "report-status"); - ask_for_status_report = 0; - expect_status_report = 1; - } - else - packet_write(out, "%s %s %s", - old_hex, new_hex, ref->name); - } - if (will_delete_ref) - fprintf(stderr, "deleting '%s'\n", ref->name); - else { - fprintf(stderr, "updating '%s'", ref->name); - if (strcmp(ref->name, ref->peer_ref->name)) - fprintf(stderr, " using '%s'", - ref->peer_ref->name); - fprintf(stderr, "\n from %s\n to %s\n", - old_hex, new_hex); - } - } - - packet_flush(out); - if (new_refs && !dry_run) - ret = pack_objects(out, remote_refs); - close(out); - - if (expect_status_report) { - if (receive_status(in)) - ret = -4; - } - - if (!dry_run && remote && ret == 0) { - for (ref = remote_refs; ref; ref = ref->next) - update_tracking_ref(remote, ref); - } - - if (!new_refs && ret == 0) - fprintf(stderr, "Everything up-to-date\n"); - return ret; -} - -static void verify_remote_names(int nr_heads, char **heads) -{ - int i; - - for (i = 0; i < nr_heads; i++) { - const char *remote = strchr(heads[i], ':'); - - remote = remote ? (remote + 1) : heads[i]; - switch (check_ref_format(remote)) { - case 0: /* ok */ - case -2: /* ok but a single level -- that is fine for - * a match pattern. - */ - case -3: /* ok but ends with a pattern-match character */ - continue; - } - die("remote part of refspec is not a valid name in %s", - heads[i]); - } -} - -int main(int argc, char **argv) -{ - int i, nr_heads = 0; - char *dest = NULL; - char **heads = NULL; - int fd[2], ret; - struct child_process *conn; - char *remote_name = NULL; - struct remote *remote = NULL; - - setup_git_directory(); - git_config(git_default_config); - - argv++; - for (i = 1; i < argc; i++, argv++) { - char *arg = *argv; - - if (*arg == '-') { - if (!prefixcmp(arg, "--receive-pack=")) { - receivepack = arg + 15; - continue; - } - if (!prefixcmp(arg, "--exec=")) { - receivepack = arg + 7; - continue; - } - if (!prefixcmp(arg, "--remote=")) { - remote_name = arg + 9; - continue; - } - if (!strcmp(arg, "--all")) { - send_all = 1; - continue; - } - if (!strcmp(arg, "--dry-run")) { - dry_run = 1; - continue; - } - if (!strcmp(arg, "--force")) { - force_update = 1; - continue; - } - if (!strcmp(arg, "--verbose")) { - verbose = 1; - continue; - } - if (!strcmp(arg, "--thin")) { - use_thin_pack = 1; - continue; - } - usage(send_pack_usage); - } - if (!dest) { - dest = arg; - continue; - } - heads = argv; - nr_heads = argc - i; - break; - } - if (!dest) - usage(send_pack_usage); - if (heads && send_all) - usage(send_pack_usage); - verify_remote_names(nr_heads, heads); - - if (remote_name) { - remote = remote_get(remote_name); - if (!remote_has_url(remote, dest)) { - die("Destination %s is not a uri for %s", - dest, remote_name); - } - } - - conn = git_connect(fd, dest, receivepack, verbose ? CONNECT_VERBOSE : 0); - ret = send_pack(fd[0], fd[1], remote, nr_heads, heads); - close(fd[0]); - close(fd[1]); - ret |= finish_connect(conn); - return !!ret; -} diff --git a/send-pack.h b/send-pack.h new file mode 100644 index 0000000000..8ff1dc3539 --- /dev/null +++ b/send-pack.h @@ -0,0 +1,18 @@ +#ifndef SEND_PACK_H +#define SEND_PACK_H + +struct send_pack_args { + const char *receivepack; + unsigned verbose:1, + send_all:1, + send_mirror:1, + force_update:1, + use_thin_pack:1, + dry_run:1; +}; + +int send_pack(struct send_pack_args *args, + const char *dest, struct remote *remote, + int nr_heads, const char **heads); + +#endif diff --git a/server-info.c b/server-info.c index 0d1312ca56..a051e49a9e 100644 --- a/server-info.c +++ b/server-info.c @@ -35,7 +35,7 @@ static int update_info_refs(int force) safe_create_leading_directories(path0); info_ref_fp = fopen(path1, "w"); if (!info_ref_fp) - return error("unable to update %s", path0); + return error("unable to update %s", path1); for_each_ref(add_info_ref, NULL); fclose(info_ref_fp); adjust_shared_perm(path1); @@ -59,7 +59,7 @@ const char *prefix_path(const char *prefix, int len, const char *path) const char *prefix_filename(const char *pfx, int pfx_len, const char *arg) { static char path[PATH_MAX]; - if (!pfx || !*pfx || arg[0] == '/') + if (!pfx || !*pfx || is_absolute_path(arg)) return arg; memcpy(path, pfx, pfx_len); strcpy(path + pfx_len, arg); @@ -206,6 +206,38 @@ static const char *set_work_tree(const char *dir) return NULL; } +void setup_work_tree(void) +{ + const char *work_tree, *git_dir; + static int initialized = 0; + + if (initialized) + return; + work_tree = get_git_work_tree(); + git_dir = get_git_dir(); + if (!is_absolute_path(git_dir)) + set_git_dir(make_absolute_path(git_dir)); + if (!work_tree || chdir(work_tree)) + die("This operation must be run in a work tree"); + initialized = 1; +} + +static int check_repository_format_gently(int *nongit_ok) +{ + git_config(check_repository_format_version); + if (GIT_REPO_VERSION < repository_format_version) { + if (!nongit_ok) + die ("Expected git repo version <= %d, found %d", + GIT_REPO_VERSION, repository_format_version); + warning("Expected git repo version <= %d, found %d", + GIT_REPO_VERSION, repository_format_version); + warning("Please upgrade Git"); + *nongit_ok = -1; + return -1; + } + return 0; +} + /* * We cannot decide in this function whether we are in the work tree or * not, since the config can only be read _after_ this function was called. @@ -230,8 +262,15 @@ const char *setup_git_directory_gently(int *nongit_ok) static char buffer[1024 + 1]; const char *retval; - if (!work_tree_env) - return set_work_tree(gitdirenv); + if (!work_tree_env) { + retval = set_work_tree(gitdirenv); + /* config may override worktree */ + if (check_repository_format_gently(nongit_ok)) + return NULL; + return retval; + } + if (check_repository_format_gently(nongit_ok)) + return NULL; retval = get_relative_cwd(buffer, sizeof(buffer) - 1, get_git_work_tree()); if (!retval || !*retval) @@ -270,6 +309,7 @@ const char *setup_git_directory_gently(int *nongit_ok) if (!work_tree_env) inside_work_tree = 0; setenv(GIT_DIR_ENVIRONMENT, ".", 1); + check_repository_format_gently(nongit_ok); return NULL; } chdir(".."); @@ -290,6 +330,8 @@ const char *setup_git_directory_gently(int *nongit_ok) if (!work_tree_env) inside_work_tree = 1; git_work_tree_cfg = xstrndup(cwd, offset); + if (check_repository_format_gently(nongit_ok)) + return NULL; if (offset == len) return NULL; @@ -340,11 +382,7 @@ int check_repository_format_version(const char *var, const char *value) int check_repository_format(void) { - git_config(check_repository_format_version); - if (GIT_REPO_VERSION < repository_format_version) - die ("Expected git repo version <= %d, found %d", - GIT_REPO_VERSION, repository_format_version); - return 0; + return check_repository_format_gently(NULL); } const char *setup_git_directory(void) diff --git a/sha1_file.c b/sha1_file.c index f007874cbb..b0c24356ae 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -25,8 +25,10 @@ #ifdef NO_C99_FORMAT #define SZ_FMT "lu" +static unsigned long sz_fmt(size_t s) { return (unsigned long)s; } #else #define SZ_FMT "zu" +static size_t sz_fmt(size_t s) { return s; } #endif const unsigned char null_sha1[20]; @@ -86,7 +88,7 @@ int safe_create_leading_directories(char *path) char *pos = path; struct stat st; - if (*pos == '/') + if (is_absolute_path(path)) pos++; while (pos) { @@ -253,7 +255,7 @@ static int link_alt_odb_entry(const char * entry, int len, const char * relative int entlen = pfxlen + 43; int base_len = -1; - if (*entry != '/' && relative_base) { + if (!is_absolute_path(entry) && relative_base) { /* Relative alt-odb */ if (base_len < 0) base_len = strlen(relative_base) + 1; @@ -262,7 +264,7 @@ static int link_alt_odb_entry(const char * entry, int len, const char * relative } ent = xmalloc(sizeof(*ent) + entlen); - if (*entry != '/' && relative_base) { + if (!is_absolute_path(entry) && relative_base) { memcpy(ent->base, relative_base, base_len - 1); ent->base[base_len - 1] = '/'; memcpy(ent->base + base_len, entry, len); @@ -333,7 +335,7 @@ static void link_alt_odb_entries(const char *alt, const char *ep, int sep, while (cp < ep && *cp != sep) cp++; if (last != cp) { - if ((*last != '/') && depth) { + if (!is_absolute_path(last) && depth) { error("%s: ignoring relative alternate object store %s", relative_base, last); } else { @@ -423,9 +425,9 @@ void pack_report(void) "pack_report: getpagesize() = %10" SZ_FMT "\n" "pack_report: core.packedGitWindowSize = %10" SZ_FMT "\n" "pack_report: core.packedGitLimit = %10" SZ_FMT "\n", - (size_t) getpagesize(), - packed_git_window_size, - packed_git_limit); + sz_fmt(getpagesize()), + sz_fmt(packed_git_window_size), + sz_fmt(packed_git_limit)); fprintf(stderr, "pack_report: pack_used_ctr = %10u\n" "pack_report: pack_mmap_calls = %10u\n" @@ -435,7 +437,7 @@ void pack_report(void) pack_used_ctr, pack_mmap_calls, pack_open_windows, peak_pack_open_windows, - pack_mapped, peak_pack_mapped); + sz_fmt(pack_mapped), sz_fmt(peak_pack_mapped)); } static int check_packed_git_idx(const char *path, struct packed_git *p) diff --git a/sha1_name.c b/sha1_name.c index 2d727d54dc..13e11645e1 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -239,23 +239,13 @@ static int ambiguous_path(const char *path, int len) return slash; } -static const char *ref_fmt[] = { - "%.*s", - "refs/%.*s", - "refs/tags/%.*s", - "refs/heads/%.*s", - "refs/remotes/%.*s", - "refs/remotes/%.*s/HEAD", - NULL -}; - int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref) { const char **p, *r; int refs_found = 0; *ref = NULL; - for (p = ref_fmt; *p; p++) { + for (p = ref_rev_parse_rules; *p; p++) { unsigned char sha1_from_ref[20]; unsigned char *this_result; @@ -277,7 +267,7 @@ int dwim_log(const char *str, int len, unsigned char *sha1, char **log) int logs_found = 0; *log = NULL; - for (p = ref_fmt; *p; p++) { + for (p = ref_rev_parse_rules; *p; p++) { struct stat st; unsigned char hash[20]; char path[PATH_MAX]; @@ -610,24 +600,35 @@ static int get_sha1_oneline(const char *prefix, unsigned char *sha1) { struct commit_list *list = NULL, *backup = NULL, *l; int retval = -1; + char *temp_commit_buffer = NULL; if (prefix[0] == '!') { if (prefix[1] != '!') die ("Invalid search pattern: %s", prefix); prefix++; } - if (!save_commit_buffer) - return error("Could not expand oneline-name."); for_each_ref(handle_one_ref, &list); for (l = list; l; l = l->next) commit_list_insert(l->item, &backup); while (list) { char *p; struct commit *commit; + enum object_type type; + unsigned long size; commit = pop_most_recent_commit(&list, ONELINE_SEEN); parse_object(commit->object.sha1); - if (!commit->buffer || !(p = strstr(commit->buffer, "\n\n"))) + if (temp_commit_buffer) + free(temp_commit_buffer); + if (commit->buffer) + p = commit->buffer; + else { + p = read_sha1_file(commit->object.sha1, &type, &size); + if (!p) + continue; + temp_commit_buffer = p; + } + if (!(p = strstr(p, "\n\n"))) continue; if (!prefixcmp(p + 2, prefix)) { hashcpy(sha1, commit->object.sha1); @@ -635,6 +636,8 @@ static int get_sha1_oneline(const char *prefix, unsigned char *sha1) break; } } + if (temp_commit_buffer) + free(temp_commit_buffer); free_commit_list(list); for (l = backup; l; l = l->next) clear_commit_marks(l->item, ONELINE_SEEN); diff --git a/sideband.c b/sideband.c index ab8a1e990d..756bbc28d7 100644 --- a/sideband.c +++ b/sideband.c @@ -11,13 +11,19 @@ * stream, aka "verbose"). A message over band #3 is a signal that * the remote died unexpectedly. A flush() concludes the stream. */ + +#define PREFIX "remote:" +#define SUFFIX "\033[K" /* change to " " if ANSI sequences don't work */ + int recv_sideband(const char *me, int in_stream, int out, int err) { - char buf[7 + LARGE_PACKET_MAX + 1]; - strcpy(buf, "remote:"); + unsigned pf = strlen(PREFIX); + unsigned sf = strlen(SUFFIX); + char buf[pf + LARGE_PACKET_MAX + sf + 1]; + memcpy(buf, PREFIX, pf); while (1) { int band, len; - len = packet_read_line(in_stream, buf+7, LARGE_PACKET_MAX); + len = packet_read_line(in_stream, buf + pf, LARGE_PACKET_MAX); if (len == 0) break; if (len < 1) { @@ -25,35 +31,52 @@ int recv_sideband(const char *me, int in_stream, int out, int err) safe_write(err, buf, len); return SIDEBAND_PROTOCOL_ERROR; } - band = buf[7] & 0xff; + band = buf[pf] & 0xff; len--; switch (band) { case 3: - buf[7] = ' '; - buf[8+len] = '\n'; - safe_write(err, buf, 8+len+1); + buf[pf] = ' '; + buf[pf+1+len] = '\n'; + safe_write(err, buf, pf+1+len+1); return SIDEBAND_REMOTE_ERROR; case 2: - buf[7] = ' '; - len += 8; + buf[pf] = ' '; + len += pf+1; while (1) { - int brk = 8; + int brk = pf+1; + + /* Break the buffer into separate lines. */ while (brk < len) { brk++; if (buf[brk-1] == '\n' || buf[brk-1] == '\r') break; } - safe_write(err, buf, brk); + + /* + * Let's insert a suffix to clear the end + * of the screen line, but only if current + * line data actually contains something. + */ + if (brk > pf+1 + 1) { + char save[sf]; + memcpy(save, buf + brk, sf); + buf[brk + sf - 1] = buf[brk - 1]; + memcpy(buf + brk - 1, SUFFIX, sf); + safe_write(err, buf, brk + sf); + memcpy(buf + brk, save, sf); + } else + safe_write(err, buf, brk); + if (brk < len) { - memmove(buf + 8, buf + brk, len - brk); - len = len - brk + 8; + memmove(buf + pf+1, buf + brk, len - brk); + len = len - brk + pf+1; } else break; } continue; case 1: - safe_write(out, buf+8, len); + safe_write(out, buf + pf+1, len); continue; default: len = sprintf(buf, @@ -106,17 +106,25 @@ void strbuf_add(struct strbuf *sb, const void *data, size_t len) strbuf_setlen(sb, sb->len + len); } +void strbuf_adddup(struct strbuf *sb, size_t pos, size_t len) +{ + strbuf_grow(sb, len); + memcpy(sb->buf + sb->len, sb->buf + pos, len); + strbuf_setlen(sb, sb->len + len); +} + void strbuf_addf(struct strbuf *sb, const char *fmt, ...) { int len; va_list ap; + if (!strbuf_avail(sb)) + strbuf_grow(sb, 64); va_start(ap, fmt); len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap); va_end(ap); - if (len < 0) { - len = 0; - } + if (len < 0) + die("your vsnprintf is broken"); if (len > strbuf_avail(sb)) { strbuf_grow(sb, len); va_start(ap, fmt); @@ -129,6 +137,30 @@ void strbuf_addf(struct strbuf *sb, const char *fmt, ...) strbuf_setlen(sb, sb->len + len); } +void strbuf_expand(struct strbuf *sb, const char *format, + const char **placeholders, expand_fn_t fn, void *context) +{ + for (;;) { + const char *percent, **p; + + percent = strchrnul(format, '%'); + strbuf_add(sb, format, percent - format); + if (!*percent) + break; + format = percent + 1; + + for (p = placeholders; *p; p++) { + if (!prefixcmp(format, *p)) + break; + } + if (*p) { + fn(sb, *p, context); + format += strlen(*p); + } else + strbuf_addch(sb, '%'); + } +} + size_t strbuf_fread(struct strbuf *sb, size_t size, FILE *f) { size_t res; @@ -23,12 +23,12 @@ * that way: * * strbuf_grow(sb, SOME_SIZE); - * // ... here the memory areay starting at sb->buf, and of length - * // sb_avail(sb) is all yours, and you are sure that sb_avail(sb) is at - * // least SOME_SIZE + * ... Here, the memory array starting at sb->buf, and of length + * ... strbuf_avail(sb) is all yours, and you are sure that + * ... strbuf_avail(sb) is at least SOME_SIZE. * strbuf_setlen(sb, sb->len + SOME_OTHER_SIZE); * - * Of course, SOME_OTHER_SIZE must be smaller or equal to sb_avail(sb). + * Of course, SOME_OTHER_SIZE must be smaller or equal to strbuf_avail(sb). * * Doing so is safe, though if it has to be done in many places, adding the * missing API to the strbuf module is the way to go. @@ -101,6 +101,10 @@ static inline void strbuf_addstr(struct strbuf *sb, const char *s) { static inline void strbuf_addbuf(struct strbuf *sb, struct strbuf *sb2) { strbuf_add(sb, sb2->buf, sb2->len); } +extern void strbuf_adddup(struct strbuf *sb, size_t pos, size_t len); + +typedef void (*expand_fn_t) (struct strbuf *sb, const char *placeholder, void *context); +extern void strbuf_expand(struct strbuf *sb, const char *format, const char **placeholders, expand_fn_t fn, void *context); __attribute__((format(printf,2,3))) extern void strbuf_addf(struct strbuf *sb, const char *fmt, ...); @@ -113,5 +117,6 @@ extern int strbuf_read_file(struct strbuf *sb, const char *path, size_t hint); extern int strbuf_getline(struct strbuf *, FILE *, int); extern void stripspace(struct strbuf *buf, int skip_comments); +extern void launch_editor(const char *path, struct strbuf *buffer, const char *const *env); #endif /* STRBUF_H */ diff --git a/t/t0001-init.sh b/t/t0001-init.sh index b14b3ec394..c015405f12 100755 --- a/t/t0001-init.sh +++ b/t/t0001-init.sh @@ -25,7 +25,7 @@ check_config () { test_expect_success 'plain' ' ( - unset GIT_DIR GIT_WORK_TREE && + unset GIT_DIR GIT_WORK_TREE mkdir plain && cd plain && git init @@ -35,7 +35,7 @@ test_expect_success 'plain' ' test_expect_success 'plain with GIT_WORK_TREE' ' if ( - unset GIT_DIR && + unset GIT_DIR mkdir plain-wt && cd plain-wt && GIT_WORK_TREE=$(pwd) git init @@ -48,7 +48,7 @@ test_expect_success 'plain with GIT_WORK_TREE' ' test_expect_success 'plain bare' ' ( - unset GIT_DIR GIT_WORK_TREE GIT_CONFIG && + unset GIT_DIR GIT_WORK_TREE GIT_CONFIG mkdir plain-bare-1 && cd plain-bare-1 && git --bare init @@ -58,7 +58,7 @@ test_expect_success 'plain bare' ' test_expect_success 'plain bare with GIT_WORK_TREE' ' if ( - unset GIT_DIR GIT_CONFIG && + unset GIT_DIR GIT_CONFIG mkdir plain-bare-2 && cd plain-bare-2 && GIT_WORK_TREE=$(pwd) git --bare init @@ -72,7 +72,7 @@ test_expect_success 'plain bare with GIT_WORK_TREE' ' test_expect_success 'GIT_DIR bare' ' ( - unset GIT_CONFIG && + unset GIT_CONFIG mkdir git-dir-bare.git && GIT_DIR=git-dir-bare.git git init ) && @@ -82,7 +82,7 @@ test_expect_success 'GIT_DIR bare' ' test_expect_success 'GIT_DIR non-bare' ' ( - unset GIT_CONFIG && + unset GIT_CONFIG mkdir non-bare && cd non-bare && GIT_DIR=.git git init @@ -93,7 +93,7 @@ test_expect_success 'GIT_DIR non-bare' ' test_expect_success 'GIT_DIR & GIT_WORK_TREE (1)' ' ( - unset GIT_CONFIG && + unset GIT_CONFIG mkdir git-dir-wt-1.git && GIT_WORK_TREE=$(pwd) GIT_DIR=git-dir-wt-1.git git init ) && @@ -103,7 +103,7 @@ test_expect_success 'GIT_DIR & GIT_WORK_TREE (1)' ' test_expect_success 'GIT_DIR & GIT_WORK_TREE (2)' ' if ( - unset GIT_CONFIG && + unset GIT_CONFIG mkdir git-dir-wt-2.git && GIT_WORK_TREE=$(pwd) GIT_DIR=git-dir-wt-2.git git --bare init ) diff --git a/t/t0040-parse-options.sh b/t/t0040-parse-options.sh index ae49424aa0..462fdf262f 100755 --- a/t/t0040-parse-options.sh +++ b/t/t0040-parse-options.sh @@ -18,6 +18,7 @@ string options -s, --string <string> get a string --string2 <str> get another string + --st <st> get another string (pervert ordering) EOF @@ -90,4 +91,16 @@ test_expect_failure 'ambiguously abbreviated option' ' test $? != 129 ' +cat > expect << EOF +boolean: 0 +integer: 0 +string: 123 +EOF + +test_expect_success 'non ambiguous option (after two options it abbreviates)' ' + test-parse-options --st 123 > output 2> output.err && + test ! -s output.err && + git diff expect output +' + test_done diff --git a/t/t1302-repo-version.sh b/t/t1302-repo-version.sh new file mode 100755 index 0000000000..37fc1c8d36 --- /dev/null +++ b/t/t1302-repo-version.sh @@ -0,0 +1,46 @@ +#!/bin/sh +# +# Copyright (c) 2007 Nguyá»…n Thái Ngá»c Duy +# + +test_description='Test repository version check' + +. ./test-lib.sh + +cat >test.patch <<EOF +diff --git a/test.txt b/test.txt +new file mode 100644 +--- /dev/null ++++ b/test.txt +@@ -0,0 +1 @@ ++123 +EOF + +test_create_repo "test" +test_create_repo "test2" + +GIT_CONFIG=test2/.git/config git config core.repositoryformatversion 99 || exit 1 + +test_expect_success 'gitdir selection on normal repos' ' + (test "$(git config core.repositoryformatversion)" = 0 && + cd test && + test "$(git config core.repositoryformatversion)" = 0)' + +# Make sure it would stop at test2, not trash +test_expect_success 'gitdir selection on unsupported repo' ' + (cd test2 && + test "$(git config core.repositoryformatversion)" = 99)' + +test_expect_success 'gitdir not required mode' ' + (git apply --stat test.patch && + cd test && git apply --stat ../test.patch && + cd ../test2 && git apply --stat ../test.patch)' + +test_expect_success 'gitdir required mode on normal repos' ' + (git apply --check --index test.patch && + cd test && git apply --check --index ../test.patch)' + +test_expect_failure 'gitdir required mode on unsupported repo' ' + (cd test2 && git apply --check --index ../test.patch)' + +test_done diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh index ce045b2a57..a90824ba8a 100755 --- a/t/t1400-update-ref.sh +++ b/t/t1400-update-ref.sh @@ -205,7 +205,7 @@ test_expect_success \ echo $h_TEST >.git/MERGE_HEAD && GIT_AUTHOR_DATE="2005-05-26 23:45" \ GIT_COMMITTER_DATE="2005-05-26 23:45" git-commit -F M && - h_MERGED=$(git rev-parse --verify HEAD) + h_MERGED=$(git rev-parse --verify HEAD) && rm -f M' cat >expect <<EOF diff --git a/t/t1410-reflog.sh b/t/t1410-reflog.sh index e5bbc384f7..f959aae846 100755 --- a/t/t1410-reflog.sh +++ b/t/t1410-reflog.sh @@ -175,4 +175,22 @@ test_expect_success 'recover and check' ' ' +test_expect_success 'prune --expire' ' + + before=$(git count-objects | sed "s/ .*//") && + BLOB=$(echo aleph | git hash-object -w --stdin) && + BLOB_FILE=.git/objects/$(echo $BLOB | sed "s/^../&\//") && + test $((1 + $before)) = $(git count-objects | sed "s/ .*//") && + test -f $BLOB_FILE && + git reset --hard && + git prune --expire=1.hour.ago && + test $((1 + $before)) = $(git count-objects | sed "s/ .*//") && + test -f $BLOB_FILE && + test-chmtime -86500 $BLOB_FILE && + git prune --expire 1.day && + test $before = $(git count-objects | sed "s/ .*//") && + ! test -f $BLOB_FILE + +' + test_done diff --git a/t/t2008-checkout-subdir.sh b/t/t2008-checkout-subdir.sh new file mode 100755 index 0000000000..f78945ed8e --- /dev/null +++ b/t/t2008-checkout-subdir.sh @@ -0,0 +1,82 @@ +#!/bin/sh +# +# Copyright (c) 2007 David Symonds + +test_description='git checkout from subdirectories' + +. ./test-lib.sh + +test_expect_success setup ' + + echo "base" > file0 && + git add file0 && + mkdir dir1 && + echo "hello" > dir1/file1 && + git add dir1/file1 && + mkdir dir2 && + echo "bonjour" > dir2/file2 && + git add dir2/file2 && + test_tick && + git commit -m "populate tree" + +' + +test_expect_success 'remove and restore with relative path' ' + + ( + cd dir1 && + rm ../file0 && + git checkout HEAD -- ../file0 && + test "base" = "$(cat ../file0)" && + rm ../dir2/file2 && + git checkout HEAD -- ../dir2/file2 && + test "bonjour" = "$(cat ../dir2/file2)" && + rm ../file0 ./file1 && + git checkout HEAD -- .. && + test "base" = "$(cat ../file0)" && + test "hello" = "$(cat file1)" + ) + +' + +test_expect_success 'checkout with empty prefix' ' + + rm file0 && + git checkout HEAD -- file0 && + test "base" = "$(cat file0)" + +' + +test_expect_success 'checkout with simple prefix' ' + + rm dir1/file1 && + git checkout HEAD -- dir1 && + test "hello" = "$(cat dir1/file1)" && + rm dir1/file1 && + git checkout HEAD -- dir1/file1 && + test "hello" = "$(cat dir1/file1)" + +' + +# This is not expected to work as ls-files was not designed +# to deal with such. Enable it when ls-files is updated. +: test_expect_success 'checkout with complex relative path' ' + + rm file1 && + git checkout HEAD -- ../dir1/../dir1/file1 && test -f ./file1 + +' + +test_expect_failure 'relative path outside tree should fail' \ + 'git checkout HEAD -- ../../Makefile' + +test_expect_failure 'incorrect relative path to file should fail (1)' \ + 'git checkout HEAD -- ../file0' + +test_expect_failure 'incorrect relative path should fail (2)' \ + '( cd dir1 && git checkout HEAD -- ./file0 )' + +test_expect_failure 'incorrect relative path should fail (3)' \ + '( cd dir1 && git checkout HEAD -- ../../file0 )' + +test_done diff --git a/t/t2200-add-update.sh b/t/t2200-add-update.sh index eb1ced3c37..24f892f793 100755 --- a/t/t2200-add-update.sh +++ b/t/t2200-add-update.sh @@ -1,6 +1,6 @@ #!/bin/sh -test_description='git add -u with path limiting +test_description='git add -u This test creates a working tree state with three files: @@ -9,7 +9,10 @@ This test creates a working tree state with three files: dir/other (untracked) and issues a git add -u with path limiting on "dir" to add -only the updates to dir/sub.' +only the updates to dir/sub. + +Also tested are "git add -u" without limiting, and "git add -u" +without contents changes.' . ./test-lib.sh @@ -85,4 +88,27 @@ test_expect_success 'replace a file with a symlink' ' ' +test_expect_success 'add everything changed' ' + + git add -u && + test -z "$(git diff-files)" + +' + +test_expect_success 'touch and then add -u' ' + + touch check && + git add -u && + test -z "$(git diff-files)" + +' + +test_expect_success 'touch and then add explicitly' ' + + touch check && + git add check && + test -z "$(git diff-files)" + +' + test_done diff --git a/t/t3001-ls-files-others-exclude.sh b/t/t3001-ls-files-others-exclude.sh index ae0639d8f3..e25b255683 100755 --- a/t/t3001-ls-files-others-exclude.sh +++ b/t/t3001-ls-files-others-exclude.sh @@ -86,7 +86,7 @@ EOF git config core.excludesFile excludes-file -git runstatus | grep "^# " > output +git status | grep "^# " > output cat > expect << EOF # .gitignore diff --git a/t/t3201-branch-contains.sh b/t/t3201-branch-contains.sh new file mode 100755 index 0000000000..9ef593f0e1 --- /dev/null +++ b/t/t3201-branch-contains.sh @@ -0,0 +1,58 @@ +#!/bin/sh + +test_description='branch --contains <commit>' + +. ./test-lib.sh + +test_expect_success setup ' + + >file && + git add file && + test_tick && + git commit -m initial && + git branch side && + + echo 1 >file && + test_tick && + git commit -a -m "second on master" && + + git checkout side && + echo 1 >file && + test_tick && + git commit -a -m "second on side" && + + git merge master + +' + +test_expect_success 'branch --contains=master' ' + + git branch --contains=master >actual && + { + echo " master" && echo "* side" + } >expect && + diff -u expect actual + +' + +test_expect_success 'branch --contains master' ' + + git branch --contains master >actual && + { + echo " master" && echo "* side" + } >expect && + diff -u expect actual + +' + +test_expect_success 'branch --contains=side' ' + + git branch --contains=side >actual && + { + echo "* side" + } >expect && + diff -u expect actual + +' + +test_done diff --git a/t/t3402-rebase-merge.sh b/t/t3402-rebase-merge.sh index 0779aaa9ab..7b7d07269a 100755 --- a/t/t3402-rebase-merge.sh +++ b/t/t3402-rebase-merge.sh @@ -48,9 +48,14 @@ test_expect_success 'reference merge' ' git merge -s recursive "reference merge" HEAD master ' +PRE_REBASE=$(git rev-parse test-rebase) test_expect_success rebase ' git checkout test-rebase && - git rebase --merge master + GIT_TRACE=1 git rebase --merge master +' + +test_expect_success 'test-rebase@{1} is pre rebase' ' + test $PRE_REBASE = $(git rev-parse test-rebase@{1}) ' test_expect_success 'merge and rebase should match' ' diff --git a/t/t3403-rebase-skip.sh b/t/t3403-rebase-skip.sh index eab053c3e0..657f68104d 100755 --- a/t/t3403-rebase-skip.sh +++ b/t/t3403-rebase-skip.sh @@ -36,21 +36,36 @@ test_expect_failure 'rebase with git am -3 (default)' ' ' test_expect_success 'rebase --skip with am -3' ' - git reset --hard HEAD && git rebase --skip ' + +test_expect_success 'rebase moves back to skip-reference' ' + test refs/heads/skip-reference = $(git symbolic-ref HEAD) && + git branch post-rebase && + git reset --hard pre-rebase && + ! git rebase master && + echo "hello" > hello && + git add hello && + git rebase --continue && + test refs/heads/skip-reference = $(git symbolic-ref HEAD) && + git reset --hard post-rebase +' + test_expect_success 'checkout skip-merge' 'git checkout -f skip-merge' test_expect_failure 'rebase with --merge' 'git rebase --merge master' test_expect_success 'rebase --skip with --merge' ' - git reset --hard HEAD && git rebase --skip ' test_expect_success 'merge and reference trees equal' \ 'test -z "`git diff-tree skip-merge skip-reference`"' +test_expect_success 'moved back to branch correctly' ' + test refs/heads/skip-merge = $(git symbolic-ref HEAD) +' + test_debug 'gitk --all & sleep 1' test_done diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh index 11139048fe..907c7f9f6b 100755 --- a/t/t3404-rebase-interactive.sh +++ b/t/t3404-rebase-interactive.sh @@ -149,7 +149,8 @@ test_expect_success 'stop on conflicting pick' ' diff -u expect .git/.dotest-merge/patch && diff -u expect2 file1 && test 4 = $(grep -v "^#" < .git/.dotest-merge/done | wc -l) && - test 0 = $(grep -v "^#" < .git/.dotest-merge/todo | wc -l) + test 0 = $(grep -ve "^#" -e "^$" < .git/.dotest-merge/git-rebase-todo | + wc -l) ' test_expect_success 'abort' ' diff --git a/t/t3502-cherry-pick-merge.sh b/t/t3502-cherry-pick-merge.sh new file mode 100755 index 0000000000..7c92e261fc --- /dev/null +++ b/t/t3502-cherry-pick-merge.sh @@ -0,0 +1,123 @@ +#!/bin/sh + +test_description='cherry picking and reverting a merge + + b---c + / / + initial---a + +' + +. ./test-lib.sh + +test_expect_success setup ' + + >A && + >B && + git add A B && + git commit -m "Initial" && + git tag initial && + git branch side && + echo new line >A && + git commit -m "add line to A" A && + git tag a && + git checkout side && + echo new line >B && + git commit -m "add line to B" B && + git tag b && + git checkout master && + git merge side && + git tag c + +' + +test_expect_success 'cherry-pick a non-merge with -m should fail' ' + + git reset --hard && + git checkout a^0 && + ! git cherry-pick -m 1 b && + git diff --exit-code a -- + +' + +test_expect_success 'cherry pick a merge without -m should fail' ' + + git reset --hard && + git checkout a^0 && + ! git cherry-pick c && + git diff --exit-code a -- + +' + +test_expect_success 'cherry pick a merge (1)' ' + + git reset --hard && + git checkout a^0 && + git cherry-pick -m 1 c && + git diff --exit-code c + +' + +test_expect_success 'cherry pick a merge (2)' ' + + git reset --hard && + git checkout b^0 && + git cherry-pick -m 2 c && + git diff --exit-code c + +' + +test_expect_success 'cherry pick a merge relative to nonexistent parent should fail' ' + + git reset --hard && + git checkout b^0 && + ! git cherry-pick -m 3 c + +' + +test_expect_success 'revert a non-merge with -m should fail' ' + + git reset --hard && + git checkout c^0 && + ! git revert -m 1 b && + git diff --exit-code c + +' + +test_expect_success 'revert a merge without -m should fail' ' + + git reset --hard && + git checkout c^0 && + ! git revert c && + git diff --exit-code c + +' + +test_expect_success 'revert a merge (1)' ' + + git reset --hard && + git checkout c^0 && + git revert -m 1 c && + git diff --exit-code a -- + +' + +test_expect_success 'revert a merge (2)' ' + + git reset --hard && + git checkout c^0 && + git revert -m 2 c && + git diff --exit-code b -- + +' + +test_expect_success 'revert a merge relative to nonexistent parent should fail' ' + + git reset --hard && + git checkout c^0 && + ! git revert -m 3 c && + git diff --exit-code c + +' + +test_done diff --git a/t/t3700-add.sh b/t/t3700-add.sh index a328bf57eb..287e058e37 100755 --- a/t/t3700-add.sh +++ b/t/t3700-add.sh @@ -104,9 +104,33 @@ test_expect_success 'add ignored ones with -f' ' git ls-files --error-unmatch d.ig/d.if d.ig/d.ig ' +test_expect_success 'add ignored ones with -f' ' + rm -f .git/index && + git add -f d.?? && + git ls-files --error-unmatch d.ig/d.if d.ig/d.ig +' + +test_expect_success '.gitignore with subdirectory' ' + + rm -f .git/index && + mkdir -p sub/dir && + echo "!dir/a.*" >sub/.gitignore && + >sub/a.ig && + >sub/dir/a.ig && + git add sub/dir && + git ls-files --error-unmatch sub/dir/a.ig && + rm -f .git/index && + ( + cd sub/dir && + git add . + ) && + git ls-files --error-unmatch sub/dir/a.ig +' + mkdir 1 1/2 1/3 touch 1/2/a 1/3/b 1/2/c test_expect_success 'check correct prefix detection' ' + rm -f .git/index && git add 1/2/a 1/3/b 1/2/c ' diff --git a/t/t3902-quoted.sh b/t/t3902-quoted.sh index 245fb3babd..73da45f18c 100755 --- a/t/t3902-quoted.sh +++ b/t/t3902-quoted.sh @@ -20,6 +20,13 @@ LF=' ' DQ='"' +echo foo > "Name and an${HT}HT" +test -f "Name and an${HT}HT" || { + # since FAT/NTFS does not allow tabs in filenames, skip this test + say 'Your filesystem does not allow tabs in filenames, test skipped.' + test_done +} + for_each_name () { for name in \ Name "Name and a${LF}LF" "Name and an${HT}HT" "Name${DQ}" \ diff --git a/t/t4000-diff-format.sh b/t/t4000-diff-format.sh index 7d92ae3e99..c44b27aeb2 100755 --- a/t/t4000-diff-format.sh +++ b/t/t4000-diff-format.sh @@ -16,7 +16,7 @@ cat path0 >path1 chmod +x path1 test_expect_success \ - 'update-cache --add two files with and without +x.' \ + 'update-index --add two files with and without +x.' \ 'git update-index --add path0 path1' mv path0 path0- diff --git a/t/t4001-diff-rename.sh b/t/t4001-diff-rename.sh index 063e79257a..a32692417d 100755 --- a/t/t4001-diff-rename.sh +++ b/t/t4001-diff-rename.sh @@ -27,7 +27,7 @@ Line 15 ' test_expect_success \ - 'update-cache --add a file.' \ + 'update-index --add a file.' \ 'git update-index --add path0' test_expect_success \ @@ -71,10 +71,10 @@ test_expect_success 'favour same basenames over different ones' ' git rm path1 && mkdir subdir && git mv another-path subdir/path1 && - git runstatus | grep "renamed: .*path1 -> subdir/path1"' + git status | grep "renamed: .*path1 -> subdir/path1"' test_expect_success 'favour same basenames even with minor differences' ' git show HEAD:path1 | sed "s/15/16/" > subdir/path1 && - git runstatus | grep "renamed: .*path1 -> subdir/path1"' + git status | grep "renamed: .*path1 -> subdir/path1"' test_done diff --git a/t/t4008-diff-break-rewrite.sh b/t/t4008-diff-break-rewrite.sh index 5836e3a899..26c2e4aa65 100755 --- a/t/t4008-diff-break-rewrite.sh +++ b/t/t4008-diff-break-rewrite.sh @@ -119,14 +119,14 @@ test_expect_success \ 'compare_diff_raw expected current' test_expect_success \ - 'run diff with -B' \ + 'run diff with -B -M' \ 'git diff-index -B -M "$tree" >current' -# This should not mistake file0 as the copy source of new file1 -# due to type differences. +# file0 changed from regular to symlink. file1 is very close to the preimage of file0. +# because we break file0, file1 can become a rename of it. cat >expected <<\EOF :100644 120000 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 67be421f88824578857624f7b3dc75e99a8a1481 T file0 -:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 M100 file1 +:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 R file0 file1 EOF test_expect_success \ diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh index f9db81d3ab..f9db81d3ab 100644..100755 --- a/t/t4018-diff-funcname.sh +++ b/t/t4018-diff-funcname.sh diff --git a/t/t4021-format-patch-numbered.sh b/t/t4021-format-patch-numbered.sh new file mode 100755 index 0000000000..43d64bbd82 --- /dev/null +++ b/t/t4021-format-patch-numbered.sh @@ -0,0 +1,106 @@ +#!/bin/sh +# +# Copyright (c) 2006 Brian C Gernhardt +# + +test_description='Format-patch numbering options' + +. ./test-lib.sh + +test_expect_success setup ' + + echo A > file && + git add file && + git commit -m First && + + echo B >> file && + git commit -a -m Second && + + echo C >> file && + git commit -a -m Third + +' + +# Each of these gets used multiple times. + +test_num_no_numbered() { + cnt=$(grep "^Subject: \[PATCH\]" $1 | wc -l) && + test $cnt = $2 +} + +test_single_no_numbered() { + test_num_no_numbered $1 1 +} + +test_no_numbered() { + test_num_no_numbered $1 2 +} + +test_single_numbered() { + grep "^Subject: \[PATCH 1/1\]" $1 +} + +test_numbered() { + grep "^Subject: \[PATCH 1/2\]" $1 && + grep "^Subject: \[PATCH 2/2\]" $1 +} + +test_expect_success 'Default: no numbered' ' + + git format-patch --stdout HEAD~2 >patch0 && + test_no_numbered patch0 + +' + +test_expect_success 'Use --numbered' ' + + git format-patch --numbered --stdout HEAD~2 >patch1 && + test_numbered patch1 + +' + +test_expect_success 'format.numbered = true' ' + + git config format.numbered true && + git format-patch --stdout HEAD~2 >patch2 && + test_numbered patch2 + +' + +test_expect_success 'format.numbered && single patch' ' + + git format-patch --stdout HEAD^ > patch3 && + test_single_numbered patch3 + +' + +test_expect_success 'format.numbered && --no-numbered' ' + + git format-patch --no-numbered --stdout HEAD~2 >patch4 && + test_no_numbered patch4 + +' + +test_expect_success 'format.numbered = auto' ' + + git config format.numbered auto + git format-patch --stdout HEAD~2 > patch5 && + test_numbered patch5 + +' + +test_expect_success 'format.numbered = auto && single patch' ' + + git format-patch --stdout HEAD^ > patch6 && + test_single_no_numbered patch6 + +' + +test_expect_success 'format.numbered = auto && --no-numbered' ' + + git format-patch --no-numbered --stdout HEAD~2 > patch7 && + test_no_numbered patch7 + +' + +test_done diff --git a/t/t4021-format-patch-signer-mime.sh b/t/t4021-format-patch-signer-mime.sh new file mode 100755 index 0000000000..67a70fadab --- /dev/null +++ b/t/t4021-format-patch-signer-mime.sh @@ -0,0 +1,42 @@ +#!/bin/sh + +test_description='format-patch -s should force MIME encoding as needed' + +. ./test-lib.sh + +test_expect_success setup ' + + >F && + git add F && + git commit -m initial && + echo new line >F && + + test_tick && + git commit -m "This adds some lines to F" F + +' + +test_expect_success 'format normally' ' + + git format-patch --stdout -1 >output && + ! grep Content-Type output + +' + +test_expect_success 'format with signoff without funny signer name' ' + + git format-patch -s --stdout -1 >output && + ! grep Content-Type output + +' + +test_expect_success 'format with non ASCII signer name' ' + + GIT_COMMITTER_NAME="$B$O$^$N(B $B$U$K$*$&(B" \ + git format-patch -s --stdout -1 >output && + grep Content-Type output + +' + +test_done + diff --git a/t/t4022-diff-rewrite.sh b/t/t4022-diff-rewrite.sh new file mode 100755 index 0000000000..6de4acbd44 --- /dev/null +++ b/t/t4022-diff-rewrite.sh @@ -0,0 +1,26 @@ +#!/bin/sh + +test_description='rewrite diff' + +. ./test-lib.sh + +test_expect_success setup ' + + cat ../../COPYING >test && + git add test && + tr 'a-zA-Z' 'n-za-mN-ZA-M' <../../COPYING >test + +' + +test_expect_success 'detect rewrite' ' + + actual=$(git diff-files -B --summary test) && + expr "$actual" : " rewrite test ([0-9]*%)$" || { + echo "Eh? <<$actual>>" + false + } + +' + +test_done + diff --git a/t/t4023-diff-rename-typechange.sh b/t/t4023-diff-rename-typechange.sh new file mode 100755 index 0000000000..255604effd --- /dev/null +++ b/t/t4023-diff-rename-typechange.sh @@ -0,0 +1,86 @@ +#!/bin/sh + +test_description='typechange rename detection' + +. ./test-lib.sh + +test_expect_success setup ' + + rm -f foo bar && + cat ../../COPYING >foo && + ln -s linklink bar && + git add foo bar && + git commit -a -m Initial && + git tag one && + + rm -f foo bar && + cat ../../COPYING >bar && + ln -s linklink foo && + git add foo bar && + git commit -a -m Second && + git tag two && + + rm -f foo bar && + cat ../../COPYING >foo && + git add foo && + git commit -a -m Third && + git tag three && + + mv foo bar && + ln -s linklink foo && + git add foo bar && + git commit -a -m Fourth && + git tag four && + + # This is purely for sanity check + + rm -f foo bar && + cat ../../COPYING >foo && + cat ../../Makefile >bar && + git add foo bar && + git commit -a -m Fifth && + git tag five && + + rm -f foo bar && + cat ../../Makefile >foo && + cat ../../COPYING >bar && + git add foo bar && + git commit -a -m Sixth && + git tag six + +' + +test_expect_success 'cross renames to be detected for regular files' ' + + git diff-tree five six -r --name-status -B -M | sort >actual && + { + echo "R100 foo bar" + echo "R100 bar foo" + } | sort >expect && + diff -u expect actual + +' + +test_expect_success 'cross renames to be detected for typechange' ' + + git diff-tree one two -r --name-status -B -M | sort >actual && + { + echo "R100 foo bar" + echo "R100 bar foo" + } | sort >expect && + diff -u expect actual + +' + +test_expect_success 'moves and renames' ' + + git diff-tree three four -r --name-status -B -M | sort >actual && + { + echo "R100 foo bar" + echo "T100 foo" + } | sort >expect && + diff -u expect actual + +' + +test_done diff --git a/t/t4100/t-apply-1.patch b/t/t4100/t-apply-1.patch index de587517f4..90ab54f0f5 100644 --- a/t/t4100/t-apply-1.patch +++ b/t/t4100/t-apply-1.patch @@ -90,7 +90,7 @@ diff --git a/Documentation/git.txt b/Documentation/git.txt diff --git a/Makefile b/Makefile --- a/Makefile +++ b/Makefile -@@ -30,7 +30,7 @@ PROG= git-update-cache git-diff-files +@@ -30,7 +30,7 @@ PROG= git-update-index git-diff-files git-checkout-cache git-diff-tree git-rev-tree git-ls-files \ git-check-files git-ls-tree git-merge-base git-merge-cache \ git-unpack-file git-export git-diff-cache git-convert-cache \ diff --git a/t/t4100/t-apply-2.patch b/t/t4100/t-apply-2.patch index cfdc80885b..f5c7d601fc 100644 --- a/t/t4100/t-apply-2.patch +++ b/t/t4100/t-apply-2.patch @@ -9,7 +9,7 @@ diff --git a/Makefile b/Makefile - git-deltafy-script + git-deltafy-script git-fetch-script - PROG= git-update-cache git-diff-files git-init-db git-write-tree \ + PROG= git-update-index git-diff-files git-init-db git-write-tree \ git-read-tree git-commit-tree git-cat-file git-fsck-cache \ diff --git a/git-pull-script b/git-fetch-script similarity index 87% diff --git a/t/t4100/t-apply-5.patch b/t/t4100/t-apply-5.patch index de11623d1b..5f6ddc1059 100644 --- a/t/t4100/t-apply-5.patch +++ b/t/t4100/t-apply-5.patch @@ -200,7 +200,7 @@ diff a/Documentation/git.txt b/Documentation/git.txt diff a/Makefile b/Makefile --- a/Makefile +++ b/Makefile -@@ -30,7 +30,7 @@ PROG= git-update-cache git-diff-files +@@ -30,7 +30,7 @@ PROG= git-update-index git-diff-files git-checkout-cache git-diff-tree git-rev-tree git-ls-files \ git-check-files git-ls-tree git-merge-base git-merge-cache \ git-unpack-file git-export git-diff-cache git-convert-cache \ diff --git a/t/t4100/t-apply-6.patch b/t/t4100/t-apply-6.patch index d9753637fc..a72729a712 100644 --- a/t/t4100/t-apply-6.patch +++ b/t/t4100/t-apply-6.patch @@ -8,7 +8,7 @@ diff a/Makefile b/Makefile - git-deltafy-script + git-deltafy-script git-fetch-script - PROG= git-update-cache git-diff-files git-init-db git-write-tree \ + PROG= git-update-index git-diff-files git-init-db git-write-tree \ git-read-tree git-commit-tree git-cat-file git-fsck-cache \ diff a/git-fetch-script b/git-fetch-script --- /dev/null diff --git a/t/t4119-apply-config.sh b/t/t4119-apply-config.sh index 65571e0549..b540f7295a 100755 --- a/t/t4119-apply-config.sh +++ b/t/t4119-apply-config.sh @@ -24,7 +24,7 @@ cat >gpatch.file <<\EOF && +++ file1+ 2007-02-21 01:07:44.000000000 -0800 @@ -1 +1 @@ -A -+B ++B EOF sed -e 's|file1|sub/&|' gpatch.file >gpatch-sub.file && diff --git a/t/t5100/sample.mbox b/t/t5100/sample.mbox index b80c981c16..070c1661b9 100644 --- a/t/t5100/sample.mbox +++ b/t/t5100/sample.mbox @@ -1,3 +1,6 @@ + + + From nobody Mon Sep 17 00:00:00 2001 From: A U Thor <a.u.thor@example.com> Date: Fri, 9 Jun 2006 00:44:16 -0700 diff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh index ba7579c251..f1106e6542 100755 --- a/t/t5300-pack-object.sh +++ b/t/t5300-pack-object.sh @@ -187,49 +187,51 @@ test_expect_success \ test-3-${packname_3}.idx' test_expect_success \ - 'corrupt a pack and see if verify catches' \ + 'verify-pack catches mismatched .idx and .pack files' \ 'cat test-1-${packname_1}.idx >test-3.idx && cat test-2-${packname_2}.pack >test-3.pack && if git verify-pack test-3.idx then false else :; - fi && + fi' - : PACK_SIGNATURE && - cat test-1-${packname_1}.pack >test-3.pack && +test_expect_success \ + 'verify-pack catches a corrupted pack signature' \ + 'cat test-1-${packname_1}.pack >test-3.pack && dd if=/dev/zero of=test-3.pack count=1 bs=1 conv=notrunc seek=2 && if git verify-pack test-3.idx then false else :; - fi && + fi' - : PACK_VERSION && - cat test-1-${packname_1}.pack >test-3.pack && +test_expect_success \ + 'verify-pack catches a corrupted pack version' \ + 'cat test-1-${packname_1}.pack >test-3.pack && dd if=/dev/zero of=test-3.pack count=1 bs=1 conv=notrunc seek=7 && if git verify-pack test-3.idx then false else :; - fi && + fi' - : TYPE/SIZE byte of the first packed object data && - cat test-1-${packname_1}.pack >test-3.pack && +test_expect_success \ + 'verify-pack catches a corrupted type/size of the 1st packed object data' \ + 'cat test-1-${packname_1}.pack >test-3.pack && dd if=/dev/zero of=test-3.pack count=1 bs=1 conv=notrunc seek=12 && if git verify-pack test-3.idx then false else :; - fi && + fi' - : sum of the index file itself && - l=`wc -c <test-3.idx` && +test_expect_success \ + 'verify-pack catches a corrupted sum of the index file itself' \ + 'l=`wc -c <test-3.idx` && l=`expr $l - 20` && cat test-1-${packname_1}.pack >test-3.pack && dd if=/dev/zero of=test-3.idx count=20 bs=1 conv=notrunc seek=$l && if git verify-pack test-3.pack then false else :; - fi && - - :' + fi' test_expect_success \ 'build pack index for an existing pack' \ diff --git a/t/t5302-pack-index.sh b/t/t5302-pack-index.sh index 4f58c4c3f9..2a2878b572 100755 --- a/t/t5302-pack-index.sh +++ b/t/t5302-pack-index.sh @@ -61,17 +61,33 @@ test_expect_success \ test_expect_success \ 'index v2: force some 64-bit offsets with pack-objects' \ - 'pack3=$(git pack-objects --index-version=2,0x40000 test-3 <obj-list) && - git verify-pack -v "test-3-${pack3}.pack"' + 'pack3=$(git pack-objects --index-version=2,0x40000 test-3 <obj-list)' + +have_64bits= +if msg=$(git verify-pack -v "test-3-${pack3}.pack" 2>&1) || + ! echo "$msg" | grep "pack too large .* off_t" +then + have_64bits=t +else + say "skipping tests concerning 64-bit offsets" +fi + +test "$have_64bits" && +test_expect_success \ + 'index v2: verify a pack with some 64-bit offsets' \ + 'git verify-pack -v "test-3-${pack3}.pack"' +test "$have_64bits" && test_expect_failure \ '64-bit offsets: should be different from previous index v2 results' \ 'cmp "test-2-${pack2}.idx" "test-3-${pack3}.idx"' +test "$have_64bits" && test_expect_success \ 'index v2: force some 64-bit offsets with index-pack' \ 'git-index-pack --index-version=2,0x40000 -o 3.idx "test-1-${pack1}.pack"' +test "$have_64bits" && test_expect_success \ '64-bit offsets: index-pack result should match pack-objects one' \ 'cmp "test-3-${pack3}.idx" "3.idx"' @@ -116,11 +132,11 @@ test_expect_failure \ test_expect_success \ '[index v2] 1) stream pack to repository' \ 'rm -f .git/objects/pack/* && - git-index-pack --index-version=2,0x40000 --stdin < "test-1-${pack1}.pack" && + git-index-pack --index-version=2 --stdin < "test-1-${pack1}.pack" && git prune-packed && git count-objects | ( read nr rest && test "$nr" -eq 1 ) && cmp "test-1-${pack1}.pack" ".git/objects/pack/pack-${pack1}.pack" && - cmp "test-3-${pack1}.idx" ".git/objects/pack/pack-${pack1}.idx"' + cmp "test-2-${pack1}.idx" ".git/objects/pack/pack-${pack1}.idx"' test_expect_success \ '[index v2] 2) create a stealth corruption in a delta base reference' \ diff --git a/t/t5404-tracking-branches.sh b/t/t5404-tracking-branches.sh new file mode 100755 index 0000000000..1493a92c06 --- /dev/null +++ b/t/t5404-tracking-branches.sh @@ -0,0 +1,53 @@ +#!/bin/sh + +test_description='tracking branch update checks for git push' + +. ./test-lib.sh + +test_expect_success 'setup' ' + echo 1 >file && + git add file && + git commit -m 1 && + git branch b1 && + git branch b2 && + git clone . aa && + git checkout b1 && + echo b1 >>file && + git commit -a -m b1 && + git checkout b2 && + echo b2 >>file && + git commit -a -m b2 +' + +test_expect_success 'prepare pushable branches' ' + cd aa && + b1=$(git rev-parse origin/b1) && + b2=$(git rev-parse origin/b2) && + git checkout -b b1 origin/b1 && + echo aa-b1 >>file && + git commit -a -m aa-b1 && + git checkout -b b2 origin/b2 && + echo aa-b2 >>file && + git commit -a -m aa-b2 && + git checkout master && + echo aa-master >>file && + git commit -a -m aa-master +' + +test_expect_success 'mixed-success push returns error' '! git push' + +test_expect_success 'check tracking branches updated correctly after push' ' + test "$(git rev-parse origin/master)" = "$(git rev-parse master)" +' + +test_expect_success 'check tracking branches not updated for failed refs' ' + test "$(git rev-parse origin/b1)" = "$b1" && + test "$(git rev-parse origin/b2)" = "$b2" +' + +test_expect_success 'deleted branches have their tracking branches removed' ' + git push origin :b1 && + test "$(git rev-parse origin/b1)" = "origin/b1" +' + +test_done diff --git a/t/t5405-send-pack-rewind.sh b/t/t5405-send-pack-rewind.sh new file mode 100755 index 0000000000..86abc62271 --- /dev/null +++ b/t/t5405-send-pack-rewind.sh @@ -0,0 +1,42 @@ +#!/bin/sh + +test_description='forced push to replace commit we do not have' + +. ./test-lib.sh + +test_expect_success setup ' + + >file1 && git add file1 && test_tick && + git commit -m Initial && + + mkdir another && ( + cd another && + git init && + git fetch .. master:master + ) && + + >file2 && git add file2 && test_tick && + git commit -m Second + +' + +test_expect_success 'non forced push should die not segfault' ' + + ( + cd another && + git push .. master:master + test $? = 1 + ) + +' + +test_expect_success 'forced push should succeed' ' + + ( + cd another && + git push .. +master:master + ) + +' + +test_done diff --git a/t/t5406-remote-rejects.sh b/t/t5406-remote-rejects.sh new file mode 100755 index 0000000000..46b2cb4e46 --- /dev/null +++ b/t/t5406-remote-rejects.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +test_description='remote push rejects are reported by client' + +. ./test-lib.sh + +test_expect_success 'setup' ' + mkdir .git/hooks && + (echo "#!/bin/sh" ; echo "exit 1") >.git/hooks/update && + chmod +x .git/hooks/update && + echo 1 >file && + git add file && + git commit -m 1 && + git clone . child && + cd child && + echo 2 >file && + git commit -a -m 2 +' + +test_expect_success 'push reports error' '! git push 2>stderr' + +test_expect_success 'individual ref reports error' 'grep rejected stderr' + +test_done diff --git a/t/t5502-quickfetch.sh b/t/t5502-quickfetch.sh index b4760f2dc0..16eadd6b68 100755 --- a/t/t5502-quickfetch.sh +++ b/t/t5502-quickfetch.sh @@ -86,4 +86,37 @@ test_expect_success 'quickfetch should not leave a corrupted repository' ' ' +test_expect_success 'quickfetch should not copy from alternate' ' + + ( + mkdir quickclone && + cd quickclone && + git init-db && + (cd ../.git/objects && pwd) >.git/objects/info/alternates && + git remote add origin .. && + git fetch -k -k + ) && + obj_cnt=$( ( + cd quickclone && + git count-objects | sed -e "s/ *objects,.*//" + ) ) && + pck_cnt=$( ( + cd quickclone && + git count-objects -v | sed -n -e "/packs:/{ + s/packs:// + p + q + }" + ) ) && + origin_master=$( ( + cd quickclone && + git rev-parse origin/master + ) ) && + echo "loose objects: $obj_cnt, packfiles: $pck_cnt" && + test $obj_cnt -eq 0 && + test $pck_cnt -eq 0 && + test z$origin_master = z$(git rev-parse master) + +' + test_done diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh index d217657146..02882c1e4b 100755 --- a/t/t5510-fetch.sh +++ b/t/t5510-fetch.sh @@ -95,6 +95,31 @@ test_expect_success 'fetch following tags' ' ' +test_expect_failure 'fetch must not resolve short tag name' ' + + cd "$D" && + + mkdir five && + cd five && + git init && + + git fetch .. anno:five + +' + +test_expect_failure 'fetch must not resolve short remote name' ' + + cd "$D" && + git-update-ref refs/remotes/six/HEAD HEAD + + mkdir six && + cd six && + git init && + + git fetch .. six:six + +' + test_expect_success 'create bundle 1' ' cd "$D" && echo >file updated again by origin && @@ -208,4 +233,66 @@ test_expect_success 'fetch with a non-applying branch.<name>.merge' ' git fetch blub ' +# the strange name is: a\!'b +test_expect_success 'quoting of a strangely named repo' ' + ! git fetch "a\\!'\''b" > result 2>&1 && + cat result && + grep "fatal: '\''a\\\\!'\''b'\''" result +' + +test_expect_success 'bundle should record HEAD correctly' ' + + cd "$D" && + git bundle create bundle5 HEAD master && + git bundle list-heads bundle5 >actual && + for h in HEAD refs/heads/master + do + echo "$(git rev-parse --verify $h) $h" + done >expect && + diff -u expect actual + +' + +test_expect_success 'explicit fetch should not update tracking' ' + + cd "$D" && + git branch -f side && + ( + cd three && + o=$(git rev-parse --verify refs/remotes/origin/master) && + git fetch origin master && + n=$(git rev-parse --verify refs/remotes/origin/master) && + test "$o" = "$n" && + ! git rev-parse --verify refs/remotes/origin/side + ) +' + +test_expect_success 'explicit pull should not update tracking' ' + + cd "$D" && + git branch -f side && + ( + cd three && + o=$(git rev-parse --verify refs/remotes/origin/master) && + git pull origin master && + n=$(git rev-parse --verify refs/remotes/origin/master) && + test "$o" = "$n" && + ! git rev-parse --verify refs/remotes/origin/side + ) +' + +test_expect_success 'configured fetch updates tracking' ' + + cd "$D" && + git branch -f side && + ( + cd three && + o=$(git rev-parse --verify refs/remotes/origin/master) && + git fetch origin && + n=$(git rev-parse --verify refs/remotes/origin/master) && + test "$o" != "$n" && + git rev-parse --verify refs/remotes/origin/side + ) +' + test_done diff --git a/t/t5512-ls-remote.sh b/t/t5512-ls-remote.sh new file mode 100755 index 0000000000..6ec5f7c48b --- /dev/null +++ b/t/t5512-ls-remote.sh @@ -0,0 +1,52 @@ +#!/bin/sh + +test_description='git ls-remote' + +. ./test-lib.sh + +test_expect_success setup ' + + >file && + git add file && + test_tick && + git commit -m initial && + git tag mark && + git show-ref --tags -d | sed -e "s/ / /" >expected.tag && + ( + echo "$(git rev-parse HEAD) HEAD" + git show-ref -d | sed -e "s/ / /" + ) >expected.all && + + git remote add self $(pwd)/.git + +' + +test_expect_success 'ls-remote --tags .git' ' + + git ls-remote --tags .git >actual && + diff -u expected.tag actual + +' + +test_expect_success 'ls-remote .git' ' + + git ls-remote .git >actual && + diff -u expected.all actual + +' + +test_expect_success 'ls-remote --tags self' ' + + git ls-remote --tags self >actual && + diff -u expected.tag actual + +' + +test_expect_success 'ls-remote self' ' + + git ls-remote self >actual && + diff -u expected.all actual + +' + +test_done diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh index 86f9b5346a..9d2dc33cbd 100755 --- a/t/t5516-fetch-push.sh +++ b/t/t5516-fetch-push.sh @@ -145,11 +145,21 @@ test_expect_success 'push with no ambiguity (1)' ' test_expect_success 'push with no ambiguity (2)' ' mk_test remotes/origin/master && - git push testrepo master:master && + git push testrepo master:origin/master && check_push_result $the_commit remotes/origin/master ' +test_expect_success 'push with colon-less refspec, no ambiguity' ' + + mk_test heads/master heads/t/master && + git branch -f t/master master && + git push testrepo master && + check_push_result $the_commit heads/master && + check_push_result $the_first_commit heads/t/master + +' + test_expect_success 'push with weak ambiguity (1)' ' mk_test heads/master remotes/origin/master && @@ -244,12 +254,28 @@ test_expect_success 'push with colon-less refspec (4)' ' ' +test_expect_success 'push with HEAD' ' + + mk_test heads/master && + git checkout master && + git push testrepo HEAD && + check_push_result $the_commit heads/master + +' + +test_expect_success 'push with HEAD nonexisting at remote' ' + + mk_test heads/master && + git checkout -b local master && + git push testrepo HEAD && + check_push_result $the_commit heads/local +' + test_expect_success 'push with dry-run' ' mk_test heads/master && - cd testrepo && - old_commit=$(git show-ref -s --verify refs/heads/master) && - cd .. && + (cd testrepo && + old_commit=$(git show-ref -s --verify refs/heads/master)) && git push --dry-run testrepo && check_push_result $old_commit heads/master ' @@ -257,28 +283,40 @@ test_expect_success 'push with dry-run' ' test_expect_success 'push updates local refs' ' rm -rf parent child && - mkdir parent && cd parent && git init && - echo one >foo && git add foo && git commit -m one && - cd .. && - git clone parent child && cd child && + mkdir parent && + (cd parent && git init && + echo one >foo && git add foo && git commit -m one) && + git clone parent child && + (cd child && echo two >foo && git commit -a -m two && git push && - test $(git rev-parse master) = $(git rev-parse remotes/origin/master) + test $(git rev-parse master) = $(git rev-parse remotes/origin/master)) ' test_expect_success 'push does not update local refs on failure' ' rm -rf parent child && - mkdir parent && cd parent && git init && + mkdir parent && + (cd parent && git init && echo one >foo && git add foo && git commit -m one && echo exit 1 >.git/hooks/pre-receive && - chmod +x .git/hooks/pre-receive && - cd .. && - git clone parent child && cd child && - echo two >foo && git commit -a -m two || exit 1 - git push && exit 1 - test $(git rev-parse master) != $(git rev-parse remotes/origin/master) + chmod +x .git/hooks/pre-receive) && + git clone parent child && + (cd child && + echo two >foo && git commit -a -m two && + ! git push && + test $(git rev-parse master) != \ + $(git rev-parse remotes/origin/master)) + +' + +test_expect_success 'allow deleting an invalid remote ref' ' + + pwd && + rm -f testrepo/.git/objects/??/* && + git push testrepo :refs/heads/master && + (cd testrepo && ! git rev-parse --verify refs/heads/master) ' diff --git a/t/t5517-push-mirror.sh b/t/t5517-push-mirror.sh new file mode 100755 index 0000000000..ed3fec192a --- /dev/null +++ b/t/t5517-push-mirror.sh @@ -0,0 +1,228 @@ +#!/bin/sh + +test_description='pushing to a mirror repository' + +. ./test-lib.sh + +D=`pwd` + +invert () { + if "$@"; then + return 1 + else + return 0 + fi +} + +mk_repo_pair () { + rm -rf master mirror && + mkdir mirror && + ( + cd mirror && + git init + ) && + mkdir master && + ( + cd master && + git init && + git config remote.up.url ../mirror + ) +} + + +# BRANCH tests +test_expect_success 'push mirror creates new branches' ' + + mk_repo_pair && + ( + cd master && + echo one >foo && git add foo && git commit -m one && + git push --mirror up + ) && + master_master=$(cd master && git show-ref -s --verify refs/heads/master) && + mirror_master=$(cd mirror && git show-ref -s --verify refs/heads/master) && + test "$master_master" = "$mirror_master" + +' + +test_expect_success 'push mirror updates existing branches' ' + + mk_repo_pair && + ( + cd master && + echo one >foo && git add foo && git commit -m one && + git push --mirror up && + echo two >foo && git add foo && git commit -m two && + git push --mirror up + ) && + master_master=$(cd master && git show-ref -s --verify refs/heads/master) && + mirror_master=$(cd mirror && git show-ref -s --verify refs/heads/master) && + test "$master_master" = "$mirror_master" + +' + +test_expect_success 'push mirror force updates existing branches' ' + + mk_repo_pair && + ( + cd master && + echo one >foo && git add foo && git commit -m one && + git push --mirror up && + echo two >foo && git add foo && git commit -m two && + git push --mirror up && + git reset --hard HEAD^ + git push --mirror up + ) && + master_master=$(cd master && git show-ref -s --verify refs/heads/master) && + mirror_master=$(cd mirror && git show-ref -s --verify refs/heads/master) && + test "$master_master" = "$mirror_master" + +' + +test_expect_success 'push mirror removes branches' ' + + mk_repo_pair && + ( + cd master && + echo one >foo && git add foo && git commit -m one && + git branch remove master && + git push --mirror up && + git branch -D remove + git push --mirror up + ) && + ( + cd mirror && + invert git show-ref -s --verify refs/heads/remove + ) + +' + +test_expect_success 'push mirror adds, updates and removes branches together' ' + + mk_repo_pair && + ( + cd master && + echo one >foo && git add foo && git commit -m one && + git branch remove master && + git push --mirror up && + git branch -D remove && + git branch add master && + echo two >foo && git add foo && git commit -m two && + git push --mirror up + ) && + master_master=$(cd master && git show-ref -s --verify refs/heads/master) && + master_add=$(cd master && git show-ref -s --verify refs/heads/add) && + mirror_master=$(cd mirror && git show-ref -s --verify refs/heads/master) && + mirror_add=$(cd mirror && git show-ref -s --verify refs/heads/add) && + test "$master_master" = "$mirror_master" && + test "$master_add" = "$mirror_add" && + ( + cd mirror && + invert git show-ref -s --verify refs/heads/remove + ) + +' + + +# TAG tests +test_expect_success 'push mirror creates new tags' ' + + mk_repo_pair && + ( + cd master && + echo one >foo && git add foo && git commit -m one && + git tag -f tmaster master && + git push --mirror up + ) && + master_master=$(cd master && git show-ref -s --verify refs/tags/tmaster) && + mirror_master=$(cd mirror && git show-ref -s --verify refs/tags/tmaster) && + test "$master_master" = "$mirror_master" + +' + +test_expect_success 'push mirror updates existing tags' ' + + mk_repo_pair && + ( + cd master && + echo one >foo && git add foo && git commit -m one && + git tag -f tmaster master && + git push --mirror up && + echo two >foo && git add foo && git commit -m two && + git tag -f tmaster master && + git push --mirror up + ) && + master_master=$(cd master && git show-ref -s --verify refs/tags/tmaster) && + mirror_master=$(cd mirror && git show-ref -s --verify refs/tags/tmaster) && + test "$master_master" = "$mirror_master" + +' + +test_expect_success 'push mirror force updates existing tags' ' + + mk_repo_pair && + ( + cd master && + echo one >foo && git add foo && git commit -m one && + git tag -f tmaster master && + git push --mirror up && + echo two >foo && git add foo && git commit -m two && + git tag -f tmaster master && + git push --mirror up && + git reset --hard HEAD^ + git tag -f tmaster master && + git push --mirror up + ) && + master_master=$(cd master && git show-ref -s --verify refs/tags/tmaster) && + mirror_master=$(cd mirror && git show-ref -s --verify refs/tags/tmaster) && + test "$master_master" = "$mirror_master" + +' + +test_expect_success 'push mirror removes tags' ' + + mk_repo_pair && + ( + cd master && + echo one >foo && git add foo && git commit -m one && + git tag -f tremove master && + git push --mirror up && + git tag -d tremove + git push --mirror up + ) && + ( + cd mirror && + invert git show-ref -s --verify refs/tags/tremove + ) + +' + +test_expect_success 'push mirror adds, updates and removes tags together' ' + + mk_repo_pair && + ( + cd master && + echo one >foo && git add foo && git commit -m one && + git tag -f tmaster master && + git tag -f tremove master && + git push --mirror up && + git tag -d tremove && + git tag tadd master && + echo two >foo && git add foo && git commit -m two && + git tag -f tmaster master && + git push --mirror up + ) && + master_master=$(cd master && git show-ref -s --verify refs/tags/tmaster) && + master_add=$(cd master && git show-ref -s --verify refs/tags/tadd) && + mirror_master=$(cd mirror && git show-ref -s --verify refs/tags/tmaster) && + mirror_add=$(cd mirror && git show-ref -s --verify refs/tags/tadd) && + test "$master_master" = "$mirror_master" && + test "$master_add" = "$mirror_add" && + ( + cd mirror && + invert git show-ref -s --verify refs/tags/tremove + ) + +' + +test_done diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh index 93eaf2c154..52b3a0c6dd 100755 --- a/t/t5520-pull.sh +++ b/t/t5520-pull.sh @@ -53,4 +53,26 @@ test_expect_success 'the default remote . should not break explicit pull' ' test `cat file` = modified ' +test_expect_success '--rebase' ' + git branch to-rebase && + echo modified again > file && + git commit -m file file && + git checkout to-rebase && + echo new > file2 && + git add file2 && + git commit -m "new file" && + git tag before-rebase && + git pull --rebase . copy && + test $(git rev-parse HEAD^) = $(git rev-parse copy) && + test new = $(git show HEAD:file2) +' + +test_expect_success 'branch.to-rebase.rebase' ' + git reset --hard before-rebase && + git config branch.to-rebase.rebase 1 && + git pull . copy && + test $(git rev-parse HEAD^) = $(git rev-parse copy) && + test new = $(git show HEAD:file2) +' + test_done diff --git a/t/t5530-upload-pack-error.sh b/t/t5530-upload-pack-error.sh new file mode 100755 index 0000000000..cc8949e3ef --- /dev/null +++ b/t/t5530-upload-pack-error.sh @@ -0,0 +1,75 @@ +#!/bin/sh + +test_description='errors in upload-pack' + +. ./test-lib.sh + +D=`pwd` + +corrupt_repo () { + object_sha1=$(git rev-parse "$1") && + ob=$(expr "$object_sha1" : "\(..\)") && + ject=$(expr "$object_sha1" : "..\(..*\)") && + rm -f ".git/objects/$ob/$ject" +} + +test_expect_success 'setup and corrupt repository' ' + + echo file >file && + git add file && + git rev-parse :file && + git commit -a -m original && + test_tick && + echo changed >file && + git commit -a -m changed && + corrupt_repo HEAD:file + +' + +test_expect_failure 'fsck fails' ' + + git fsck +' + +test_expect_success 'upload-pack fails due to error in pack-objects' ' + + ! echo "0032want $(git rev-parse HEAD) +00000009done +0000" | git-upload-pack . > /dev/null 2> output.err && + grep "pack-objects died" output.err +' + +test_expect_success 'corrupt repo differently' ' + + git hash-object -w file && + corrupt_repo HEAD^^{tree} + +' + +test_expect_failure 'fsck fails' ' + + git fsck +' +test_expect_success 'upload-pack fails due to error in rev-list' ' + + ! echo "0032want $(git rev-parse HEAD) +00000009done +0000" | git-upload-pack . > /dev/null 2> output.err && + grep "waitpid (async) failed" output.err +' + +test_expect_success 'create empty repository' ' + + mkdir foo && + cd foo && + git init + +' + +test_expect_failure 'fetch fails' ' + + git fetch .. master + +' + +test_done diff --git a/t/t6008-rev-list-submodule.sh b/t/t6008-rev-list-submodule.sh new file mode 100755 index 0000000000..88e96fb91b --- /dev/null +++ b/t/t6008-rev-list-submodule.sh @@ -0,0 +1,42 @@ +#!/bin/sh +# +# Copyright (c) 2007 Johannes E. Schindelin +# + +test_description='git rev-list involving submodules that this repo has' + +. ./test-lib.sh + +test_expect_success 'setup' ' + : > file && + git add file && + test_tick && + git commit -m initial && + echo 1 > file && + test_tick && + git commit -m second file && + echo 2 > file && + test_tick && + git commit -m third file && + + rm .git/index && + + : > super-file && + git add super-file && + git submodule add . sub && + git symbolic-ref HEAD refs/heads/super && + test_tick && + git commit -m super-initial && + echo 1 > super-file && + test_tick && + git commit -m super-first super-file && + echo 2 > super-file && + test_tick && + git commit -m super-second super-file +' + +test_expect_success "Ilari's test" ' + git rev-list --objects super master ^super^ +' + +test_done diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh index 53956c08e2..2ba4b00e52 100755 --- a/t/t6030-bisect-porcelain.sh +++ b/t/t6030-bisect-porcelain.sh @@ -71,6 +71,43 @@ test_expect_success 'bisect start with one bad and good' ' git bisect next ' +test_expect_success 'bisect reset: back in the master branch' ' + git bisect reset && + echo "* master" > branch.expect && + git branch > branch.output && + cmp branch.expect branch.output +' + +test_expect_success 'bisect reset: back in another branch' ' + git checkout -b other && + git bisect start && + git bisect good $HASH1 && + git bisect bad $HASH3 && + git bisect reset && + echo " master" > branch.expect && + echo "* other" >> branch.expect && + git branch > branch.output && + cmp branch.expect branch.output +' + +test_expect_success 'bisect reset when not bisecting' ' + git bisect reset && + git branch > branch.output && + cmp branch.expect branch.output +' + +test_expect_success 'bisect reset removes packed refs' ' + git bisect reset && + git bisect start && + git bisect good $HASH1 && + git bisect bad $HASH3 && + git pack-refs --all --prune && + git bisect next && + git bisect reset && + test -z "$(git for-each-ref "refs/bisect/*")" && + test -z "$(git for-each-ref "refs/heads/bisect")" +' + # $HASH1 is good, $HASH4 is bad, we skip $HASH3 # but $HASH2 is bad, # so we should find $HASH2 as the first bad commit @@ -167,7 +204,7 @@ test_expect_success 'bisect skip: add line and then a new test' ' git bisect skip && git bisect good > my_bisect_log.txt && grep "$HASH5 is first bad commit" my_bisect_log.txt && - git bisect log > log_to_replay.txt + git bisect log > log_to_replay.txt && git bisect reset ' diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh index d0809eb651..8a23aaf21b 100755 --- a/t/t6300-for-each-ref.sh +++ b/t/t6300-for-each-ref.sh @@ -148,4 +148,65 @@ test_expect_success 'Check format "rfc2822" date fields output' ' git diff expected actual ' +cat >expected <<\EOF +refs/heads/master +refs/tags/testtag +EOF + +test_expect_success 'Verify ascending sort' ' + git-for-each-ref --format="%(refname)" --sort=refname >actual && + git diff expected actual +' + + +cat >expected <<\EOF +refs/tags/testtag +refs/heads/master +EOF + +test_expect_success 'Verify descending sort' ' + git-for-each-ref --format="%(refname)" --sort=-refname >actual && + git diff expected actual +' + +cat >expected <<\EOF +'refs/heads/master' +'refs/tags/testtag' +EOF + +test_expect_success 'Quoting style: shell' ' + git for-each-ref --shell --format="%(refname)" >actual && + git diff expected actual +' + +test_expect_success 'Quoting style: perl' ' + git for-each-ref --perl --format="%(refname)" >actual && + git diff expected actual +' + +test_expect_success 'Quoting style: python' ' + git for-each-ref --python --format="%(refname)" >actual && + git diff expected actual +' + +cat >expected <<\EOF +"refs/heads/master" +"refs/tags/testtag" +EOF + +test_expect_success 'Quoting style: tcl' ' + git for-each-ref --tcl --format="%(refname)" >actual && + git diff expected actual +' + +for i in "--perl --shell" "-s --python" "--python --tcl" "--tcl --perl"; do + test_expect_success "more than one quoting style: $i" " + git for-each-ref $i 2>&1 | (read line && + case \$line in + \"error: more than one quoting style\"*) : happy;; + *) false + esac) + " +done + test_done diff --git a/t/t7003-filter-branch.sh b/t/t7003-filter-branch.sh index 2089351f7d..5f60b22d87 100755 --- a/t/t7003-filter-branch.sh +++ b/t/t7003-filter-branch.sh @@ -114,7 +114,7 @@ test_expect_success 'use index-filter to move into a subdirectory' ' test_expect_success 'stops when msg filter fails' ' old=$(git rev-parse HEAD) && - ! git-filter-branch -f --msg-filter false && + ! git-filter-branch -f --msg-filter false HEAD && test $old = $(git rev-parse HEAD) && rm -rf .git-rewrite ' diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh index 0d07bc39c7..c7130c4dcc 100755 --- a/t/t7004-tag.sh +++ b/t/t7004-tag.sh @@ -339,20 +339,14 @@ test_expect_success \ ' test_expect_success \ - 'trying to create tags giving many -m or -F options should fail' ' + 'trying to create tags giving both -m or -F options should fail' ' echo "message file 1" >msgfile1 && echo "message file 2" >msgfile2 && ! tag_exists msgtag && - ! git-tag -m "message 1" -m "message 2" msgtag && - ! tag_exists msgtag && - ! git-tag -F msgfile1 -F msgfile2 msgtag && - ! tag_exists msgtag && ! git-tag -m "message 1" -F msgfile1 msgtag && ! tag_exists msgtag && ! git-tag -F msgfile1 -m "message 1" msgtag && ! tag_exists msgtag && - ! git-tag -F msgfile1 -m "message 1" -F msgfile2 msgtag && - ! tag_exists msgtag && ! git-tag -m "message 1" -F msgfile1 -m "message 2" msgtag && ! tag_exists msgtag ' @@ -673,6 +667,22 @@ test_expect_success 'creating a signed tag with -F - should succeed' ' git diff expect actual ' +cat >fakeeditor <<'EOF' +#!/bin/sh +test -n "$1" && exec >"$1" +echo A signed tag message +echo from a fake editor. +EOF +chmod +x fakeeditor +get_tag_header implied-annotate $commit commit $time >expect +./fakeeditor >>expect +echo '-----BEGIN PGP SIGNATURE-----' >>expect +test_expect_success '-s implies annotated tag' ' + GIT_EDITOR=./fakeeditor git-tag -s implied-annotate && + get_tag_msg implied-annotate >actual && + git diff expect actual +' + test_expect_success \ 'trying to create a signed tag with non-existing -F file should fail' ' ! test -f nonexistingfile && @@ -1004,4 +1014,30 @@ test_expect_failure \ 'verify signed tag fails when public key is not present' \ 'git-tag -v signed-tag' +test_expect_failure \ + 'git-tag -a fails if tag annotation is empty' ' + GIT_EDITOR=cat git tag -a initial-comment +' + +test_expect_success \ + 'message in editor has initial comment' ' + GIT_EDITOR=cat git tag -a initial-comment > actual + # check the first line --- should be empty + first=$(sed -e 1q <actual) && + test -z "$first" && + # remove commented lines from the remainder -- should be empty + rest=$(sed -e 1d -e '/^#/d' <actual) && + test -z "$rest" +' + +get_tag_header reuse $commit commit $time >expect +echo "An annotation to be reused" >> expect +test_expect_success \ + 'overwriting an annoted tag should use its previous body' ' + git tag -a -m "An annotation to be reused" reuse && + GIT_EDITOR=true git tag -f -a reuse && + get_tag_msg reuse >actual && + git diff expect actual +' + test_done diff --git a/t/t7005-editor.sh b/t/t7005-editor.sh index 01cc0c02b1..44228b5ac1 100755 --- a/t/t7005-editor.sh +++ b/t/t7005-editor.sh @@ -15,7 +15,6 @@ do done unset vi mv e-vi.sh vi -PATH=".:$PATH" unset EDITOR VISUAL GIT_EDITOR test_expect_success setup ' @@ -61,7 +60,7 @@ do ;; esac test_expect_success "Using $i" ' - git commit --amend && + git --exec-path=. commit --amend && git show -s --pretty=oneline | sed -e "s/^[0-9a-f]* //" >actual && diff actual expect @@ -83,7 +82,7 @@ do ;; esac test_expect_success "Using $i (override)" ' - git commit --amend && + git --exec-path=. commit --amend && git show -s --pretty=oneline | sed -e "s/^[0-9a-f]* //" >actual && diff actual expect diff --git a/t/t7102-reset.sh b/t/t7102-reset.sh index f64b1cbf75..e5c9f30c73 100755 --- a/t/t7102-reset.sh +++ b/t/t7102-reset.sh @@ -59,6 +59,15 @@ test_expect_success 'giving a non existing revision should fail' ' check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc ' +test_expect_success 'reset --soft with unmerged index should fail' ' + touch .git/MERGE_HEAD && + echo "100644 44c5b5884550c17758737edcced463447b91d42b 1 un" | + git update-index --index-info && + ! git reset --soft HEAD && + rm .git/MERGE_HEAD && + git rm --cached -- un +' + test_expect_success \ 'giving paths with options different than --mixed should fail' ' ! git reset --soft -- first && @@ -402,4 +411,21 @@ test_expect_success 'test resetting the index at give paths' ' ' +test_expect_success 'resetting an unmodified path is a no-op' ' + git reset --hard && + git reset -- file1 && + git diff-files --exit-code && + git diff-index --cached --exit-code HEAD +' + +cat > expect << EOF +file2: needs update +EOF + +test_expect_success '--mixed refreshes the index' ' + echo 123 >> file2 && + git reset --mixed HEAD > output && + git diff --exit-code expect output +' + test_done diff --git a/t/t7201-co.sh b/t/t7201-co.sh index ed2e9ee3c6..55558aba8b 100755 --- a/t/t7201-co.sh +++ b/t/t7201-co.sh @@ -77,7 +77,7 @@ test_expect_success "checkout with dirty tree without -m" ' test_expect_success "checkout -m with dirty tree" ' git checkout -f master && - git clean && + git clean -f && fill 0 1 2 3 4 5 6 7 8 >one && git checkout -m side && @@ -99,7 +99,7 @@ test_expect_success "checkout -m with dirty tree" ' test_expect_success "checkout -m with dirty tree, renamed" ' - git checkout -f master && git clean && + git checkout -f master && git clean -f && fill 1 2 3 4 5 7 8 >one && if git checkout renamer @@ -121,7 +121,7 @@ test_expect_success "checkout -m with dirty tree, renamed" ' test_expect_success 'checkout -m with merge conflict' ' - git checkout -f master && git clean && + git checkout -f master && git clean -f && fill 1 T 3 4 5 6 S 8 >one && if git checkout renamer @@ -144,7 +144,7 @@ test_expect_success 'checkout -m with merge conflict' ' test_expect_success 'checkout to detach HEAD' ' - git checkout -f renamer && git clean && + git checkout -f renamer && git clean -f && git checkout renamer^ && H=$(git rev-parse --verify HEAD) && M=$(git show-ref -s --verify refs/heads/master) && @@ -160,7 +160,7 @@ test_expect_success 'checkout to detach HEAD' ' test_expect_success 'checkout to detach HEAD with branchname^' ' - git checkout -f master && git clean && + git checkout -f master && git clean -f && git checkout renamer^ && H=$(git rev-parse --verify HEAD) && M=$(git show-ref -s --verify refs/heads/master) && @@ -176,7 +176,7 @@ test_expect_success 'checkout to detach HEAD with branchname^' ' test_expect_success 'checkout to detach HEAD with HEAD^0' ' - git checkout -f master && git clean && + git checkout -f master && git clean -f && git checkout HEAD^0 && H=$(git rev-parse --verify HEAD) && M=$(git show-ref -s --verify refs/heads/master) && diff --git a/t/t7300-clean.sh b/t/t7300-clean.sh index eb0847afe9..dfd118878f 100755 --- a/t/t7300-clean.sh +++ b/t/t7300-clean.sh @@ -7,6 +7,8 @@ test_description='git-clean basic tests' . ./test-lib.sh +git config clean.requireForce no + test_expect_success 'setup' ' mkdir -p src && @@ -37,6 +39,107 @@ test_expect_success 'git-clean' ' ' +test_expect_success 'git-clean src/' ' + + mkdir -p build docs && + touch a.out src/part3.c docs/manual.txt obj.o build/lib.so && + git-clean src/ && + test -f Makefile && + test -f README && + test -f src/part1.c && + test -f src/part2.c && + test -f a.out && + test ! -f src/part3.c && + test -f docs/manual.txt && + test -f obj.o && + test -f build/lib.so + +' + +test_expect_success 'git-clean src/ src/' ' + + mkdir -p build docs && + touch a.out src/part3.c docs/manual.txt obj.o build/lib.so && + git-clean src/ src/ && + test -f Makefile && + test -f README && + test -f src/part1.c && + test -f src/part2.c && + test -f a.out && + test ! -f src/part3.c && + test -f docs/manual.txt && + test -f obj.o && + test -f build/lib.so + +' + +test_expect_success 'git-clean with prefix' ' + + mkdir -p build docs && + touch a.out src/part3.c docs/manual.txt obj.o build/lib.so && + (cd src/ && git-clean) && + test -f Makefile && + test -f README && + test -f src/part1.c && + test -f src/part2.c && + test -f a.out && + test ! -f src/part3.c && + test -f docs/manual.txt && + test -f obj.o && + test -f build/lib.so + +' +test_expect_success 'git-clean -d with prefix and path' ' + + mkdir -p build docs src/feature && + touch a.out src/part3.c src/feature/file.c docs/manual.txt obj.o build/lib.so && + (cd src/ && git-clean -d feature/) && + test -f Makefile && + test -f README && + test -f src/part1.c && + test -f src/part2.c && + test -f a.out && + test -f src/part3.c && + test ! -f src/feature/file.c && + test -f docs/manual.txt && + test -f obj.o && + test -f build/lib.so + +' + +test_expect_success 'git-clean symbolic link' ' + + mkdir -p build docs && + touch a.out src/part3.c docs/manual.txt obj.o build/lib.so && + ln -s docs/manual.txt src/part4.c + git-clean && + test -f Makefile && + test -f README && + test -f src/part1.c && + test -f src/part2.c && + test ! -f a.out && + test ! -f src/part3.c && + test ! -f src/part4.c && + test -f docs/manual.txt && + test -f obj.o && + test -f build/lib.so + +' + +test_expect_success 'git-clean with wildcard' ' + + touch a.clean b.clean other.c && + git-clean "*.clean" && + test -f Makefile && + test -f README && + test -f src/part1.c && + test -f src/part2.c && + test ! -f a.clean && + test ! -f b.clean && + test -f other.c + +' + test_expect_success 'git-clean -n' ' mkdir -p build docs && @@ -71,6 +174,24 @@ test_expect_success 'git-clean -d' ' ' +test_expect_success 'git-clean -d src/ examples/' ' + + mkdir -p build docs examples && + touch a.out src/part3.c docs/manual.txt obj.o build/lib.so examples/1.c && + git-clean -d src/ examples/ && + test -f Makefile && + test -f README && + test -f src/part1.c && + test -f src/part2.c && + test -f a.out && + test ! -f src/part3.c && + test ! -f examples/1.c && + test -f docs/manual.txt && + test -f obj.o && + test -f build/lib.so + +' + test_expect_success 'git-clean -x' ' mkdir -p build docs && @@ -139,6 +260,13 @@ test_expect_success 'git-clean -d -X' ' ' +test_expect_success 'clean.requireForce defaults to true' ' + + git config --unset clean.requireForce && + ! git-clean + +' + test_expect_success 'clean.requireForce' ' git config clean.requireForce true && @@ -177,4 +305,15 @@ test_expect_success 'clean.requireForce and -f' ' ' +test_expect_success 'core.excludesfile' ' + + echo excludes >excludes && + echo included >included && + git config core.excludesfile excludes && + output=$(git clean -n excludes included 2>&1) && + expr "$output" : ".*included" >/dev/null && + ! expr "$output" : ".*excludes" >/dev/null + +' + test_done diff --git a/t/t7500-commit.sh b/t/t7500-commit.sh index abbf54ba63..baed6ce96b 100755 --- a/t/t7500-commit.sh +++ b/t/t7500-commit.sh @@ -93,4 +93,49 @@ test_expect_success 'commit message from file should override template' ' commit_msg_is "standard input msg" ' +test_expect_success 'using alternate GIT_INDEX_FILE (1)' ' + + cp .git/index saved-index && + ( + echo some new content >file && + GIT_INDEX_FILE=.git/another_index && + export GIT_INDEX_FILE && + git add file && + git commit -m "commit using another index" && + git diff-index --exit-code HEAD && + git diff-files --exit-code + ) && + cmp .git/index saved-index >/dev/null + +' + +test_expect_success 'using alternate GIT_INDEX_FILE (2)' ' + + cp .git/index saved-index && + ( + rm -f .git/no-such-index && + GIT_INDEX_FILE=.git/no-such-index && + export GIT_INDEX_FILE && + git commit -m "commit using nonexistent index" && + test -z "$(git ls-files)" && + test -z "$(git ls-tree HEAD)" + + ) && + cmp .git/index saved-index >/dev/null +' + +cat > expect << EOF +zort + +Signed-off-by: C O Mitter <committer@example.com> +EOF + +test_expect_success '--signoff' ' + echo "yet another content *narf*" >> foo && + echo "zort" | + GIT_EDITOR=../t7500/add-content git commit -s -F - foo && + git cat-file commit HEAD | sed "1,/^$/d" > output && + diff expect output +' + test_done diff --git a/t/t7501-commit.sh b/t/t7501-commit.sh index b151b51a34..05aa97d6f3 100644..100755 --- a/t/t7501-commit.sh +++ b/t/t7501-commit.sh @@ -4,7 +4,7 @@ # # FIXME: Test the various index usages, -i and -o, test reflog, -# signoff, hooks +# signoff test_description='git-commit' . ./test-lib.sh @@ -34,6 +34,16 @@ test_expect_failure \ "git-commit -C HEAD -m illegal" test_expect_failure \ + "using paths with -a" \ + "echo King of the bongo >file && + git-commit -m foo -a file" + +test_expect_failure \ + "using paths with --interactive" \ + "echo bong-o-bong >file && + echo 7 | git-commit -m foo --interactive file" + +test_expect_failure \ "using invalid commit with -C" \ "git-commit -C bogus" @@ -69,7 +79,8 @@ test_expect_success \ cat >editor <<\EOF #!/bin/sh -sed -i -e "s/a file/an amend commit/g" $1 +sed -e "s/a file/an amend commit/g" < $1 > $1- +mv $1- $1 EOF chmod 755 editor @@ -88,7 +99,8 @@ test_expect_success \ cat >editor <<\EOF #!/bin/sh -sed -i -e "s/amend/older/g" $1 +sed -e "s/amend/older/g" < $1 > $1- +mv $1- $1 EOF chmod 755 editor @@ -163,4 +175,139 @@ test_expect_success 'partial commit that involves removal (3)' ' ' +author="The Real Author <someguy@his.email.org>" +test_expect_success 'amend commit to fix author' ' + + oldtick=$GIT_AUTHOR_DATE && + test_tick && + git reset --hard && + git cat-file -p HEAD | + sed -e "s/author.*/author $author $oldtick/" \ + -e "s/^\(committer.*> \).*$/\1$GIT_COMMITTER_DATE/" > \ + expected && + git commit --amend --author="$author" && + git cat-file -p HEAD > current && + diff expected current + +' + +test_expect_success 'sign off (1)' ' + + echo 1 >positive && + git add positive && + git commit -s -m "thank you" && + git cat-file commit HEAD | sed -e "1,/^\$/d" >actual && + ( + echo thank you + echo + git var GIT_COMMITTER_IDENT | + sed -e "s/>.*/>/" -e "s/^/Signed-off-by: /" + ) >expected && + diff -u expected actual + +' + +test_expect_success 'sign off (2)' ' + + echo 2 >positive && + git add positive && + existing="Signed-off-by: Watch This <watchthis@example.com>" && + git commit -s -m "thank you + +$existing" && + git cat-file commit HEAD | sed -e "1,/^\$/d" >actual && + ( + echo thank you + echo + echo $existing + git var GIT_COMMITTER_IDENT | + sed -e "s/>.*/>/" -e "s/^/Signed-off-by: /" + ) >expected && + diff -u expected actual + +' + +test_expect_success 'multiple -m' ' + + >negative && + git add negative && + git commit -m "one" -m "two" -m "three" && + git cat-file commit HEAD | sed -e "1,/^\$/d" >actual && + ( + echo one + echo + echo two + echo + echo three + ) >expected && + diff -u expected actual + +' + +author="The Real Author <someguy@his.email.org>" +test_expect_success 'amend commit to fix author' ' + + oldtick=$GIT_AUTHOR_DATE && + test_tick && + git reset --hard && + git cat-file -p HEAD | + sed -e "s/author.*/author $author $oldtick/" \ + -e "s/^\(committer.*> \).*$/\1$GIT_COMMITTER_DATE/" > \ + expected && + git commit --amend --author="$author" && + git cat-file -p HEAD > current && + diff expected current + +' + +test_expect_success 'git commit <file> with dirty index' ' + echo tacocat > elif && + echo tehlulz > chz && + git add chz && + git commit elif -m "tacocat is a palindrome" && + git show --stat | grep elif && + git diff --cached | grep chz +' + +test_expect_success 'same tree (single parent)' ' + + git reset --hard + + if git commit -m empty + then + echo oops -- should have complained + false + else + : happy + fi + +' + +test_expect_success 'same tree (single parent) --allow-empty' ' + + git commit --allow-empty -m "forced empty" && + git cat-file commit HEAD | grep forced + +' + +test_expect_success 'same tree (merge and amend merge)' ' + + git checkout -b side HEAD^ && + echo zero >zero && + git add zero && + git commit -m "add zero" && + git checkout master && + + git merge -s ours side -m "empty ok" && + git diff HEAD^ HEAD >actual && + : >expected && + diff -u expected actual && + + git commit --amend -m "empty really ok" && + git diff HEAD^ HEAD >actual && + : >expected && + diff -u expected actual + +' + test_done diff --git a/t/t7502-commit.sh b/t/t7502-commit.sh new file mode 100755 index 0000000000..21ac785e3d --- /dev/null +++ b/t/t7502-commit.sh @@ -0,0 +1,92 @@ +#!/bin/sh + +test_description='git commit porcelain-ish' + +. ./test-lib.sh + +test_expect_success 'the basics' ' + + echo doing partial >"commit is" && + mkdir not && + echo very much encouraged but we should >not/forbid && + git add "commit is" not && + echo update added "commit is" file >"commit is" && + echo also update another >not/forbid && + test_tick && + git commit -a -m "initial with -a" && + + git cat-file blob HEAD:"commit is" >current.1 && + git cat-file blob HEAD:not/forbid >current.2 && + + cmp current.1 "commit is" && + cmp current.2 not/forbid + +' + +test_expect_success 'partial' ' + + echo another >"commit is" && + echo another >not/forbid && + test_tick && + git commit -m "partial commit to handle a file" "commit is" && + + changed=$(git diff-tree --name-only HEAD^ HEAD) && + test "$changed" = "commit is" + +' + +test_expect_success 'partial modification in a subdirecotry' ' + + test_tick && + git commit -m "partial commit to subdirectory" not && + + changed=$(git diff-tree -r --name-only HEAD^ HEAD) && + test "$changed" = "not/forbid" + +' + +test_expect_success 'partial removal' ' + + git rm not/forbid && + git commit -m "partial commit to remove not/forbid" not && + + changed=$(git diff-tree -r --name-only HEAD^ HEAD) && + test "$changed" = "not/forbid" && + remain=$(git ls-tree -r --name-only HEAD) && + test "$remain" = "commit is" + +' + +test_expect_success 'sign off' ' + + >positive && + git add positive && + git commit -s -m "thank you" && + actual=$(git cat-file commit HEAD | sed -ne "s/Signed-off-by: //p") && + expected=$(git var GIT_COMMITTER_IDENT | sed -e "s/>.*/>/") && + test "z$actual" = "z$expected" + +' + +test_expect_success 'multiple -m' ' + + >negative && + git add negative && + git commit -m "one" -m "two" -m "three" && + actual=$(git cat-file commit HEAD | sed -e "1,/^\$/d") && + expected=$(echo one; echo; echo two; echo; echo three) && + test "z$actual" = "z$expected" + +' + +test_expect_success 'verbose' ' + + echo minus >negative && + git add negative && + git status -v | sed -ne "/^diff --git /p" >actual && + echo "diff --git a/negative b/negative" >expect && + diff -u expect actual + +' + +test_done diff --git a/t/t7502-status.sh b/t/t7502-status.sh new file mode 100755 index 0000000000..9ce50cade8 --- /dev/null +++ b/t/t7502-status.sh @@ -0,0 +1,122 @@ +#!/bin/sh +# +# Copyright (c) 2007 Johannes E. Schindelin +# + +test_description='git-status' + +. ./test-lib.sh + +test_expect_success 'setup' ' + : > tracked && + : > modified && + mkdir dir1 && + : > dir1/tracked && + : > dir1/modified && + mkdir dir2 && + : > dir1/tracked && + : > dir1/modified && + git add . && + test_tick && + git commit -m initial && + : > untracked && + : > dir1/untracked && + : > dir2/untracked && + echo 1 > dir1/modified && + echo 2 > dir2/modified && + echo 3 > dir2/added && + git add dir2/added +' + +cat > expect << \EOF +# On branch master +# Changes to be committed: +# (use "git reset HEAD <file>..." to unstage) +# +# new file: dir2/added +# +# Changed but not updated: +# (use "git add <file>..." to update what will be committed) +# +# modified: dir1/modified +# +# Untracked files: +# (use "git add <file>..." to include in what will be committed) +# +# dir1/untracked +# dir2/modified +# dir2/untracked +# expect +# output +# untracked +EOF + +test_expect_success 'status' ' + + git status > output && + git diff expect output + +' + +cat > expect << \EOF +# On branch master +# Changes to be committed: +# (use "git reset HEAD <file>..." to unstage) +# +# new file: ../dir2/added +# +# Changed but not updated: +# (use "git add <file>..." to update what will be committed) +# +# modified: modified +# +# Untracked files: +# (use "git add <file>..." to include in what will be committed) +# +# untracked +# ../dir2/modified +# ../dir2/untracked +# ../expect +# ../output +# ../untracked +EOF + +test_expect_success 'status with relative paths' ' + + (cd dir1 && git status) > output && + git diff expect output + +' + +cat > expect << \EOF +# On branch master +# Changes to be committed: +# (use "git reset HEAD <file>..." to unstage) +# +# new file: dir2/added +# +# Changed but not updated: +# (use "git add <file>..." to update what will be committed) +# +# modified: dir1/modified +# +# Untracked files: +# (use "git add <file>..." to include in what will be committed) +# +# dir1/untracked +# dir2/modified +# dir2/untracked +# expect +# output +# untracked +EOF + +test_expect_success 'status without relative paths' ' + + git config status.relativePaths false + (cd dir1 && git status) > output && + git diff expect output + +' + +test_done diff --git a/t/t7503-pre-commit-hook.sh b/t/t7503-pre-commit-hook.sh new file mode 100755 index 0000000000..c8097a72d9 --- /dev/null +++ b/t/t7503-pre-commit-hook.sh @@ -0,0 +1,64 @@ +#!/bin/sh + +test_description='pre-commit hook' + +. ./test-lib.sh + +test_expect_success "with no hook" \ + "echo 'foo' > file && + git add file && + git commit -m 'first'" + +test_expect_success "--no-verify with no hook" \ + "echo 'bar' > file && + git add file && + git commit --no-verify -m 'bar'" + +# now install hook that always succeeds +HOOKDIR="$(git rev-parse --git-dir)/hooks" +HOOK="$HOOKDIR/pre-commit" +mkdir -p "$HOOKDIR" +cat > "$HOOK" <<EOF +#!/bin/sh +exit 0 +EOF +chmod +x "$HOOK" + +test_expect_success "with succeeding hook" \ + "echo 'more' >> file && + git add file && + git commit -m 'more'" + +test_expect_success "--no-verify with succeeding hook" \ + "echo 'even more' >> file && + git add file && + git commit --no-verify -m 'even more'" + +# now a hook that fails +cat > "$HOOK" <<EOF +#!/bin/sh +exit 1 +EOF + +test_expect_failure "with failing hook" \ + "echo 'another' >> file && + git add file && + git commit -m 'another'" + +test_expect_success "--no-verify with failing hook" \ + "echo 'stuff' >> file && + git add file && + git commit --no-verify -m 'stuff'" + +chmod -x "$HOOK" +test_expect_success "with non-executable hook" \ + "echo 'content' >> file && + git add file && + git commit -m 'content'" + +test_expect_success "--no-verify with non-executable hook" \ + "echo 'more content' >> file && + git add file && + git commit --no-verify -m 'more content'" + +test_done diff --git a/t/t7504-commit-msg-hook.sh b/t/t7504-commit-msg-hook.sh new file mode 100755 index 0000000000..17aad7c10d --- /dev/null +++ b/t/t7504-commit-msg-hook.sh @@ -0,0 +1,88 @@ +#!/bin/sh + +test_description='commit-msg hook' + +. ./test-lib.sh + +test_expect_success "with no hook" \ + "echo 'foo' > file && + git add file && + git commit -m 'first'" + +test_expect_success "--no-verify with no hook" \ + "echo 'bar' > file && + git add file && + git commit --no-verify -m 'bar'" + +# now install hook that always succeeds +HOOKDIR="$(git rev-parse --git-dir)/hooks" +HOOK="$HOOKDIR/commit-msg" +mkdir -p "$HOOKDIR" +cat > "$HOOK" <<EOF +#!/bin/sh +exit 0 +EOF +chmod +x "$HOOK" + +test_expect_success "with succeeding hook" \ + "echo 'more' >> file && + git add file && + git commit -m 'more'" + +test_expect_success "--no-verify with succeeding hook" \ + "echo 'even more' >> file && + git add file && + git commit --no-verify -m 'even more'" + +# now a hook that fails +cat > "$HOOK" <<EOF +#!/bin/sh +exit 1 +EOF + +test_expect_failure "with failing hook" \ + "echo 'another' >> file && + git add file && + git commit -m 'another'" + +test_expect_success "--no-verify with failing hook" \ + "echo 'stuff' >> file && + git add file && + git commit --no-verify -m 'stuff'" + +chmod -x "$HOOK" +test_expect_success "with non-executable hook" \ + "echo 'content' >> file && + git add file && + git commit -m 'content'" + +test_expect_success "--no-verify with non-executable hook" \ + "echo 'more content' >> file && + git add file && + git commit --no-verify -m 'more content'" + +# now a hook that edits the commit message +cat > "$HOOK" <<'EOF' +#!/bin/sh +echo "new message" > "$1" +exit 0 +EOF +chmod +x "$HOOK" + +commit_msg_is () { + test "`git log --pretty=format:%s%b -1`" = "$1" +} + +test_expect_success "hook edits commit message" \ + "echo 'additional' >> file && + git add file && + git commit -m 'additional' && + commit_msg_is 'new message'" + +test_expect_success "hook doesn't edit commit message" \ + "echo 'plus' >> file && + git add file && + git commit --no-verify -m 'plus' && + commit_msg_is 'plus'" + +test_done diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh index 83f9470202..659f9c758f 100755 --- a/t/t9001-send-email.sh +++ b/t/t9001-send-email.sh @@ -41,4 +41,41 @@ test_expect_success \ 'Verify commandline' \ 'diff commandline expected' +cat >expected-show-all-headers <<\EOF +0001-Second.patch +(mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>' +Dry-OK. Log says: +Server: relay.example.com +MAIL FROM:<from@example.com> +RCPT TO:<to@example.com>,<cc@example.com>,<author@example.com>,<bcc@example.com> +From: Example <from@example.com> +To: to@example.com +Cc: cc@example.com, A <author@example.com> +Subject: [PATCH 1/1] Second. +Date: DATE-STRING +Message-Id: MESSAGE-ID-STRING +X-Mailer: X-MAILER-STRING +In-Reply-To: <unique-message-id@example.com> +References: <unique-message-id@example.com> + +Result: OK +EOF + +test_expect_success 'Show all headers' ' + git send-email \ + --dry-run \ + --from="Example <from@example.com>" \ + --to=to@example.com \ + --cc=cc@example.com \ + --bcc=bcc@example.com \ + --in-reply-to="<unique-message-id@example.com>" \ + --smtp-server relay.example.com \ + $patches | + sed -e "s/^\(Date:\).*/\1 DATE-STRING/" \ + -e "s/^\(Message-Id:\).*/\1 MESSAGE-ID-STRING/" \ + -e "s/^\(X-Mailer:\).*/\1 X-MAILER-STRING/" \ + >actual-show-all-headers && + diff -u expected-show-all-headers actual-show-all-headers +' + test_done diff --git a/t/t9101-git-svn-props.sh b/t/t9101-git-svn-props.sh index 3c83127a0e..d7a704754e 100755 --- a/t/t9101-git-svn-props.sh +++ b/t/t9101-git-svn-props.sh @@ -48,7 +48,7 @@ EOF printf "\r\n" > empty_crlf a_empty_crlf=`git-hash-object -w empty_crlf` - svn import -m 'import for git-svn' . "$svnrepo" >/dev/null + svn import --no-auto-props -m 'import for git-svn' . "$svnrepo" >/dev/null cd .. rm -rf import diff --git a/t/t9106-git-svn-dcommit-clobber-series.sh b/t/t9106-git-svn-dcommit-clobber-series.sh new file mode 100755 index 0000000000..745254665d --- /dev/null +++ b/t/t9106-git-svn-dcommit-clobber-series.sh @@ -0,0 +1,63 @@ +#!/bin/sh +# +# Copyright (c) 2007 Eric Wong +test_description='git-svn dcommit clobber series' +. ./lib-git-svn.sh + +test_expect_success 'initialize repo' " + mkdir import && + cd import && + awk 'BEGIN { for (i = 1; i < 64; i++) { print i } }' > file + svn import -m 'initial' . $svnrepo && + cd .. && + git svn init $svnrepo && + git svn fetch && + test -e file + " + +test_expect_success '(supposedly) non-conflicting change from SVN' " + test x\"\`sed -n -e 58p < file\`\" = x58 && + test x\"\`sed -n -e 61p < file\`\" = x61 && + svn co $svnrepo tmp && + cd tmp && + perl -i -p -e 's/^58\$/5588/' file && + perl -i -p -e 's/^61\$/6611/' file && + poke file && + test x\"\`sed -n -e 58p < file\`\" = x5588 && + test x\"\`sed -n -e 61p < file\`\" = x6611 && + svn commit -m '58 => 5588, 61 => 6611' && + cd .. + " + +test_expect_success 'some unrelated changes to git' " + echo hi > life && + git update-index --add life && + git commit -m hi-life && + echo bye >> life && + git commit -m bye-life life + " + +test_expect_success 'change file but in unrelated area' " + test x\"\`sed -n -e 4p < file\`\" = x4 && + test x\"\`sed -n -e 7p < file\`\" = x7 && + perl -i -p -e 's/^4\$/4444/' file && + perl -i -p -e 's/^7\$/7777/' file && + test x\"\`sed -n -e 4p < file\`\" = x4444 && + test x\"\`sed -n -e 7p < file\`\" = x7777 && + git commit -m '4 => 4444, 7 => 7777' file && + git svn dcommit && + svn up tmp && + cd tmp && + test x\"\`sed -n -e 4p < file\`\" = x4444 && + test x\"\`sed -n -e 7p < file\`\" = x7777 && + test x\"\`sed -n -e 58p < file\`\" = x5588 && + test x\"\`sed -n -e 61p < file\`\" = x6611 + " + +test_expect_failure 'attempt to dcommit with a dirty index' ' + echo foo >>file && + git add file && + git svn dcommit +' + +test_done diff --git a/t/t9114-git-svn-dcommit-merge.sh b/t/t9114-git-svn-dcommit-merge.sh index d6ca955081..225060b88b 100755 --- a/t/t9114-git-svn-dcommit-merge.sh +++ b/t/t9114-git-svn-dcommit-merge.sh @@ -86,4 +86,9 @@ test_expect_success 'verify post-merge ancestry' " git cat-file commit refs/heads/svn^ | grep '^friend$' " +test_expect_success 'verify merge commit message' " + git rev-list --pretty=raw -1 refs/heads/svn | \ + grep \" Merge branch 'merge' into svn\" + " + test_done diff --git a/t/t9116-git-svn-log.sh b/t/t9116-git-svn-log.sh index 0d4e6b3f04..902ed4145d 100755 --- a/t/t9116-git-svn-log.sh +++ b/t/t9116-git-svn-log.sh @@ -30,6 +30,12 @@ test_expect_success 'setup repository and import' " git reset --hard trunk && echo aye >> README && git commit -a -m aye && + git svn dcommit && + git reset --hard b && + echo spy >> README && + git commit -a -m spy && + echo try >> README && + git commit -a -m try && git svn dcommit " @@ -45,4 +51,78 @@ test_expect_success 'run log against a from trunk' " git svn log -r3 a | grep ^r3 " +printf 'r1 \nr2 \nr4 \n' > expected-range-r1-r2-r4 + +test_expect_success 'test ascending revision range' " + git reset --hard trunk && + git svn log -r 1:4 | grep '^r[0-9]' | cut -d'|' -f1 | diff -u expected-range-r1-r2-r4 - + " + +printf 'r4 \nr2 \nr1 \n' > expected-range-r4-r2-r1 + +test_expect_success 'test descending revision range' " + git reset --hard trunk && + git svn log -r 4:1 | grep '^r[0-9]' | cut -d'|' -f1 | diff -u expected-range-r4-r2-r1 - + " + +printf 'r1 \nr2 \n' > expected-range-r1-r2 + +test_expect_success 'test ascending revision range with unreachable revision' " + git reset --hard trunk && + git svn log -r 1:3 | grep '^r[0-9]' | cut -d'|' -f1 | diff -u expected-range-r1-r2 - + " + +printf 'r2 \nr1 \n' > expected-range-r2-r1 + +test_expect_success 'test descending revision range with unreachable revision' " + git reset --hard trunk && + git svn log -r 3:1 | grep '^r[0-9]' | cut -d'|' -f1 | diff -u expected-range-r2-r1 - + " + +printf 'r2 \n' > expected-range-r2 + +test_expect_success 'test ascending revision range with unreachable upper boundary revision and 1 commit' " + git reset --hard trunk && + git svn log -r 2:3 | grep '^r[0-9]' | cut -d'|' -f1 | diff -u expected-range-r2 - + " + +test_expect_success 'test descending revision range with unreachable upper boundary revision and 1 commit' " + git reset --hard trunk && + git svn log -r 3:2 | grep '^r[0-9]' | cut -d'|' -f1 | diff -u expected-range-r2 - + " + +printf 'r4 \n' > expected-range-r4 + +test_expect_success 'test ascending revision range with unreachable lower boundary revision and 1 commit' " + git reset --hard trunk && + git svn log -r 3:4 | grep '^r[0-9]' | cut -d'|' -f1 | diff -u expected-range-r4 - + " + +test_expect_success 'test descending revision range with unreachable lower boundary revision and 1 commit' " + git reset --hard trunk && + git svn log -r 4:3 | grep '^r[0-9]' | cut -d'|' -f1 | diff -u expected-range-r4 - + " + +printf -- '------------------------------------------------------------------------\n' > expected-separator + +test_expect_success 'test ascending revision range with unreachable boundary revisions and no commits' " + git reset --hard trunk && + git svn log -r 5:6 | diff -u expected-separator - + " + +test_expect_success 'test descending revision range with unreachable boundary revisions and no commits' " + git reset --hard trunk && + git svn log -r 6:5 | diff -u expected-separator - + " + +test_expect_success 'test ascending revision range with unreachable boundary revisions and 1 commit' " + git reset --hard trunk && + git svn log -r 3:5 | grep '^r[0-9]' | cut -d'|' -f1 | diff -u expected-range-r4 - + " + +test_expect_success 'test descending revision range with unreachable boundary revisions and 1 commit' " + git reset --hard trunk && + git svn log -r 5:3 | grep '^r[0-9]' | cut -d'|' -f1 | diff -u expected-range-r4 - + " + test_done diff --git a/t/t9117-git-svn-init-clone.sh b/t/t9117-git-svn-init-clone.sh new file mode 100755 index 0000000000..d482b407f2 --- /dev/null +++ b/t/t9117-git-svn-init-clone.sh @@ -0,0 +1,55 @@ +#!/bin/sh +# +# Copyright (c) 2007 Eric Wong +# + +test_description='git-svn init/clone tests' + +. ./lib-git-svn.sh + +# setup, run inside tmp so we don't have any conflicts with $svnrepo +set -e +rm -r .git +mkdir tmp +cd tmp + +test_expect_success 'setup svnrepo' " + mkdir project project/trunk project/branches project/tags && + echo foo > project/trunk/foo && + svn import -m '$test_description' project $svnrepo/project && + rm -rf project + " + +test_expect_success 'basic clone' " + test ! -d trunk && + git svn clone $svnrepo/project/trunk && + test -d trunk/.git/svn && + test -e trunk/foo && + rm -rf trunk + " + +test_expect_success 'clone to target directory' " + test ! -d target && + git svn clone $svnrepo/project/trunk target && + test -d target/.git/svn && + test -e target/foo && + rm -rf target + " + +test_expect_success 'clone with --stdlayout' " + test ! -d project && + git svn clone -s $svnrepo/project && + test -d project/.git/svn && + test -e project/foo && + rm -rf project + " + +test_expect_success 'clone to target directory with --stdlayout' " + test ! -d target && + git svn clone -s $svnrepo/project target && + test -d target/.git/svn && + test -e target/foo && + rm -rf target + " + +test_done diff --git a/t/t9118-git-svn-funky-branch-names.sh b/t/t9118-git-svn-funky-branch-names.sh new file mode 100755 index 0000000000..640bb066f3 --- /dev/null +++ b/t/t9118-git-svn-funky-branch-names.sh @@ -0,0 +1,40 @@ +#!/bin/sh +# +# Copyright (c) 2007 Eric Wong +# + +test_description='git-svn funky branch names' +. ./lib-git-svn.sh + +test_expect_success 'setup svnrepo' " + mkdir project project/trunk project/branches project/tags && + echo foo > project/trunk/foo && + svn import -m '$test_description' project \"$svnrepo/pr ject\" && + rm -rf project && + svn cp -m 'fun' \"$svnrepo/pr ject/trunk\" \ + \"$svnrepo/pr ject/branches/fun plugin\" && + svn cp -m 'more fun!' \"$svnrepo/pr ject/branches/fun plugin\" \ + \"$svnrepo/pr ject/branches/more fun plugin!\" && + start_httpd + " + +test_expect_success 'test clone with funky branch names' " + git svn clone -s \"$svnrepo/pr ject\" project && + cd project && + git rev-parse 'refs/remotes/fun%20plugin' && + git rev-parse 'refs/remotes/more%20fun%20plugin!' && + cd .. + " + +test_expect_success 'test dcommit to funky branch' " + cd project && + git reset --hard 'refs/remotes/more%20fun%20plugin!' && + echo hello >> foo && + git commit -m 'hello' -- foo && + git svn dcommit && + cd .. + " + +stop_httpd + +test_done diff --git a/t/t9119-git-svn-info.sh b/t/t9119-git-svn-info.sh new file mode 100755 index 0000000000..439bd93c88 --- /dev/null +++ b/t/t9119-git-svn-info.sh @@ -0,0 +1,368 @@ +#!/bin/sh +# +# Copyright (c) 2007 David D. Kilzer + +test_description='git-svn info' + +. ./lib-git-svn.sh + +ptouch() { + perl -w -e ' + use strict; + die "ptouch requires exactly 2 arguments" if @ARGV != 2; + die "$ARGV[0] does not exist" if ! -e $ARGV[0]; + my @s = stat $ARGV[0]; + utime $s[8], $s[9], $ARGV[1]; + ' "$1" "$2" +} + +test_expect_success 'setup repository and import' " + mkdir info && + cd info && + echo FIRST > A && + echo one > file && + ln -s file symlink-file && + mkdir directory && + touch directory/.placeholder && + ln -s directory symlink-directory && + svn import -m 'initial' . $svnrepo && + cd .. && + mkdir gitwc && + cd gitwc && + git-svn init $svnrepo && + git-svn fetch && + cd .. && + svn co $svnrepo svnwc && + ptouch svnwc/file gitwc/file && + ptouch svnwc/directory gitwc/directory && + ptouch svnwc/symlink-file gitwc/symlink-file && + ptouch svnwc/symlink-directory gitwc/symlink-directory + " + +test_expect_success 'info' " + (cd svnwc; svn info) > expected.info && + (cd gitwc; git-svn info) > actual.info && + git-diff expected.info actual.info + " + +test_expect_success 'info --url' ' + test $(cd gitwc; git-svn info --url) = $svnrepo + ' + +test_expect_success 'info .' " + (cd svnwc; svn info .) > expected.info-dot && + (cd gitwc; git-svn info .) > actual.info-dot && + git-diff expected.info-dot actual.info-dot + " + +test_expect_success 'info --url .' ' + test $(cd gitwc; git-svn info --url .) = $svnrepo + ' + +test_expect_success 'info file' " + (cd svnwc; svn info file) > expected.info-file && + (cd gitwc; git-svn info file) > actual.info-file && + git-diff expected.info-file actual.info-file + " + +test_expect_success 'info --url file' ' + test $(cd gitwc; git-svn info --url file) = "$svnrepo/file" + ' + +test_expect_success 'info directory' " + (cd svnwc; svn info directory) > expected.info-directory && + (cd gitwc; git-svn info directory) > actual.info-directory && + git-diff expected.info-directory actual.info-directory + " + +test_expect_success 'info --url directory' ' + test $(cd gitwc; git-svn info --url directory) = "$svnrepo/directory" + ' + +test_expect_success 'info symlink-file' " + (cd svnwc; svn info symlink-file) > expected.info-symlink-file && + (cd gitwc; git-svn info symlink-file) > actual.info-symlink-file && + git-diff expected.info-symlink-file actual.info-symlink-file + " + +test_expect_success 'info --url symlink-file' ' + test $(cd gitwc; git-svn info --url symlink-file) \ + = "$svnrepo/symlink-file" + ' + +test_expect_success 'info symlink-directory' " + (cd svnwc; svn info symlink-directory) \ + > expected.info-symlink-directory && + (cd gitwc; git-svn info symlink-directory) \ + > actual.info-symlink-directory && + git-diff expected.info-symlink-directory actual.info-symlink-directory + " + +test_expect_success 'info --url symlink-directory' ' + test $(cd gitwc; git-svn info --url symlink-directory) \ + = "$svnrepo/symlink-directory" + ' + +test_expect_success 'info added-file' " + echo two > gitwc/added-file && + cd gitwc && + git add added-file && + cd .. && + cp gitwc/added-file svnwc/added-file && + ptouch gitwc/added-file svnwc/added-file && + cd svnwc && + svn add added-file > /dev/null && + cd .. && + (cd svnwc; svn info added-file) > expected.info-added-file && + (cd gitwc; git-svn info added-file) > actual.info-added-file && + git-diff expected.info-added-file actual.info-added-file + " + +test_expect_success 'info --url added-file' ' + test $(cd gitwc; git-svn info --url added-file) \ + = "$svnrepo/added-file" + ' + +test_expect_success 'info added-directory' " + mkdir gitwc/added-directory svnwc/added-directory && + ptouch gitwc/added-directory svnwc/added-directory && + touch gitwc/added-directory/.placeholder && + cd svnwc && + svn add added-directory > /dev/null && + cd .. && + cd gitwc && + git add added-directory && + cd .. && + (cd svnwc; svn info added-directory) \ + > expected.info-added-directory && + (cd gitwc; git-svn info added-directory) \ + > actual.info-added-directory && + git-diff expected.info-added-directory actual.info-added-directory + " + +test_expect_success 'info --url added-directory' ' + test $(cd gitwc; git-svn info --url added-directory) \ + = "$svnrepo/added-directory" + ' + +test_expect_success 'info added-symlink-file' " + cd gitwc && + ln -s added-file added-symlink-file && + git add added-symlink-file && + cd .. && + cd svnwc && + ln -s added-file added-symlink-file && + svn add added-symlink-file > /dev/null && + cd .. && + ptouch gitwc/added-symlink-file svnwc/added-symlink-file && + (cd svnwc; svn info added-symlink-file) \ + > expected.info-added-symlink-file && + (cd gitwc; git-svn info added-symlink-file) \ + > actual.info-added-symlink-file && + git-diff expected.info-added-symlink-file \ + actual.info-added-symlink-file + " + +test_expect_success 'info --url added-symlink-file' ' + test $(cd gitwc; git-svn info --url added-symlink-file) \ + = "$svnrepo/added-symlink-file" + ' + +test_expect_success 'info added-symlink-directory' " + cd gitwc && + ln -s added-directory added-symlink-directory && + git add added-symlink-directory && + cd .. && + cd svnwc && + ln -s added-directory added-symlink-directory && + svn add added-symlink-directory > /dev/null && + cd .. && + ptouch gitwc/added-symlink-directory svnwc/added-symlink-directory && + (cd svnwc; svn info added-symlink-directory) \ + > expected.info-added-symlink-directory && + (cd gitwc; git-svn info added-symlink-directory) \ + > actual.info-added-symlink-directory && + git-diff expected.info-added-symlink-directory \ + actual.info-added-symlink-directory + " + +test_expect_success 'info --url added-symlink-directory' ' + test $(cd gitwc; git-svn info --url added-symlink-directory) \ + = "$svnrepo/added-symlink-directory" + ' + +# The next few tests replace the "Text Last Updated" value with a +# placeholder since git doesn't have a way to know the date that a +# now-deleted file was last checked out locally. Internally it +# simply reuses the Last Changed Date. + +test_expect_success 'info deleted-file' " + cd gitwc && + git rm -f file > /dev/null && + cd .. && + cd svnwc && + svn rm --force file > /dev/null && + cd .. && + (cd svnwc; svn info file) | + sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \ + > expected.info-deleted-file && + (cd gitwc; git-svn info file) | + sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \ + > actual.info-deleted-file && + git-diff expected.info-deleted-file actual.info-deleted-file + " + +test_expect_success 'info --url file (deleted)' ' + test $(cd gitwc; git-svn info --url file) \ + = "$svnrepo/file" + ' + +test_expect_success 'info deleted-directory' " + cd gitwc && + git rm -r -f directory > /dev/null && + cd .. && + cd svnwc && + svn rm --force directory > /dev/null && + cd .. && + (cd svnwc; svn info directory) | + sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \ + > expected.info-deleted-directory && + (cd gitwc; git-svn info directory) | + sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \ + > actual.info-deleted-directory && + git-diff expected.info-deleted-directory actual.info-deleted-directory + " + +test_expect_success 'info --url directory (deleted)' ' + test $(cd gitwc; git-svn info --url directory) \ + = "$svnrepo/directory" + ' + +test_expect_success 'info deleted-symlink-file' " + cd gitwc && + git rm -f symlink-file > /dev/null && + cd .. && + cd svnwc && + svn rm --force symlink-file > /dev/null && + cd .. && + (cd svnwc; svn info symlink-file) | + sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \ + > expected.info-deleted-symlink-file && + (cd gitwc; git-svn info symlink-file) | + sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \ + > actual.info-deleted-symlink-file && + git-diff expected.info-deleted-symlink-file \ + actual.info-deleted-symlink-file + " + +test_expect_success 'info --url symlink-file (deleted)' ' + test $(cd gitwc; git-svn info --url symlink-file) \ + = "$svnrepo/symlink-file" + ' + +test_expect_success 'info deleted-symlink-directory' " + cd gitwc && + git rm -f symlink-directory > /dev/null && + cd .. && + cd svnwc && + svn rm --force symlink-directory > /dev/null && + cd .. && + (cd svnwc; svn info symlink-directory) | + sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \ + > expected.info-deleted-symlink-directory && + (cd gitwc; git-svn info symlink-directory) | + sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \ + > actual.info-deleted-symlink-directory && + git-diff expected.info-deleted-symlink-directory \ + actual.info-deleted-symlink-directory + " + +test_expect_success 'info --url symlink-directory (deleted)' ' + test $(cd gitwc; git-svn info --url symlink-directory) \ + = "$svnrepo/symlink-directory" + ' + +# NOTE: git does not have the concept of replaced objects, +# so we can't test for files in that state. + +test_expect_success 'info unknown-file' " + echo two > gitwc/unknown-file && + cp gitwc/unknown-file svnwc/unknown-file && + ptouch gitwc/unknown-file svnwc/unknown-file && + (cd svnwc; svn info unknown-file) 2> expected.info-unknown-file && + (cd gitwc; git-svn info unknown-file) 2> actual.info-unknown-file && + git-diff expected.info-unknown-file actual.info-unknown-file + " + +test_expect_success 'info --url unknown-file' ' + test -z $(cd gitwc; git-svn info --url unknown-file \ + 2> ../actual.info--url-unknown-file) && + git-diff expected.info-unknown-file actual.info--url-unknown-file + ' + +test_expect_success 'info unknown-directory' " + mkdir gitwc/unknown-directory svnwc/unknown-directory && + ptouch gitwc/unknown-directory svnwc/unknown-directory && + touch gitwc/unknown-directory/.placeholder && + (cd svnwc; svn info unknown-directory) \ + 2> expected.info-unknown-directory && + (cd gitwc; git-svn info unknown-directory) \ + 2> actual.info-unknown-directory && + git-diff expected.info-unknown-directory actual.info-unknown-directory + " + +test_expect_success 'info --url unknown-directory' ' + test -z $(cd gitwc; git-svn info --url unknown-directory \ + 2> ../actual.info--url-unknown-directory) && + git-diff expected.info-unknown-directory \ + actual.info--url-unknown-directory + ' + +test_expect_success 'info unknown-symlink-file' " + cd gitwc && + ln -s unknown-file unknown-symlink-file && + cd .. && + cd svnwc && + ln -s unknown-file unknown-symlink-file && + cd .. && + ptouch gitwc/unknown-symlink-file svnwc/unknown-symlink-file && + (cd svnwc; svn info unknown-symlink-file) \ + 2> expected.info-unknown-symlink-file && + (cd gitwc; git-svn info unknown-symlink-file) \ + 2> actual.info-unknown-symlink-file && + git-diff expected.info-unknown-symlink-file \ + actual.info-unknown-symlink-file + " + +test_expect_success 'info --url unknown-symlink-file' ' + test -z $(cd gitwc; git-svn info --url unknown-symlink-file \ + 2> ../actual.info--url-unknown-symlink-file) && + git-diff expected.info-unknown-symlink-file \ + actual.info--url-unknown-symlink-file + ' + +test_expect_success 'info unknown-symlink-directory' " + cd gitwc && + ln -s unknown-directory unknown-symlink-directory && + cd .. && + cd svnwc && + ln -s unknown-directory unknown-symlink-directory && + cd .. && + ptouch gitwc/unknown-symlink-directory \ + svnwc/unknown-symlink-directory && + (cd svnwc; svn info unknown-symlink-directory) \ + 2> expected.info-unknown-symlink-directory && + (cd gitwc; git-svn info unknown-symlink-directory) \ + 2> actual.info-unknown-symlink-directory && + git-diff expected.info-unknown-symlink-directory \ + actual.info-unknown-symlink-directory + " + +test_expect_success 'info --url unknown-symlink-directory' ' + test -z $(cd gitwc; git-svn info --url unknown-symlink-directory \ + 2> ../actual.info--url-unknown-symlink-directory) && + git-diff expected.info-unknown-symlink-directory \ + actual.info--url-unknown-symlink-directory + ' + +test_done diff --git a/t/t9301-fast-export.sh b/t/t9301-fast-export.sh new file mode 100755 index 0000000000..f09bfb1117 --- /dev/null +++ b/t/t9301-fast-export.sh @@ -0,0 +1,123 @@ +#!/bin/sh +# +# Copyright (c) 2007 Johannes E. Schindelin +# + +test_description='git-fast-export' +. ./test-lib.sh + +test_expect_success 'setup' ' + + echo Wohlauf > file && + git add file && + test_tick && + git commit -m initial && + echo die Luft > file && + echo geht frisch > file2 && + git add file file2 && + test_tick && + git commit -m second && + echo und > file2 && + test_tick && + git commit -m third file2 && + test_tick && + git tag rein && + git checkout -b wer HEAD^ && + echo lange > file2 + test_tick && + git commit -m sitzt file2 && + test_tick && + git tag -a -m valentin muss && + git merge -s ours master + +' + +test_expect_success 'fast-export | fast-import' ' + + MASTER=$(git rev-parse --verify master) && + REIN=$(git rev-parse --verify rein) && + WER=$(git rev-parse --verify wer) && + MUSS=$(git rev-parse --verify muss) && + mkdir new && + git --git-dir=new/.git init && + git fast-export --all | + (cd new && + git fast-import && + test $MASTER = $(git rev-parse --verify refs/heads/master) && + test $REIN = $(git rev-parse --verify refs/tags/rein) && + test $WER = $(git rev-parse --verify refs/heads/wer) && + test $MUSS = $(git rev-parse --verify refs/tags/muss)) + +' + +test_expect_success 'fast-export master~2..master' ' + + git fast-export master~2..master | + sed "s/master/partial/" | + (cd new && + git fast-import && + test $MASTER != $(git rev-parse --verify refs/heads/partial) && + git diff master..partial && + git diff master^..partial^ && + ! git rev-parse partial~2) + +' + +test_expect_success 'iso-8859-1' ' + + git config i18n.commitencoding ISO-8859-1 && + # use author and committer name in ISO-8859-1 to match it. + . ../t3901-8859-1.txt && + test_tick && + echo rosten >file && + git commit -s -m den file && + git fast-export wer^..wer | + sed "s/wer/i18n/" | + (cd new && + git fast-import && + git cat-file commit i18n | grep "Ãéà óú") + +' + +cat > signed-tag-import << EOF +tag sign-your-name +from $(git rev-parse HEAD) +tagger C O Mitter <committer@example.com> 1112911993 -0700 +data 210 +A message for a sign +-----BEGIN PGP SIGNATURE----- +Version: GnuPG v1.4.5 (GNU/Linux) + +fakedsignaturefakedsignaturefakedsignaturefakedsignaturfakedsign +aturefakedsignaturefake= +=/59v +-----END PGP SIGNATURE----- +EOF + +test_expect_success 'set up faked signed tag' ' + + cat signed-tag-import | git fast-import + +' + +test_expect_success 'signed-tags=abort' ' + + ! git fast-export --signed-tags=abort sign-your-name + +' + +test_expect_success 'signed-tags=verbatim' ' + + git fast-export --signed-tags=verbatim sign-your-name > output && + grep PGP output + +' + +test_expect_success 'signed-tags=strip' ' + + git fast-export --signed-tags=strip sign-your-name > output && + ! grep PGP output + +' + +test_done diff --git a/t/t9500-gitweb-standalone-no-errors.sh b/t/t9500-gitweb-standalone-no-errors.sh index f7bad5bb2f..35fff3ddba 100755 --- a/t/t9500-gitweb-standalone-no-errors.sh +++ b/t/t9500-gitweb-standalone-no-errors.sh @@ -31,7 +31,6 @@ our \$projects_list = ""; our \$export_ok = ""; our \$strict_export = ""; -CGI::Carp::set_programname("gitweb/gitweb.cgi"); EOF cat >.git/description <<EOF @@ -558,4 +557,27 @@ test_expect_success \ 'gitweb_run "p=.git;a=tree;opt=--no-merges"' test_debug 'cat gitweb.log' +# ---------------------------------------------------------------------- +# gitweb config and repo config + +cat >>gitweb_config.perl <<EOF + +\$feature{'blame'}{'override'} = 1; +\$feature{'snapshot'}{'override'} = 1; +EOF + +test_expect_success \ + 'config override: tree view, features disabled in repo config' \ + 'git config gitweb.blame no && + git config gitweb.snapshot none && + gitweb_run "p=.git;a=tree"' +test_debug 'cat gitweb.log' + +test_expect_success \ + 'config override: tree view, features enabled in repo config' \ + 'git config gitweb.blame yes && + git config gitweb.snapshot "zip,tgz, tbz2" && + gitweb_run "p=.git;a=tree"' +test_debug 'cat gitweb.log' + test_done diff --git a/t/t9600-cvsimport.sh b/t/t9600-cvsimport.sh new file mode 100755 index 0000000000..7706430d81 --- /dev/null +++ b/t/t9600-cvsimport.sh @@ -0,0 +1,150 @@ +#!/bin/sh + +test_description='git-cvsimport basic tests' +. ./test-lib.sh + +if ! type cvs >/dev/null 2>&1 +then + say 'skipping cvsimport tests, cvs not found' + test_done + exit +fi + +cvsps_version=`cvsps -h 2>&1 | sed -ne 's/cvsps version //p'` +case "$cvsps_version" in +2.1) + ;; +'') + say 'skipping cvsimport tests, cvsps not found' + test_done + exit + ;; +*) + say 'skipping cvsimport tests, cvsps too old' + test_done + exit + ;; +esac + +CVSROOT=$(pwd)/cvsroot +export CVSROOT +# for clean cvsps cache +HOME=$(pwd) +export HOME + +test_expect_success 'setup cvsroot' 'cvs init' + +test_expect_success 'setup a cvs module' ' + + mkdir $CVSROOT/module && + cvs co -d module-cvs module && + cd module-cvs && + cat <<EOF >o_fortuna && +O Fortuna +velut luna +statu variabilis, + +semper crescis +aut decrescis; +vita detestabilis + +nunc obdurat +et tunc curat +ludo mentis aciem, + +egestatem, +potestatem +dissolvit ut glaciem. +EOF + cvs add o_fortuna && + cat <<EOF >message && +add "O Fortuna" lyrics + +These public domain lyrics make an excellent sample text. +EOF + cvs commit -F message && + cd .. +' + +test_expect_success 'import a trivial module' ' + + git cvsimport -a -z 0 -C module-git module && + git diff module-cvs/o_fortuna module-git/o_fortuna + +' + +test_expect_success 'pack refs' 'cd module-git && git gc && cd ..' + +test_expect_success 'update cvs module' ' + + cd module-cvs && + cat <<EOF >o_fortuna && +O Fortune, +like the moon +you are changeable, + +ever waxing +and waning; +hateful life + +first oppresses +and then soothes +as fancy takes it; + +poverty +and power +it melts them like ice. +EOF + cat <<EOF >message && +translate to English + +My Latin is terrible. +EOF + cvs commit -F message && + cd .. +' + +test_expect_success 'update git module' ' + + cd module-git && + git cvsimport -a -z 0 module && + git merge origin && + cd .. && + git diff module-cvs/o_fortuna module-git/o_fortuna + +' + +test_expect_success 'update cvs module' ' + + cd module-cvs && + echo 1 >tick && + cvs add tick && + cvs commit -m 1 + cd .. + +' + +test_expect_success 'cvsimport.module config works' ' + + cd module-git && + git config cvsimport.module module && + git cvsimport -a -z0 && + git merge origin && + cd .. && + git diff module-cvs/tick module-git/tick + +' + +test_expect_success 'import from a CVS working tree' ' + + cvs co -d import-from-wt module && + cd import-from-wt && + git cvsimport -a -z0 && + echo 1 >expect && + git log -1 --pretty=format:%s%n >actual && + git diff actual expect && + cd .. + +' + +test_done diff --git a/t/test-lib.sh b/t/test-lib.sh index 603a8cd5e7..90b6844d00 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -66,9 +66,6 @@ esac tput sgr0 >/dev/null 2>&1 && color=t -test "${test_description}" != "" || -error "Test script did not set test_description." - while test "$#" -ne 0 do case "$1" in @@ -77,8 +74,7 @@ do -i|--i|--im|--imm|--imme|--immed|--immedi|--immedia|--immediat|--immediate) immediate=t; shift ;; -h|--h|--he|--hel|--help) - echo "$test_description" - exit 0 ;; + help=t; shift ;; -v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose) verbose=t; shift ;; -q|--q|--qu|--qui|--quie|--quiet) @@ -124,6 +120,15 @@ say () { say_color info "$*" } +test "${test_description}" != "" || +error "Test script did not set test_description." + +if test "$help" = "t" +then + echo "$test_description" + exit 0 +fi + exec 5>&1 if test "$verbose" = "t" then diff --git a/templates/Makefile b/templates/Makefile index 6f4dbd362f..ebd3a62fd8 100644 --- a/templates/Makefile +++ b/templates/Makefile @@ -46,6 +46,6 @@ clean: $(RM) -r blt boilerplates.made install: all - $(INSTALL) -d -m755 '$(DESTDIR_SQ)$(template_dir_SQ)' + $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(template_dir_SQ)' (cd blt && $(TAR) cf - .) | \ (cd '$(DESTDIR_SQ)$(template_dir_SQ)' && $(TAR) xf -) diff --git a/templates/hooks--pre-commit b/templates/hooks--pre-commit index a19279b3e4..7092bae263 100644 --- a/templates/hooks--pre-commit +++ b/templates/hooks--pre-commit @@ -13,7 +13,7 @@ if git-rev-parse --verify HEAD 2>/dev/null then - git-diff-index -p -M --cached HEAD + git-diff-index -p -M --cached HEAD -- else # NEEDSWORK: we should produce a diff with an empty tree here # if we want to do the same verification for the initial import. diff --git a/templates/hooks--update b/templates/hooks--update index d8c76264be..bd93dd1977 100644 --- a/templates/hooks--update +++ b/templates/hooks--update @@ -10,6 +10,12 @@ # hooks.allowunannotated # This boolean sets whether unannotated tags will be allowed into the # repository. By default they won't be. +# hooks.allowdeletetag +# This boolean sets whether deleting tags will be allowed in the +# repository. By default they won't be. +# hooks.allowdeletebranch +# This boolean sets whether deleting branches will be allowed in the +# repository. By default they won't be. # # --- Command line @@ -32,18 +38,20 @@ fi # --- Config allowunannotated=$(git-repo-config --bool hooks.allowunannotated) +allowdeletebranch=$(git-repo-config --bool hooks.allowdeletebranch) +allowdeletetag=$(git-repo-config --bool hooks.allowdeletetag) # check for no description -projectdesc=$(sed -e '1p' "$GIT_DIR/description") -if [ -z "$projectdesc" -o "$projectdesc" = "Unnamed repository; edit this file to name it for gitweb" ]; then +projectdesc=$(sed -e '1q' "$GIT_DIR/description") +if [ -z "$projectdesc" -o "$projectdesc" = "Unnamed repository; edit this file to name it for gitweb." ]; then echo "*** Project description file hasn't been set" >&2 exit 1 fi # --- Check types -# if $newrev is 0000...0000, it's a commit to delete a branch +# if $newrev is 0000...0000, it's a commit to delete a ref. if [ "$newrev" = "0000000000000000000000000000000000000000" ]; then - newrev_type=commit + newrev_type=delete else newrev_type=$(git-cat-file -t $newrev) fi @@ -58,15 +66,36 @@ case "$refname","$newrev_type" in exit 1 fi ;; + refs/tags/*,delete) + # delete tag + if [ "$allowdeletetag" != "true" ]; then + echo "*** Deleting a tag is not allowed in this repository" >&2 + exit 1 + fi + ;; refs/tags/*,tag) # annotated tag ;; refs/heads/*,commit) # branch ;; + refs/heads/*,delete) + # delete branch + if [ "$allowdeletebranch" != "true" ]; then + echo "*** Deleting a branch is not allowed in this repository" >&2 + exit 1 + fi + ;; refs/remotes/*,commit) # tracking branch ;; + refs/remotes/*,delete) + # delete tracking branch + if [ "$allowdeletebranch" != "true" ]; then + echo "*** Deleting a tracking branch is not allowed in this repository" >&2 + exit 1 + fi + ;; *) # Anything else (is there anything else?) echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2 diff --git a/test-parse-options.c b/test-parse-options.c index 277cfe4d6d..4d3e2ec39e 100644 --- a/test-parse-options.c +++ b/test-parse-options.c @@ -18,6 +18,7 @@ int main(int argc, const char **argv) OPT_GROUP("string options"), OPT_STRING('s', "string", &string, "string", "get a string"), OPT_STRING(0, "string2", &string, "str", "get another string"), + OPT_STRING(0, "st", &string, "st", "get another string (pervert ordering)"), OPT_END(), }; int i; @@ -37,7 +37,7 @@ static int get_trace_fd(int *need_close) return STDERR_FILENO; if (strlen(trace) == 1 && isdigit(*trace)) return atoi(trace); - if (*trace == '/') { + if (is_absolute_path(trace)) { int fd = open(trace, O_WRONLY | O_APPEND | O_CREAT, 0666); if (fd == -1) { fprintf(stderr, @@ -72,7 +72,7 @@ void trace_printf(const char *fmt, ...) if (!fd) return; - strbuf_init(&buf, 0); + strbuf_init(&buf, 64); va_start(ap, fmt); len = vsnprintf(buf.buf, strbuf_avail(&buf), fmt, ap); va_end(ap); @@ -93,7 +93,7 @@ void trace_printf(const char *fmt, ...) close(fd); } -void trace_argv_printf(const char **argv, int count, const char *fmt, ...) +void trace_argv_printf(const char **argv, const char *fmt, ...) { struct strbuf buf; va_list ap; @@ -103,7 +103,7 @@ void trace_argv_printf(const char **argv, int count, const char *fmt, ...) if (!fd) return; - strbuf_init(&buf, 0); + strbuf_init(&buf, 64); va_start(ap, fmt); len = vsnprintf(buf.buf, strbuf_avail(&buf), fmt, ap); va_end(ap); @@ -117,7 +117,7 @@ void trace_argv_printf(const char **argv, int count, const char *fmt, ...) } strbuf_setlen(&buf, len); - sq_quote_argv(&buf, argv, count, 0); + sq_quote_argv(&buf, argv, 0); strbuf_addch(&buf, '\n'); write_or_whine_pipe(fd, buf.buf, buf.len, err_msg); strbuf_release(&buf); diff --git a/transport.c b/transport.c index d44fe7cee7..58e66f6c11 100644 --- a/transport.c +++ b/transport.c @@ -6,6 +6,7 @@ #endif #include "pkt-line.h" #include "fetch-pack.h" +#include "send-pack.h" #include "walker.h" #include "bundle.h" #include "dir.h" @@ -141,7 +142,7 @@ static void insert_packed_refs(const char *packed_refs, struct ref **list) } } -static struct ref *get_refs_via_rsync(const struct transport *transport) +static struct ref *get_refs_via_rsync(struct transport *transport) { struct strbuf buf = STRBUF_INIT, temp_dir = STRBUF_INIT; struct ref dummy, *tail = &dummy; @@ -283,6 +284,9 @@ static int rsync_transport_push(struct transport *transport, struct child_process rsync; const char *args[10]; + if (flags & TRANSPORT_PUSH_MIRROR) + return error("rsync transport does not support mirror mode"); + /* first push the objects */ strbuf_addstr(&buf, transport->url); @@ -344,6 +348,7 @@ static int rsync_transport_push(struct transport *transport, /* Generic functions for using commit walkers */ +#ifndef NO_CURL /* http fetch is the only user */ static int fetch_objs_via_walker(struct transport *transport, int nr_objs, struct ref **to_fetch) { @@ -370,6 +375,7 @@ static int fetch_objs_via_walker(struct transport *transport, free(dest); return 0; } +#endif /* NO_CURL */ static int disconnect_walker(struct transport *transport) { @@ -380,12 +386,16 @@ static int disconnect_walker(struct transport *transport) } #ifndef NO_CURL -static int curl_transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags) { +static int curl_transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags) +{ const char **argv; int argc; int err; - argv = xmalloc((refspec_nr + 11) * sizeof(char *)); + if (flags & TRANSPORT_PUSH_MIRROR) + return error("http transport does not support mirror mode"); + + argv = xmalloc((refspec_nr + 12) * sizeof(char *)); argv[0] = "http-push"; argc = 1; if (flags & TRANSPORT_PUSH_ALL) @@ -394,6 +404,8 @@ static int curl_transport_push(struct transport *transport, int refspec_nr, cons argv[argc++] = "--force"; if (flags & TRANSPORT_PUSH_DRY_RUN) argv[argc++] = "--dry-run"; + if (flags & TRANSPORT_PUSH_VERBOSE) + argv[argc++] = "--verbose"; argv[argc++] = transport->url; while (refspec_nr--) argv[argc++] = *refspec++; @@ -427,7 +439,7 @@ static int missing__target(int code, int result) #define missing_target(a) missing__target((a)->http_code, (a)->curl_result) -static struct ref *get_refs_via_curl(const struct transport *transport) +static struct ref *get_refs_via_curl(struct transport *transport) { struct buffer buffer; char *data, *start, *mid; @@ -458,6 +470,10 @@ static struct ref *get_refs_via_curl(const struct transport *transport) curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer); curl_easy_setopt(slot->curl, CURLOPT_URL, refs_url); curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL); + if (transport->remote->http_proxy) + curl_easy_setopt(slot->curl, CURLOPT_PROXY, + transport->remote->http_proxy); + if (start_active_slot(slot)) { run_active_slot(slot); if (results.curl_result != CURLE_OK) { @@ -524,7 +540,7 @@ struct bundle_transport_data { struct bundle_header header; }; -static struct ref *get_refs_from_bundle(const struct transport *transport) +static struct ref *get_refs_from_bundle(struct transport *transport) { struct bundle_transport_data *data = transport->data; struct ref *result = NULL; @@ -596,7 +612,7 @@ static int set_git_option(struct transport *connection, return 1; } -static struct ref *get_refs_via_connect(const struct transport *transport) +static struct ref *get_refs_via_connect(struct transport *transport) { struct git_transport_data *data = transport->data; struct ref *refs; @@ -643,53 +659,23 @@ static int fetch_refs_via_pack(struct transport *transport, free(heads); free_refs(refs); free(dest); - return 0; + return (refs ? 0 : -1); } -static int git_transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags) { +static int git_transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags) +{ struct git_transport_data *data = transport->data; - const char **argv; - char *rem; - int argc; - int err; + struct send_pack_args args; - argv = xmalloc((refspec_nr + 11) * sizeof(char *)); - argv[0] = "send-pack"; - argc = 1; - if (flags & TRANSPORT_PUSH_ALL) - argv[argc++] = "--all"; - if (flags & TRANSPORT_PUSH_FORCE) - argv[argc++] = "--force"; - if (flags & TRANSPORT_PUSH_DRY_RUN) - argv[argc++] = "--dry-run"; - if (data->receivepack) { - char *rp = xmalloc(strlen(data->receivepack) + 16); - sprintf(rp, "--receive-pack=%s", data->receivepack); - argv[argc++] = rp; - } - if (data->thin) - argv[argc++] = "--thin"; - rem = xmalloc(strlen(transport->remote->name) + 10); - sprintf(rem, "--remote=%s", transport->remote->name); - argv[argc++] = rem; - argv[argc++] = transport->url; - while (refspec_nr--) - argv[argc++] = *refspec++; - argv[argc] = NULL; - err = run_command_v_opt(argv, RUN_GIT_CMD); - switch (err) { - case -ERR_RUN_COMMAND_FORK: - error("unable to fork for %s", argv[0]); - case -ERR_RUN_COMMAND_EXEC: - error("unable to exec %s", argv[0]); - break; - case -ERR_RUN_COMMAND_WAITPID: - case -ERR_RUN_COMMAND_WAITPID_WRONG_PID: - case -ERR_RUN_COMMAND_WAITPID_SIGNAL: - case -ERR_RUN_COMMAND_WAITPID_NOEXIT: - error("%s died with strange error", argv[0]); - } - return !!err; + args.receivepack = data->receivepack; + args.send_all = !!(flags & TRANSPORT_PUSH_ALL); + args.send_mirror = !!(flags & TRANSPORT_PUSH_MIRROR); + args.force_update = !!(flags & TRANSPORT_PUSH_FORCE); + args.use_thin_pack = data->thin; + args.verbose = !!(flags & TRANSPORT_PUSH_VERBOSE); + args.dry_run = !!(flags & TRANSPORT_PUSH_DRY_RUN); + + return send_pack(&args, transport->url, transport->remote, refspec_nr, refspec); } static int disconnect_git(struct transport *transport) @@ -781,7 +767,7 @@ int transport_push(struct transport *transport, return transport->push(transport, refspec_nr, refspec, flags); } -struct ref *transport_get_remote_refs(struct transport *transport) +const struct ref *transport_get_remote_refs(struct transport *transport) { if (!transport->remote_refs) transport->remote_refs = transport->get_refs_list(transport); diff --git a/transport.h b/transport.h index df12ea7424..6fb4526cda 100644 --- a/transport.h +++ b/transport.h @@ -8,7 +8,7 @@ struct transport { struct remote *remote; const char *url; void *data; - struct ref *remote_refs; + const struct ref *remote_refs; /** * Returns 0 if successful, positive if the option is not @@ -18,7 +18,7 @@ struct transport { int (*set_option)(struct transport *connection, const char *name, const char *value); - struct ref *(*get_refs_list)(const struct transport *transport); + struct ref *(*get_refs_list)(struct transport *transport); int (*fetch)(struct transport *transport, int refs_nr, struct ref **refs); int (*push)(struct transport *connection, int refspec_nr, const char **refspec, int flags); @@ -30,6 +30,8 @@ struct transport { #define TRANSPORT_PUSH_ALL 1 #define TRANSPORT_PUSH_FORCE 2 #define TRANSPORT_PUSH_DRY_RUN 4 +#define TRANSPORT_PUSH_MIRROR 8 +#define TRANSPORT_PUSH_VERBOSE 16 /* Returns a transport suitable for the url */ struct transport *transport_get(struct remote *, const char *); @@ -61,7 +63,7 @@ int transport_set_option(struct transport *transport, const char *name, int transport_push(struct transport *connection, int refspec_nr, const char **refspec, int flags); -struct ref *transport_get_remote_refs(struct transport *transport); +const struct ref *transport_get_remote_refs(struct transport *transport); int transport_fetch_refs(struct transport *transport, struct ref *refs); void transport_unlock_pack(struct transport *transport); diff --git a/tree-diff.c b/tree-diff.c index 7c261fd7c3..aa0a100295 100644 --- a/tree-diff.c +++ b/tree-diff.c @@ -39,7 +39,7 @@ static int compare_tree_entry(struct tree_desc *t1, struct tree_desc *t2, const show_entry(opt, "+", t2, base, baselen); return 1; } - if (!opt->find_copies_harder && !hashcmp(sha1, sha2) && mode1 == mode2) + if (!DIFF_OPT_TST(opt, FIND_COPIES_HARDER) && !hashcmp(sha1, sha2) && mode1 == mode2) return 0; /* @@ -52,10 +52,10 @@ static int compare_tree_entry(struct tree_desc *t1, struct tree_desc *t2, const return 0; } - if (opt->recursive && S_ISDIR(mode1)) { + if (DIFF_OPT_TST(opt, RECURSIVE) && S_ISDIR(mode1)) { int retval; char *newbase = malloc_base(base, baselen, path1, pathlen1); - if (opt->tree_in_recursive) + if (DIFF_OPT_TST(opt, TREE_IN_RECURSIVE)) opt->change(opt, mode1, mode2, sha1, sha2, base, path1); retval = diff_tree_sha1(sha1, sha2, newbase, opt); @@ -206,7 +206,7 @@ static void show_entry(struct diff_options *opt, const char *prefix, struct tree const char *path; const unsigned char *sha1 = tree_entry_extract(desc, &path, &mode); - if (opt->recursive && S_ISDIR(mode)) { + if (DIFF_OPT_TST(opt, RECURSIVE) && S_ISDIR(mode)) { enum object_type type; int pathlen = tree_entry_len(path, sha1); char *newbase = malloc_base(base, baselen, path, pathlen); @@ -257,7 +257,7 @@ int diff_tree(struct tree_desc *t1, struct tree_desc *t2, const char *base, stru int baselen = strlen(base); for (;;) { - if (opt->quiet && opt->has_changes) + if (DIFF_OPT_TST(opt, QUIET) && DIFF_OPT_TST(opt, HAS_CHANGES)) break; if (opt->nr_paths) { skip_uninteresting(t1, base, baselen, opt); @@ -315,7 +315,7 @@ static void try_to_follow_renames(struct tree_desc *t1, struct tree_desc *t2, co q->nr = 0; diff_setup(&diff_opts); - diff_opts.recursive = 1; + DIFF_OPT_SET(&diff_opts, RECURSIVE); diff_opts.detect_rename = DIFF_DETECT_RENAME; diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT; diff_opts.single_follow = opt->paths[0]; @@ -380,7 +380,7 @@ int diff_tree_sha1(const unsigned char *old, const unsigned char *new, const cha init_tree_desc(&t1, tree1, size1); init_tree_desc(&t2, tree2, size2); retval = diff_tree(&t1, &t2, base, opt); - if (opt->follow_renames && diff_might_be_rename()) { + if (DIFF_OPT_TST(opt, FOLLOW_RENAMES) && diff_might_be_rename()) { init_tree_desc(&t1, tree1, size1); init_tree_desc(&t2, tree2, size2); try_to_follow_renames(&t1, &t2, base, opt); diff --git a/unpack-trees.c b/unpack-trees.c index c527d7d049..e9eb795d64 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -71,12 +71,8 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len, int remove; int baselen = strlen(base); int src_size = len + 1; - int i_stk = i_stk; int retval = 0; - if (o->dir) - i_stk = push_exclude_per_directory(o->dir, base, strlen(base)); - do { int i; const char *first; @@ -255,8 +251,6 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len, } while (1); leave_directory: - if (o->dir) - pop_exclude_per_directory(o->dir, i_stk); return retval; } @@ -404,7 +398,7 @@ static void verify_uptodate(struct cache_entry *ce, return; if (!lstat(ce->name, &st)) { - unsigned changed = ce_match_stat(ce, &st, 1); + unsigned changed = ce_match_stat(ce, &st, CE_MATCH_IGNORE_VALID); if (!changed) return; /* @@ -925,7 +919,7 @@ int oneway_merge(struct cache_entry **src, if (o->reset) { struct stat st; if (lstat(old->name, &st) || - ce_match_stat(old, &st, 1)) + ce_match_stat(old, &st, CE_MATCH_IGNORE_VALID)) old->ce_flags |= htons(CE_UPDATE); } return keep_entry(old, o); diff --git a/upload-pack.c b/upload-pack.c index 67994680f2..7e04311027 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -144,6 +144,7 @@ static void create_pack_file(void) char abort_msg[] = "aborting due to possible repository " "corruption on the remote side."; int buffered = -1; + ssize_t sz; const char *argv[10]; int arg = 0; @@ -168,22 +169,15 @@ static void create_pack_file(void) pack_objects.git_cmd = 1; pack_objects.argv = argv; - if (start_command(&pack_objects)) { - /* daemon sets things up to ignore TERM */ - kill(rev_list.pid, SIGKILL); + if (start_command(&pack_objects)) die("git-upload-pack: unable to fork git-pack-objects"); - } /* We read from pack_objects.err to capture stderr output for * progress bar, and pack_objects.out to capture the pack data. */ while (1) { - const char *who; struct pollfd pfd[2]; - pid_t pid; - int status; - ssize_t sz; int pe, pu, pollsize; reset_timeout(); @@ -204,123 +198,91 @@ static void create_pack_file(void) pollsize++; } - if (pollsize) { - if (poll(pfd, pollsize, -1) < 0) { - if (errno != EINTR) { - error("poll failed, resuming: %s", - strerror(errno)); - sleep(1); - } - continue; - } - if (0 <= pu && (pfd[pu].revents & (POLLIN|POLLHUP))) { - /* Data ready; we keep the last byte - * to ourselves in case we detect - * broken rev-list, so that we can - * leave the stream corrupted. This - * is unfortunate -- unpack-objects - * would happily accept a valid pack - * data with trailing garbage, so - * appending garbage after we pass all - * the pack data is not good enough to - * signal breakage to downstream. - */ - char *cp = data; - ssize_t outsz = 0; - if (0 <= buffered) { - *cp++ = buffered; - outsz++; - } - sz = xread(pack_objects.out, cp, - sizeof(data) - outsz); - if (0 < sz) - ; - else if (sz == 0) { - close(pack_objects.out); - pack_objects.out = -1; - } - else - goto fail; - sz += outsz; - if (1 < sz) { - buffered = data[sz-1] & 0xFF; - sz--; - } - else - buffered = -1; - sz = send_client_data(1, data, sz); - if (sz < 0) - goto fail; - } - if (0 <= pe && (pfd[pe].revents & (POLLIN|POLLHUP))) { - /* Status ready; we ship that in the side-band - * or dump to the standard error. - */ - sz = xread(pack_objects.err, progress, - sizeof(progress)); - if (0 < sz) - send_client_data(2, progress, sz); - else if (sz == 0) { - close(pack_objects.err); - pack_objects.err = -1; - } - else - goto fail; + if (!pollsize) + break; + + if (poll(pfd, pollsize, -1) < 0) { + if (errno != EINTR) { + error("poll failed, resuming: %s", + strerror(errno)); + sleep(1); } + continue; } - - /* See if the children are still there */ - if (rev_list.pid || pack_objects.pid) { - pid = waitpid(-1, &status, WNOHANG); - if (!pid) - continue; - who = ((pid == rev_list.pid) ? "git-rev-list" : - (pid == pack_objects.pid) ? "git-pack-objects" : - NULL); - if (!who) { - if (pid < 0) { - error("git-upload-pack: %s", - strerror(errno)); - goto fail; - } - error("git-upload-pack: we weren't " - "waiting for %d", pid); - continue; + if (0 <= pu && (pfd[pu].revents & (POLLIN|POLLHUP))) { + /* Data ready; we keep the last byte to ourselves + * in case we detect broken rev-list, so that we + * can leave the stream corrupted. This is + * unfortunate -- unpack-objects would happily + * accept a valid packdata with trailing garbage, + * so appending garbage after we pass all the + * pack data is not good enough to signal + * breakage to downstream. + */ + char *cp = data; + ssize_t outsz = 0; + if (0 <= buffered) { + *cp++ = buffered; + outsz++; + } + sz = xread(pack_objects.out, cp, + sizeof(data) - outsz); + if (0 < sz) + ; + else if (sz == 0) { + close(pack_objects.out); + pack_objects.out = -1; } - if (!WIFEXITED(status) || WEXITSTATUS(status) > 0) { - error("git-upload-pack: %s died with error.", - who); + else goto fail; + sz += outsz; + if (1 < sz) { + buffered = data[sz-1] & 0xFF; + sz--; } - if (pid == rev_list.pid) - rev_list.pid = 0; - if (pid == pack_objects.pid) - pack_objects.pid = 0; - if (rev_list.pid || pack_objects.pid) - continue; - } - - /* both died happily */ - if (pollsize) - continue; - - /* flush the data */ - if (0 <= buffered) { - data[0] = buffered; - sz = send_client_data(1, data, 1); + else + buffered = -1; + sz = send_client_data(1, data, sz); if (sz < 0) goto fail; - fprintf(stderr, "flushed.\n"); } - if (use_sideband) - packet_flush(1); - return; + if (0 <= pe && (pfd[pe].revents & (POLLIN|POLLHUP))) { + /* Status ready; we ship that in the side-band + * or dump to the standard error. + */ + sz = xread(pack_objects.err, progress, + sizeof(progress)); + if (0 < sz) + send_client_data(2, progress, sz); + else if (sz == 0) { + close(pack_objects.err); + pack_objects.err = -1; + } + else + goto fail; + } + } + + if (finish_command(&pack_objects)) { + error("git-upload-pack: git-pack-objects died with error."); + goto fail; + } + if (finish_async(&rev_list)) + goto fail; /* error was already reported */ + + /* flush the data */ + if (0 <= buffered) { + data[0] = buffered; + sz = send_client_data(1, data, 1); + if (sz < 0) + goto fail; + fprintf(stderr, "flushed.\n"); } + if (use_sideband) + packet_flush(1); + return; + fail: - if (pack_objects.pid) - kill(pack_objects.pid, SIGKILL); - if (rev_list.pid) - kill(rev_list.pid, SIGKILL); send_client_data(3, abort_msg, sizeof(abort_msg)); die("git-upload-pack: %s", abort_msg); } @@ -7,9 +7,9 @@ static void report(const char *prefix, const char *err, va_list params) { - fputs(prefix, stderr); - vfprintf(stderr, err, params); - fputs("\n", stderr); + char msg[256]; + vsnprintf(msg, sizeof(msg), err, params); + fprintf(stderr, "%s%s\n", prefix, msg); } static NORETURN void usage_builtin(const char *err) @@ -11,7 +11,8 @@ struct interval { }; /* auxiliary function for binary search in interval table */ -static int bisearch(ucs_char_t ucs, const struct interval *table, int max) { +static int bisearch(ucs_char_t ucs, const struct interval *table, int max) +{ int min = 0; int mid; @@ -283,7 +284,6 @@ int print_wrapped_text(const char *text, int indent, int indent2, int width) text++; } } - return w; } int is_encoding_utf8(const char *name) @@ -21,7 +21,7 @@ static void list_vars(void) { struct git_var *ptr; for(ptr = git_vars; ptr->read; ptr++) { - printf("%s=%s\n", ptr->name, ptr->read(0)); + printf("%s=%s\n", ptr->name, ptr->read(IDENT_WARN_ON_NO_NAME)); } } @@ -32,7 +32,7 @@ static const char *read_var(const char *var) val = NULL; for(ptr = git_vars; ptr->read; ptr++) { if (strcmp(var, ptr->name) == 0) { - val = ptr->read(1); + val = ptr->read(IDENT_ERROR_ON_NO_NAME); break; } } diff --git a/wt-status.c b/wt-status.c index 03b5ec4488..51c1879691 100644 --- a/wt-status.c +++ b/wt-status.c @@ -8,6 +8,7 @@ #include "revision.h" #include "diffcore.h" +int wt_status_relative_paths = 1; int wt_status_use_color = 0; static char wt_status_colors[][COLOR_MAXLEN] = { "", /* WT_STATUS_HEADER: normal */ @@ -22,7 +23,6 @@ static const char use_add_rm_msg[] = "use \"git add/rm <file>...\" to update what will be committed"; static const char use_add_to_include_msg[] = "use \"git add <file>...\" to include in what will be committed"; -static const char *excludes_file; static int parse_status_slot(const char *var, int offset) { @@ -82,33 +82,50 @@ static void wt_status_print_trailer(struct wt_status *s) color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), "#"); } -static const char *quote_crlf(const char *in, char *buf, size_t sz) +static char *quote_path(const char *in, int len, + struct strbuf *out, const char *prefix) { - const char *scan; - char *out; - const char *ret = in; + if (len < 0) + len = strlen(in); + + strbuf_grow(out, len); + strbuf_setlen(out, 0); + if (prefix) { + int off = 0; + while (prefix[off] && off < len && prefix[off] == in[off]) + if (prefix[off] == '/') { + prefix += off + 1; + in += off + 1; + len -= off + 1; + off = 0; + } else + off++; + + for (; *prefix; prefix++) + if (*prefix == '/') + strbuf_addstr(out, "../"); + } - for (scan = in, out = buf; *scan; scan++) { - int ch = *scan; - int quoted; + for ( ; len > 0; in++, len--) { + int ch = *in; switch (ch) { case '\n': - quoted = 'n'; + strbuf_addstr(out, "\\n"); break; case '\r': - quoted = 'r'; + strbuf_addstr(out, "\\r"); break; default: - *out++ = ch; + strbuf_addch(out, ch); continue; } - *out++ = '\\'; - *out++ = quoted; - ret = buf; } - *out = '\0'; - return ret; + + if (!out->len) + strbuf_addstr(out, "./"); + + return out->buf; } static void wt_status_print_filepair(struct wt_status *s, @@ -116,10 +133,12 @@ static void wt_status_print_filepair(struct wt_status *s, { const char *c = color(t); const char *one, *two; - char onebuf[PATH_MAX], twobuf[PATH_MAX]; + struct strbuf onebuf, twobuf; - one = quote_crlf(p->one->path, onebuf, sizeof(onebuf)); - two = quote_crlf(p->two->path, twobuf, sizeof(twobuf)); + strbuf_init(&onebuf, 0); + strbuf_init(&twobuf, 0); + one = quote_path(p->one->path, -1, &onebuf, s->prefix); + two = quote_path(p->two->path, -1, &twobuf, s->prefix); color_fprintf(s->fp, color(WT_STATUS_HEADER), "#\t"); switch (p->status) { @@ -151,6 +170,8 @@ static void wt_status_print_filepair(struct wt_status *s, die("bug: unhandled diff status %c", p->status); } fprintf(s->fp, "\n"); + strbuf_release(&onebuf); + strbuf_release(&twobuf); } static void wt_status_print_updated_cb(struct diff_queue_struct *q, @@ -205,8 +226,9 @@ static void wt_read_cache(struct wt_status *s) static void wt_status_print_initial(struct wt_status *s) { int i; - char buf[PATH_MAX]; + struct strbuf buf; + strbuf_init(&buf, 0); wt_read_cache(s); if (active_nr) { s->commitable = 1; @@ -215,11 +237,12 @@ static void wt_status_print_initial(struct wt_status *s) for (i = 0; i < active_nr; i++) { color_fprintf(s->fp, color(WT_STATUS_HEADER), "#\t"); color_fprintf_ln(s->fp, color(WT_STATUS_UPDATED), "new file: %s", - quote_crlf(active_cache[i]->name, - buf, sizeof(buf))); + quote_path(active_cache[i]->name, -1, + &buf, s->prefix)); } if (active_nr) wt_status_print_trailer(s); + strbuf_release(&buf); } static void wt_status_print_updated(struct wt_status *s) @@ -232,6 +255,7 @@ static void wt_status_print_updated(struct wt_status *s) rev.diffopt.format_callback_data = s; rev.diffopt.detect_rename = 1; rev.diffopt.rename_limit = 100; + rev.diffopt.break_opt = 0; wt_read_cache(s); run_diff_index(&rev, 1); } @@ -251,22 +275,18 @@ static void wt_status_print_changed(struct wt_status *s) static void wt_status_print_untracked(struct wt_status *s) { struct dir_struct dir; - const char *x; int i; int shown_header = 0; + struct strbuf buf; + strbuf_init(&buf, 0); memset(&dir, 0, sizeof(dir)); - dir.exclude_per_dir = ".gitignore"; if (!s->untracked) { dir.show_other_directories = 1; dir.hide_empty_directories = 1; } - x = git_path("info/exclude"); - if (file_exists(x)) - add_excludes_from_file(&dir, x); - if (excludes_file && file_exists(excludes_file)) - add_excludes_from_file(&dir, excludes_file); + setup_standard_excludes(&dir); read_directory(&dir, ".", "", 0, NULL); for(i = 0; i < dir.nr; i++) { @@ -291,20 +311,38 @@ static void wt_status_print_untracked(struct wt_status *s) shown_header = 1; } color_fprintf(s->fp, color(WT_STATUS_HEADER), "#\t"); - color_fprintf_ln(s->fp, color(WT_STATUS_UNTRACKED), "%.*s", - ent->len, ent->name); + color_fprintf_ln(s->fp, color(WT_STATUS_UNTRACKED), "%s", + quote_path(ent->name, ent->len, + &buf, s->prefix)); } + strbuf_release(&buf); } static void wt_status_print_verbose(struct wt_status *s) { struct rev_info rev; + int saved_stdout; + + fflush(s->fp); + + /* Sigh, the entire diff machinery is hardcoded to output to + * stdout. Do the dup-dance...*/ + saved_stdout = dup(STDOUT_FILENO); + if (saved_stdout < 0 ||dup2(fileno(s->fp), STDOUT_FILENO) < 0) + die("couldn't redirect stdout\n"); + init_revisions(&rev, NULL); setup_revisions(0, NULL, &rev, s->reference); rev.diffopt.output_format |= DIFF_FORMAT_PATCH; rev.diffopt.detect_rename = 1; wt_read_cache(s); run_diff_index(&rev, 1); + + fflush(stdout); + + if (dup2(saved_stdout, STDOUT_FILENO) < 0) + die("couldn't restore stdout\n"); + close(saved_stdout); } void wt_status_print(struct wt_status *s) @@ -357,17 +395,16 @@ void wt_status_print(struct wt_status *s) int git_status_config(const char *k, const char *v) { if (!strcmp(k, "status.color") || !strcmp(k, "color.status")) { - wt_status_use_color = git_config_colorbool(k, v); + wt_status_use_color = git_config_colorbool(k, v, -1); return 0; } if (!prefixcmp(k, "status.color.") || !prefixcmp(k, "color.status.")) { int slot = parse_status_slot(k, 13); color_parse(v, k, wt_status_colors[slot]); + return 0; } - if (!strcmp(k, "core.excludesfile")) { - if (!v) - die("core.excludesfile without value"); - excludes_file = xstrdup(v); + if (!strcmp(k, "status.relativepaths")) { + wt_status_relative_paths = git_config_bool(k, v); return 0; } return git_default_config(k, v); diff --git a/wt-status.h b/wt-status.h index 77449326db..63d50f2871 100644 --- a/wt-status.h +++ b/wt-status.h @@ -23,9 +23,12 @@ struct wt_status { int workdir_untracked; const char *index_file; FILE *fp; + const char *prefix; }; int git_status_config(const char *var, const char *value); +int wt_status_use_color; +int wt_status_relative_paths; void wt_status_prepare(struct wt_status *s); void wt_status_print(struct wt_status *s); diff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c index 5cb7171a8f..1bad8462fb 100644 --- a/xdiff/xdiffi.c +++ b/xdiff/xdiffi.c @@ -257,8 +257,6 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1, return ec; } } - - return -1; } diff --git a/xdiff/xutils.c b/xdiff/xutils.c index 2ade97b257..d7974d1a3e 100644 --- a/xdiff/xutils.c +++ b/xdiff/xutils.c @@ -232,8 +232,6 @@ int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags) return i1 >= s1 && i2 >= s2; } else return s1 == s2 && !memcmp(l1, l2, s1); - - return 0; } static unsigned long xdl_hash_record_with_whitespace(char const **data, |