git-diff-index
git-diff-tree
git-describe
+git-fast-export
git-fast-import
git-fetch
git-fetch--tool
git-rev-parse
git-revert
git-rm
-git-runstatus
git-send-email
git-send-pack
git-sh-setup
#
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>
--- /dev/null
+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).
ASCIIDOC=asciidoc
ASCIIDOC_EXTRA =
-ifdef ASCIIDOC8
-ASCIIDOC_EXTRA += -a asciidoc7compatible
-endif
INSTALL?=install
RM ?= rm -f
DOC_REF = origin/man
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:
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 \
#
doc.dep : $(wildcard *.txt) build-docdep.perl
$(RM) $@+ $@
- perl ./build-docdep.perl >$@+
+ $(PERL_PATH) ./build-docdep.perl >$@+
mv $@+ $@
-include doc.dep
$(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
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+
* 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".
* "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.
--- /dev/null
+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.
--- /dev/null
+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.
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
------------------
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
- 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
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
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:
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>
endif::doctype-manpage[]
</literallayout>
{title#}</example>
+endif::docbook-xsl-172[]
endif::backend-docbook[]
ifdef::doctype-manpage[]
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";
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";
}
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
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
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
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>::
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
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>::
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].
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.
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
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
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.
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].
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
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
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
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.
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
--
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
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[]
--- /dev/null
+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").
+// 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".
--raw::
Generate the raw format.
+ {git-diff-core? This is the default.}
--patch-with-raw::
Synonym for "-p --raw".
--patch-with-stat::
Synonym for "-p --stat".
+ {git-format-patch? This is the default.}
-z::
NUL-line termination on output. This affects the --raw
SYNOPSIS
--------
[verse]
-'git-add' [-n] [-v] [-f] [--interactive | -i] [-u] [--refresh]
+'git-add' [-n] [-v] [-f] [--interactive | -i] [--patch | -p] [-u] [--refresh]
[--] <filepattern>...
DESCRIPTION
-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
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.
--------
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]
$ 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
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[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>...
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>.
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
'--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.
$ 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
SYNOPSIS
--------
-'git-cherry-pick' [--edit] [-n] [-x] <commit>
+'git-cherry-pick' [--edit] [-n] [-m parent-number] [-x] <commit>
DESCRIPTION
-----------
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
'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
-----------
for "host.xz:foo/.git"). Cloning into an existing directory
is not allowed.
+:git-clone: 1
include::urls.txt[]
Examples
[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
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
--------
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:
'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
-----------
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
% 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[]
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
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
-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.
$ 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::
+
------------
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
---
OPTIONS
-------
+:git-diff: 1
include::diff-options.txt[]
<path>...::
--- /dev/null
+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
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]
OPTIONS
-------
+:git-format-patch: 1
include::diff-options.txt[]
-<n>::
-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.
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
------------
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
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.
--- /dev/null
+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
+++ /dev/null
-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
include::pretty-options.txt[]
+:git-log: 1
+include::diff-options.txt[]
+
-<n>::
Limits the number of commits to show.
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.
include::pretty-formats.txt[]
+include::diff-generate-patch.txt[]
Examples
--------
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,
[-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>]\*
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).
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
-------
SYNOPSIS
--------
-'git-prune' [-n] [--] [<head>...]
+'git-prune' [-n] [--expire <expire>] [--] [<head>...]
DESCRIPTION
-----------
\--::
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
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
-----------------
--------
[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
-----------
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.
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[]
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.
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
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]).
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
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
--------
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
-----------
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.
[ \--not ]
[ \--all ]
[ \--stdin ]
+ [ \--quiet ]
[ \--topo-order ]
[ \--parents ]
[ \--timestamp ]
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
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.
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
--------------
SYNOPSIS
--------
-'git-revert' [--edit | --no-edit] [-n] <commit>
+'git-revert' [--edit | --no-edit] [-n] [-m parent-number] <commit>
DESCRIPTION
-----------
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.
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.
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.
+++ /dev/null
-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
+++ /dev/null
-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
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
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
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]
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
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
* 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],
--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.
-------
* 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
`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
--- /dev/null
+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.
--- /dev/null
+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
- 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[]
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
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:
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:
-------------------------------------------------
$ 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
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
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
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>>.
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
~~~~~~~~~~~~~~~~~~~~~~~
$ 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,
$ 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
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
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
$ 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
-------------------------------------------------
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
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
==========================
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
-----------
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.
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
# 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.
# 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).
#
prefix = $(HOME)
bindir = $(prefix)/bin
+mandir = $(prefix)/share/man
gitexecdir = $(bindir)
sharedir = $(prefix)/share
template_dir = $(sharedir)/git-core/templates
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
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 = \
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 \
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
# 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
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 \
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 \
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 \
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 \
builtin-push.o \
builtin-read-tree.o \
builtin-reflog.o \
+ builtin-send-pack.o \
builtin-config.o \
builtin-rerere.o \
builtin-reset.o \
builtin-rev-parse.o \
builtin-revert.o \
builtin-rm.o \
- builtin-runstatus.o \
builtin-shortlog.o \
builtin-show-branch.o \
builtin-stripspace.o \
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
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
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
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
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))
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)
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)
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,$^)
$(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 $@
$(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 $@+ $@
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)|' \
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)"' $<
$(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)
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)
### 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)'; \
$(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
### 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
#
"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)
{
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);
}
/*
}
}
-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)
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;
"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"),
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;
}
}
}
}
- return NULL;
}
/* Verify that we recognize the lines following a git header */
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)
* 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;
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;
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;
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);
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;
argv[unk++] = arg;
}
- if (!incremental)
- setup_pager();
-
if (!blame_move_score)
blame_move_score = BLAME_DEFAULT_MOVE_SCORE;
if (!blame_copy_score)
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));
* 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), ":");
}
read_mailmap(&mailmap, ".mailmap", NULL);
+ if (!incremental)
+ setup_pager();
+
assign_blame(&sb, &revs, opt);
if (incremental)
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.")) {
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]);
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);
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;
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),
}
}
-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;
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);
}
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);
--- /dev/null
+/*
+ * "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;
+}
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');
--- /dev/null
+/*
+ * 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;
+}
#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;
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) {
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_);
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;
}
}
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);
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++;
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;
}
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;
}
}
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 |
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);
}
!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;
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);
}
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
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();
--- /dev/null
+/*
+ * "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;
+}
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;
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++) {
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],
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);
#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)
{
(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
{
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];
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;
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));
*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);
#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)
}
static void add_merge_config(struct ref **head,
- struct ref *remote_refs,
+ const struct ref *remote_refs,
struct branch *branch,
struct ref ***tail)
{
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])
/* 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/";
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)
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);
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;
}
* 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;
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;
}
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) {
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);
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);
/* 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");
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)
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++;
refs[j++] = ref;
} else
refs[j++] = argv[i];
- i++;
}
refs[j] = NULL;
ref_nr = j;
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)
#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;
if (!eol)
return "";
eol++;
- if (eol[1] == '\n')
+ if (*eol == '\n')
return ""; /* end of header */
buf = eol;
}
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(),
};
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;
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()
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);
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);
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);
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++;
*/
#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"
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);
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");
}
}
+/*
+ * 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) {
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--;
}
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;
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)
{
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);
}
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)
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 */
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;
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"))
!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);
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;
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;
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);
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);
/*
- * 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++;
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);
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 &&
* 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];
}
}
+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)
{
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;
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);
*/
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;
--- /dev/null
+#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;
+}
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;
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);
{
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;
}
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));
fclose(f);
}
- path_list_clear(&list, 1);
-
ret = skip;
out:
+ path_list_clear(&list, 1);
return ret;
}
{
char name[PATH_MAX];
int ret = -1;
+ int peek;
FILE *f = !strcmp(file, "-") ? stdin : fopen(file, "r");
int file_done = 0;
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) {
--- /dev/null
+/*
+ * 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);
+}
* 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;
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)
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];
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))
/* 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);
return;
if (name[cmplen] != '/') {
add_object_entry(entry.sha1,
- S_ISDIR(entry.mode) ? OBJ_TREE : OBJ_BLOB,
+ object_type(entry.mode),
fullname, 1);
return;
}
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 {
#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)
{
}
}
- 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
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 {
free(delta_buf);
}
+ trg_entry->delta = src_entry;
+ trg_entry->delta_size = delta_size;
+ trg->depth = src->depth + 1;
+
return 1;
}
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;
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;
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
count++;
if (idx >= window)
idx = 0;
- } while (i > 0);
+ }
for (i = 0; i < window; ++i) {
free_delta_index(array[i].index);
pthread_t thread;
struct object_entry **list;
unsigned list_size;
+ unsigned remaining;
int window;
int depth;
unsigned *processed;
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);
}
}
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)
#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);
}
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);
}
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;
}
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);
}
#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;
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);
}
}
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);
}
#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)
{
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)
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++) {
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)
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];
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++;
reset_type = HARD;
i++;
}
+ else if (!strcmp(argv[i], "-q")) {
+ quiet = 1;
+ i++;
+ }
+ else
+ break;
}
if (i < argc && argv[i][0] != '-')
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 */
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;
}
" --remove-empty\n"
" --all\n"
" --stdin\n"
+" --quiet\n"
" ordering output:\n"
" --topo-order\n"
" --date-order\n"
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)
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;
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
*/
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),
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;
/*
* 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;
char *ep, *sp;
fprintf(stderr, "%c%c%c ",
- (flags & TREECHANGE) ? 'T' : ' ',
+ (flags & TREESAME) ? ' ' : 'T',
(flags & UNINTERESTING) ? 'U' : ' ',
(flags & COUNTED) ? 'C' : ' ');
if (commit->util)
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)
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)
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",
* 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",
continue;
p->next = last;
last = p;
- if (!revs.prune_fn || (flags & TREECHANGE))
+ if (!(flags & TREESAME))
nr++;
on_list++;
}
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);
read_revisions_from_stdin(&revs);
continue;
}
+ if (!strcmp(arg, "--quiet")) {
+ quiet = 1;
+ continue;
+ }
usage(rev_list_usage);
}
}
}
- traverse_commit_list(&revs, show_commit, show_object);
+ traverse_commit_list(&revs,
+ quiet ? finish_commit : show_commit,
+ quiet ? finish_object : show_object);
return 0;
}
#include "refs.h"
#include "quote.h"
#include "builtin.h"
+#include "parse-options.h"
#define DO_REVS 1
#define DO_NOREV 2
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];
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;
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);
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);
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;
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.");
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));
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);
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",
}
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)
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++)
git_config(git_status_config);
wt_status_prepare(&s);
+ s.prefix = prefix;
for (i = 1; i < argc; i++) {
if (!strcmp(argv[i], "--color"))
--- /dev/null
+#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;
+}
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);
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)
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)
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] "))
#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)
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));
}
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 &&
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, '>');
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];
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.");
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);
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))
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)
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);
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);
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);
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);
#include "revision.h"
#include "list-objects.h"
#include "run-command.h"
+#include "refs.h"
static const char bundle_signature[] = "# v2 git bundle\n";
}
/* 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;
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
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);
}
#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
#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 {
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"
#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);
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);
struct lock_file {
struct lock_file *next;
+ int fd;
pid_t owner;
char on_list;
char filename[PATH_MAX];
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);
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;
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 */
};
#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);
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)
extern int pager_use_color;
extern char *editor_program;
+extern char *excludes_file;
/* base85 */
int decode_85(char *dst, const char *line, int linelen);
/* 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;
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 */
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++;
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,
/* "\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, ...);
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;
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;
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);
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;
--- /dev/null
+# 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
#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)
}
}
-/*
- * 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;
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
*
*
* 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 */
struct commit {
struct object object;
void *util;
+ unsigned int indegree;
unsigned long date;
struct commit_list *parents;
struct tree *tree;
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.
/*
* 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];
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 */
#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>
#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>
*
*/
#include "cache.h"
+#include "exec_cmd.h"
#define MAXNAME (256)
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;
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;
* 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)
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 ||
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@
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); \
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],
## 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.
[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])
])
[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.
# 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
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
*/
*
* 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;
prog, path, 0,
target_host, 0);
free(target_host);
+ free(url);
if (free_path)
free(path);
return NULL;
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;
_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
}
--- /dev/null
+#!/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
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
}
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 () {
all=
also=
+allow_empty=f
interactive=
only=
logfile=
-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#*=}"
;;
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." ;;
# 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
'')
# 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
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:
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":
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)
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)
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"]
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)
## 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
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})')
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
#
# 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
# ------
# 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.
# 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
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)
# 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
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
#
# 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
#
# 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:
# \
# * --- 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,
# 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)
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")
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
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
#
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)
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
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
#
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
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
}
# ---------------------------- main()
# --- Constants
-EMAILPREFIX="[SCM] "
LOGBEGIN="- Log -----------------------------------------------------------------"
LOGEND="-----------------------------------------------------------------------"
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"
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
{
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
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)
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;
/* 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];
{ "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)) {
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)) {
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;
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);
} 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;
}
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] == '-') {
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--;
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);
}
}
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;
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) &&
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");
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],
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;
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))
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));
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);
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))
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")) {
}
/* 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];
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 == '/'));
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;
}
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",
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;
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);
}
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)
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);
}
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)))
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;
}
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 |
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;
* 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);
}
/*
* 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;
}
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;
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;
break;
default:
if (p->score) {
- puts(" rewrite ");
+ fputs(" rewrite ", stdout);
write_name_quoted(p->two->path, stdout, ' ');
printf("(%d%%)\n", similarity_index(p));
}
* 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);
}
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);
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);
}
* 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);
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,
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;
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,
#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;
int output_format;
int pickaxe_opts;
int rename_score;
- int reverse_diff;
int rename_limit;
int setup;
int abbrev;
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[];
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);
* 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))
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)) {
* 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)
}
/* cost matrix sorted by most to least similar pair */
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)
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;
goto err;
close(fd);
+ if (buf_p)
+ *buf_p = buf;
buf[size++] = '\n';
entry = buf;
for (i = 0; i < size; i++) {
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.
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:
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);
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;
}
exit_early:
closedir(fdir);
-
- pop_exclude_per_directory(dir, exclude_stk);
}
return contents;
{
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);
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;
}
/*
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);
+}
#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 */
} **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;
/* 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);
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);
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
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) {
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;
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);
#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];
};
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);
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;
strbuf_swap(&last->data, dat);
}
last->offset = e->offset;
+ last->depth = e->depth;
}
return 0;
}
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;
}
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="))
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 '
#!/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') {
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?
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);
}
}
- 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}) {
}
push @return, +{
VALUE => $_,
- PRINT => (sprintf $status_fmt,
- $it->{INDEX}, $it->{FILE}, $_),
%$it,
};
}
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) {
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}) &&
return if ($opts->{LIST_ONLY});
- print $opts->{PROMPT};
+ print colored $prompt_color, $opts->{PROMPT};
if ($opts->{SINGLETON}) {
print "> ";
}
}
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);
$chosen[$i] = $choose;
}
}
- last if ($opts->{IMMEDIATE});
+ last if ($opts->{IMMEDIATE} || $line eq '*');
}
for ($i = 0; $i < @stuff; $i++) {
if ($chosen[$i]) {
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());
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;
}
}
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.
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) {
$next_hunk_start = $i;
}
push @{$this->{TEXT}}, $line;
+ push @{$this->{DISPLAY}}, $display;
$this->{OCNT}++;
$this->{NCNT}++;
if (defined $next_hunk_start) {
redo OUTER;
}
push @{$this->{TEXT}}, $line;
+ push @{$this->{DISPLAY}}, $display;
$this->{ADDDEL}++;
if ($line =~ /^-/) {
$this->{OCNT}++;
" +$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 {
}
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;
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) {
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;
}
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 {
}
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
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, ],
}
}
+process_args();
refresh();
-status_cmd();
-main_loop();
+if ($patch_mode) {
+ patch_update_cmd();
+}
+else {
+ status_cmd();
+ main_loop();
+}
#
# 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
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=
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
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
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
[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" ;;
# 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
}
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
git bisect run <cmd>...
use <cmd>... to automatically bisect.'
+OPTIONS_SPEC=
. git-sh-setup
require_work_tree
}
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
;;
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"
# Get rid of any old bisect state
#
bisect_clean_state
- mkdir "$GIT_DIR/refs/bisect"
#
# Check for one bad and then some good revisions.
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"
}
;;
*)
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 '
}
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
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"`
}
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"
next)
# Not sure we want "next" at the UI level anymore.
bisect_next "$@" ;;
- visualize)
+ visualize|view)
bisect_visualize "$@" ;;
reset)
bisect_reset "$@" ;;
#!/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
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" &&
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"
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
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" --
}
}
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
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
# 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
+++ /dev/null
-#!/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
# 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`" &&
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
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
#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
#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)
#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>
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)
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
#!/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);
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);
}
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);
}
}
}
}
- 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:";
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) {
}
}
- $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";
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
$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";
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;
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;
}
$state = 4;
} elsif ($state == 4 and s/^Branch:\s+//) {
s/\s+$//;
+ tr/_/\./ if ( $opt_u );
s/[\/]/$opt_s/g;
$branch = $_;
$state = 5;
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;
$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");
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;
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) {
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";
}
}
+ ### 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.");
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 ...
# 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
}
echo "$*" >&2
exit 1
}
+EOF
+)
+
+eval "$functions"
# When piped a commit, output a script to set the ident of either
# "author" or "committer
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
}
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>] \
[--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
filter_msg="$OPTARG"
;;
--commit-filter)
- filter_commit='SOURCE_FUNCTIONS=1 . "$this_script";'" $OPTARG"
+ filter_commit="$functions; $OPTARG"
;;
--tag-name-filter)
filter_tag_name="$OPTARG"
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
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
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"
[ -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"
+.DS_Store
+config.mak
+Git Gui.app*
+git-gui.tcl
GIT-VERSION-FILE
GIT-GUI-VARS
-git-citool
git-gui
lib/tclIndex
#!/bin/sh
GVF=GIT-VERSION-FILE
-DEF_VER=0.8.GITGUI
+DEF_VER=0.9.GITGUI
LF='
'
# 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
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 =
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=
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' \
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
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)
@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
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
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}]
######################################################################
##
tk_messageBox \
-icon error \
-type ok \
- -title "git-gui: fatal error" \
+ -title [mc "git-gui: fatal error"] \
-message $err
exit 1
}
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?
######################################################################
##
-## 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 {}
proc is_many_config {name} {
switch -glob -- $name {
+ gui.recentrepo -
remote.*.fetch -
remote.*.push
{return 1}
}
}
-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
$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 {
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
tk_messageBox \
-icon error \
-type ok \
- -title "git-gui: fatal error" \
+ -title [mc "git-gui: fatal error"] \
-message "Cannot determine Git version:
$err
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
}
-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
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]:
##
## 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
}
} 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
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
}
}
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 \
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]
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
##
## 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}
}
##
## 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
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
#
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 {}
}
######################################################################
##
-## 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
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
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]
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
[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
.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 \
#
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]
# -- 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
}
}
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
}
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
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 {}
-borderwidth 1 \
-relief sunken
label .branch.l1 \
- -text {Current Branch:} \
+ -text [mc "Current Branch:"] \
-anchor w \
-justify left
label .branch.cb \
# -- 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 \
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 \
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
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
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
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
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
}
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
}
$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"
} 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
}
set ctxm .vpane.lower.diff.header.ctxm
menu $ctxm -tearoff 0
$ctxm add command \
- -label {Copy} \
+ -label [mc Copy] \
-command {
clipboard clear
clipboard append \
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
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 {}
#
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
#
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}
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
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$} -
}
}
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
}
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]} {
--- /dev/null
+# 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
+}
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
-anchor w \
-justify left
label $w.header.path_l \
- -text {File:} \
+ -text [mc "File:"] \
-background gold \
-anchor w \
-justify left
menu $w.ctxm -tearoff 0
$w.ctxm add command \
- -label "Copy Commit" \
+ -label [mc "Copy Commit"] \
-command [cb _copycommit]
foreach i $w_columns {
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]
_exec_blame $this $w_asim @asim_data \
[list] \
- { copy/move tracking}
+ [mc "Loading copy/move tracking annotations..."]
}
} ifdeleted { catch {close $fd} }
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} {
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
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]
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 {}
}
$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
}
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"
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
}
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
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
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
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
-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]} {
-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
}
}
-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
}
-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
}
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 \
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] {
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 \
}
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 \
-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
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 .]"
}
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 \
-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
}
-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
}
-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
}
-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
}
-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
}
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
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
} 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]
}
}
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
}
-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]
if {[eof $fd]} {
close $fd
- set browser_status Ready.
+ set browser_status [mc "Ready."]
set browser_busy 0
set ls_buf {}
if {$n > 0} {
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
_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]
}
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
}
}
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
}
# 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
}
# 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.
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
}
}
}
}
default {
- _error $this "Merge strategy '$merge_type' not supported."
+ _error $this [mc "Merge strategy '%s' not supported." $merge_type]
return 0
}
}
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
}
}
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
}
}
&& $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
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 \
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
}
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
$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
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
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
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
}
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
}
--- /dev/null
+# 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
+}
+
+}
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 \
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
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]
}
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 \
if {$i ne {}} {
return [lindex $cur_specs $i 1]
} else {
- error "No revision selected."
+ error [mc "No revision selected."]
}
}
if {$c_expr ne {}} {
return $c_expr
} else {
- error "Revision expression is empty."
+ error [mc "Revision expression is empty."]
}
}
HEAD { return HEAD }
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"
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"
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]"
}
}
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
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
}
}
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
}
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 {}
}
}
&& $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
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
}
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
}
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
&& [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
}
}
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
}
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
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
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]
}
field t_short
field t_long
field w
+field w_t
field console_cr
field is_toplevel 1; # are we our own window?
}
set console_cr 1.0
+ set w_t $w.m.t
frame $w.m
label $w.m.l1 \
-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 {}}} {
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} {
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
}
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
}
}
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
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]"
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}
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
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
}
}
--- /dev/null
+# 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]]
+}
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 __
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!
#
} 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]} {
}
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} {
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
}
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
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
}
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 .
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 .
-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
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
}
# 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
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 \
$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
}
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} {
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 \
$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
}
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} {
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 \
$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
}
}
}
- 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} {
[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]
}
}
[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]
}
}
}
}
+
+ # 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 \
--- /dev/null
+# 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
+}
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
}
#
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
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
}
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"
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 {}
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]
}
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 .]"
}
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
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
}
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
}
}
}
-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
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 {
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]
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
}
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
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)}]} {
}
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)}]} {
}
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
}
}
}
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
set urltype url
}
radiobutton $w.dest.url_r \
- -text {Arbitrary URL:} \
+ -text [mc "Arbitrary URL:"] \
-value url \
-variable @urltype
entry $w.dest.url_t \
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 \
-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
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 {}]
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
-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")
}
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 \
-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
}
-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
}
set cons [console::new \
"push $uri" \
- "Deleting branches from $uri"]
+ [mc "Deleting branches from %s" $uri]]
console::exec $cons $push_cmd
}
$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]
# 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"]
}
}
}
}
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 \
--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"]
}
}
}
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 != {}} {
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"]
}
}
}
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
-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
$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
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} {
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]} {
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
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 {
if {$push_thin} {
lappend cmd --thin
}
+ if {$push_force} {
+ lappend cmd --force
+ }
if {$push_tags} {
lappend cmd --tags
}
}
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
}
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 \
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
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 \
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
}
--- /dev/null
+# 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
+}
--- /dev/null
+// 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();
+}
--- /dev/null
+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
--- /dev/null
+<?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>
--- /dev/null
+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.
--- /dev/null
+# 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"
--- /dev/null
+# 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 ""
--- /dev/null
+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) ; )
--- /dev/null
+# 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"
--- /dev/null
+# 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 ""
+
--- /dev/null
+"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."
--- /dev/null
+# 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"
--- /dev/null
+#!/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
--- /dev/null
+# 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 "工作副本,工作源码树"
--- /dev/null
+# 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..."
--- /dev/null
+# 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"
--- /dev/null
+# 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 "タグを含める"
--- /dev/null
+#!/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."]
+}
--- /dev/null
+# 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 "Дальше >"
--- /dev/null
+# 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 "上传..."
--- /dev/null
+#!/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]
#
# 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
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:
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
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
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"
}
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'";#'
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*)
esac
start_httpd
-test -z "$browser" && browser=echo
url=http://127.0.0.1:$port
-$browser $url || echo $url
+"$browser" $url || echo $url
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
# 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
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
}
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
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
single_strategy=no
;;
*)
- rm -f "$GIT_DIR/MERGE_SAVE"
+ rm -f "$GIT_DIR/MERGE_STASH"
single_strategy=yes
;;
esac
USAGE='[--tool=tool] [file to merge] ...'
SUBDIRECTORY_OK=Yes
+OPTIONS_SPEC=
. git-sh-setup
require_work_tree
prefix=$(git rev-parse --show-prefix)
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"
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
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
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
;;
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"
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
#!/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
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
}
# 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";
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
USAGE='(--continue | --abort | --skip | [--preserve-merges] [--verbose]
[--onto <branch>] <upstream> [<branch>])'
+OPTIONS_SPEC=
. git-sh-setup
require_work_tree
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
}
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"
}
die_with_patch () {
make_patch "$1"
+ git rerere
die "$2"
}
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
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^
} &&
--abort)
comment_for_reflog abort
+ git rerere clear
test -d "$DOTEST" || die "No interactive rebase running"
HEADNAME=$(cat "$DOTEST"/head-name)
--skip)
comment_for_reflog skip
+ git rerere clear
test -d "$DOTEST" || die "No interactive rebase running"
output git reset --hard && do_rest
-s|--strategy)
case "$#,$1" in
*,*=*)
- STRATEGY="-s `expr "z$1" : 'z-[^=]*=\(.*\)'`" ;;
+ STRATEGY="-s "$(expr "z$1" : 'z-[^=]*=\(.*\)') ;;
1,*)
usage ;;
*)
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
# 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"
'
SUBDIRECTORY_OK=Yes
+OPTIONS_SPEC=
. git-sh-setup
set_reflog_action rebase
require_work_tree
fi
cmt=`cat "$dotest/current"`
- if ! git diff-index --quiet HEAD
+ if ! git diff-index --quiet HEAD --
then
if ! git-commit -C "$cmt"
then
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))"'
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."
}
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
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?"
# 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"
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"
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
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`
# 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
-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
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
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
--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.
my $have_email_valid = eval { require Email::Valid; 1 };
my $smtp;
+my $auth;
sub unique_email_list(@);
sub cleanup_compose_files();
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 = $_;
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
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);
}
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;
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";
} 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";
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;
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, $_;
}
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;
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;
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();
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}" ]
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
#!/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
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
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"
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'
}
shift
show_stash "$@"
;;
+save)
+ shift
+ save_stash "$*" && git-reset --hard
+ ;;
apply)
shift
apply_stash "$@"
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
# Copyright (c) 2007 Lars Hjemli
USAGE='[--quiet] [--cached] [add <repo> [-b branch]|status|init|update] [--] [<path>...]'
+OPTIONS_SPEC=
. git-sh-setup
require_work_tree
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' )
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'"
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'"
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 ||
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"
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/;
$_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,
'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);
'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 } ],
'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;
}
};
-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}) {
$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();
}
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:
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);
"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) {
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 {
# 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,
@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;
}
}
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);
});
}
+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;
});
}
+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.
# 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;
}
}
+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 {
(\@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/;
$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 ",
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 {
$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' ",
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;
}
$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);
}
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) {
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 {
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);
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";
use warnings;
use Carp qw/croak/;
use IO::File;
-use Digest::MD5;
sub new {
my ($class, $opts) = @_;
$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);
]
}
+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!/+$!!;
$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(/|$)##;
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)?://#) {
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;
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);
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)$//;
$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 {
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}) {
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);
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};
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;
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;
}
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);
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))
{ "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 },
{ "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 },
{ "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 },
{ "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 },
--- /dev/null
+# 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 $@+ $@
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
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
color: #000000;
}
+div.readme {
+ padding: 8px;
+}
+
a.title:hover {
background-color: #d9d8d1;
}
table {
padding: 8px 4px;
-}
-
-table.project_list {
border-spacing: 0;
}
table.diff_tree {
- border-spacing: 0;
font-family: monospace;
}
);
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');
# 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);
}
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
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) {
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;
$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);
$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'
$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);
}
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],
}
} 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'},
}
}
- $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'},
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";
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";
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
my ($diffinfo, $patchinfo) = @_;
return defined $diffinfo && defined $patchinfo
- && ($diffinfo->{'to_file'} || $diffinfo->{'file'}) eq $patchinfo->{'to_file'};
+ && $diffinfo->{'to_file'} eq $patchinfo->{'to_file'};
}
$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]};
$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]};
$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];
$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];
$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]};
"<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";
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'}) {
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;
}
}
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'})},
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");
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",
"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);
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)},
}
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 &&
}
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";
}
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'}";
}
%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'};
# 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))) {
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 &&
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();
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);
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();
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";
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");
}
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")) {
#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)
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;
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)
page = p;
}
+ setup_man_path();
execlp("man", "man", page, NULL);
}
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;
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;
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;
/* 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);
}
}
if (*arg == '-') {
if (!strcmp(arg, "--all")) {
- push_all = 1;
+ push_all = MATCH_REFS_ALL;
continue;
}
if (!strcmp(arg, "--force")) {
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");
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);
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;
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;
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;
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);
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;
}
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 == '\'';
}
/*
"\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"
"\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)
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)
}
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);
}
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;
}
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;
}
}
+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;
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];
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)
(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",
}
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;
+ }
}
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;
}
}
return p;
}
- if (link[0] == '/') {
+ if (is_absolute_path(link)) {
/* absolute path simply replaces p */
if (link_len < s)
strcpy(p, link);
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);
/*
*/
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);
}
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)
{
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;
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;
}
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;
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)
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,
*/
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);
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);
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;
#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.
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 == '-')) {
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)
return (*opt->callback)(opt, get_arg(p), 0);
case OPTION_INTEGER:
- if (flags & OPT_UNSET) {
+ if (unset) {
*(int *)opt->value = 0;
return 0;
}
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);
/* 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;
}
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)
{
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))
#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)
fprintf(stderr, "%s\n", opts->help);
continue;
}
+ if (!full && (opts->flags & PARSE_OPT_HIDDEN))
+ continue;
pos = fprintf(stderr, " ");
if (opts->short_name)
pos += fprintf(stderr, " ...");
}
break;
- default:
+ default: /* OPTION_{BIT,BOOLEAN,SET_INT,SET_PTR} */
break;
}
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;
#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,
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;
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) \
{
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;
+++ /dev/null
-#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;
-}
};
}
+=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 )
$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..])
--- /dev/null
+#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);
+}
#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];
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)
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) {
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;
}
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;
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:
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,
if (!progress) {
/* unlikely, but here's a good fallback */
fprintf(stderr, "%s...\n", title);
+ fflush(stderr);
return NULL;
}
progress->title = title;
}
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)
*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);
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
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)) {
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)
/* 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;
}
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
}
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
}
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;
/*
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));
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;
* 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)
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;
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;
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;
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) {
}
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 {
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 */
}
}
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:
}
}
+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)
{
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",
}
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;
+}
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);
} 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;
}
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;
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);
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;
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
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)
* 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;
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
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 */
{
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;
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;
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;
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)
const char *receivepack;
const char *uploadpack;
+
+ /*
+ * for curl remotes only
+ */
+ char *http_proxy;
};
struct remote *remote_get(const char *name);
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.
*/
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
* 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.
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
#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;
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;
+ }
}
/*
}
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,
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)
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;
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);
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;
}
parent->next = NULL;
commit->parents = parent;
+ commit->object.flags |= TREESAME;
return;
case REV_TREE_NEW:
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)
* 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;
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);
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);
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;
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);
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;
}
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")) {
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)
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);
}
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;
}
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;
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)
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;
}
#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;
/* 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,
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;
};
#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);
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
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)
close(cmd->out);
}
- if (need_err) {
+ if (cmd->no_stderr)
+ dup_devnull(2);
+ else if (need_err) {
dup2(fderr[1], 2);
close_pair(fderr);
}
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;
};
+++ /dev/null
-#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;
-}
--- /dev/null
+#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
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);
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);
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.
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)
if (!work_tree_env)
inside_work_tree = 0;
setenv(GIT_DIR_ENVIRONMENT, ".", 1);
+ check_repository_format_gently(nongit_ok);
return NULL;
}
chdir("..");
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;
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)
#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];
char *pos = path;
struct stat st;
- if (*pos == '/')
+ if (is_absolute_path(path))
pos++;
while (pos) {
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;
}
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);
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 {
"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"
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)
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;
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];
{
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);
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);
* 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) {
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,
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);
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;
* 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.
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, ...);
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 */
test_expect_success 'plain' '
(
- unset GIT_DIR GIT_WORK_TREE &&
+ unset GIT_DIR GIT_WORK_TREE
mkdir plain &&
cd plain &&
git init
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
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
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
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
) &&
test_expect_success 'GIT_DIR non-bare' '
(
- unset GIT_CONFIG &&
+ unset GIT_CONFIG
mkdir non-bare &&
cd non-bare &&
GIT_DIR=.git git init
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
) &&
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
)
-s, --string <string>
get a string
--string2 <str> get another string
+ --st <st> get another string (pervert ordering)
EOF
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
--- /dev/null
+#!/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
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
'
+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
--- /dev/null
+#!/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
#!/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:
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
'
+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
git config core.excludesFile excludes-file
-git runstatus | grep "^# " > output
+git status | grep "^# " > output
cat > expect << EOF
# .gitignore
--- /dev/null
+#!/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
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' '
'
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 -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' '
--- /dev/null
+#!/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
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
'
'
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}" \
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-
'
test_expect_success \
- 'update-cache --add a file.' \
+ 'update-index --add a file.' \
'git update-index --add path0'
test_expect_success \
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
'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 \
--- /dev/null
+#!/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
--- /dev/null
+#!/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="\e$B$O$^$N\e(B \e$B$U$K$*$&\e(B" \
+ git format-patch -s --stdout -1 >output &&
+ grep Content-Type output
+
+'
+
+test_done
+
--- /dev/null
+#!/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
+
--- /dev/null
+#!/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/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 \
- 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 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 \
- 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
+++ 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 &&
+
+
+
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
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' \
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"'
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' \
--- /dev/null
+#!/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
--- /dev/null
+#!/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
--- /dev/null
+#!/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
'
+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
'
+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 &&
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
--- /dev/null
+#!/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
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 &&
'
+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
'
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)
'
--- /dev/null
+#!/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
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
--- /dev/null
+#!/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
--- /dev/null
+#!/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
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
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
'
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
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
'
'
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
'
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 &&
'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
done
unset vi
mv e-vi.sh vi
-PATH=".:$PATH"
unset EDITOR VISUAL GIT_EDITOR
test_expect_success setup '
;;
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
;;
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
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 &&
'
+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
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 &&
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
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
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) &&
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) &&
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) &&
. ./test-lib.sh
+git config clean.requireForce no
+
test_expect_success 'setup' '
mkdir -p src &&
'
+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 &&
'
+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 &&
'
+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 &&
'
+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
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
#
# FIXME: Test the various index usages, -i and -o, test reflog,
-# signoff, hooks
+# signoff
test_description='git-commit'
. ./test-lib.sh
"invalid options 2" \
"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"
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
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
'
+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
--- /dev/null
+#!/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
--- /dev/null
+#!/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
--- /dev/null
+#!/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
--- /dev/null
+#!/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
'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
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
--- /dev/null
+#!/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
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
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
"
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
--- /dev/null
+#!/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
--- /dev/null
+#!/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
--- /dev/null
+#!/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
--- /dev/null
+#!/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
our \$export_ok = "";
our \$strict_export = "";
-CGI::Carp::set_programname("gitweb/gitweb.cgi");
EOF
cat >.git/description <<EOF
'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
--- /dev/null
+#!/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
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
-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)
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
$(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 -)
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.
# 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
# --- 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
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
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;
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,
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);
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;
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);
}
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);
#endif
#include "pkt-line.h"
#include "fetch-pack.h"
+#include "send-pack.h"
#include "walker.h"
#include "bundle.h"
#include "dir.h"
}
}
-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;
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);
/* 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)
{
free(dest);
return 0;
}
+#endif /* NO_CURL */
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)
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++;
#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;
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) {
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;
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;
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)
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);
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
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);
#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 *);
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);
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;
/*
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);
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);
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);
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];
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);
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;
} while (1);
leave_directory:
- if (o->dir)
- pop_exclude_per_directory(o->dir, i_stk);
return retval;
}
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;
/*
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);
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;
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();
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);
}
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)
};
/* 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;
text++;
}
}
- return w;
}
int is_encoding_utf8(const char *name)
{
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));
}
}
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;
}
}
#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 */
"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)
{
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,
{
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) {
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,
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;
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)
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);
}
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++) {
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)
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);
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);
return ec;
}
}
-
- return -1;
}
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,