Merge branch 'nd/negative-pathspec'
authorJunio C Hamano <gitster@pobox.com>
Mon, 27 Jan 2014 18:45:43 +0000 (10:45 -0800)
committerJunio C Hamano <gitster@pobox.com>
Mon, 27 Jan 2014 18:45:44 +0000 (10:45 -0800)
* nd/negative-pathspec:
tree-walk.c: ignore trailing slash on submodule in tree_entry_interesting()

335 files changed:
.gitignore
Documentation/Makefile
Documentation/RelNotes/1.8.5.2.txt [new file with mode: 0644]
Documentation/RelNotes/1.8.5.3.txt [new file with mode: 0644]
Documentation/RelNotes/1.9.txt
Documentation/SubmittingPatches
Documentation/config.txt
Documentation/diff-config.txt
Documentation/diff-options.txt
Documentation/fetch-options.txt
Documentation/git-cat-file.txt
Documentation/git-checkout.txt
Documentation/git-cherry.txt
Documentation/git-clone.txt
Documentation/git-fetch.txt
Documentation/git-filter-branch.txt
Documentation/git-for-each-ref.txt
Documentation/git-lost-found.txt [deleted file]
Documentation/git-mv.txt
Documentation/git-p4.txt
Documentation/git-pack-objects.txt
Documentation/git-peek-remote.txt [deleted file]
Documentation/git-prune.txt
Documentation/git-pull.txt
Documentation/git-push.txt
Documentation/git-rebase.txt
Documentation/git-replace.txt
Documentation/git-repo-config.txt [deleted file]
Documentation/git-rm.txt
Documentation/git-show-ref.txt
Documentation/git-tar-tree.txt [deleted file]
Documentation/git.txt
Documentation/gitattributes.txt
Documentation/gitignore.txt
Documentation/gitk.txt
Documentation/gitmodules.txt
Documentation/gitremote-helpers.txt
Documentation/gitweb.conf.txt
Documentation/merge-options.txt
Documentation/merge-strategies.txt
Documentation/revisions.txt
Documentation/technical/api-builtin.txt
Documentation/technical/api-remote.txt
Documentation/technical/pack-heuristics.txt
Documentation/technical/pack-protocol.txt
Documentation/technical/protocol-capabilities.txt
Documentation/user-manual.txt
GIT-VERSION-GEN
Makefile
abspath.c
alias.c
attr.c
bisect.c
branch.c
builtin.h
builtin/add.c
builtin/apply.c
builtin/archive.c
builtin/blame.c
builtin/branch.c
builtin/cat-file.c
builtin/checkout.c
builtin/clean.c
builtin/clone.c
builtin/column.c
builtin/commit.c
builtin/config.c
builtin/describe.c
builtin/diff.c
builtin/fast-export.c
builtin/fetch-pack.c
builtin/fetch.c
builtin/fmt-merge-msg.c
builtin/for-each-ref.c
builtin/fsck.c
builtin/gc.c
builtin/get-tar-commit-id.c [new file with mode: 0644]
builtin/help.c
builtin/index-pack.c
builtin/init-db.c
builtin/log.c
builtin/ls-remote.c
builtin/mailinfo.c
builtin/merge-base.c
builtin/merge-recursive.c
builtin/merge.c
builtin/mv.c
builtin/name-rev.c
builtin/notes.c
builtin/pack-objects.c
builtin/prune-packed.c
builtin/prune.c
builtin/push.c
builtin/receive-pack.c
builtin/reflog.c
builtin/remote.c
builtin/repack.c
builtin/replace.c
builtin/rev-parse.c
builtin/send-pack.c
builtin/shortlog.c
builtin/show-branch.c
builtin/show-ref.c
builtin/symbolic-ref.c
builtin/tag.c
builtin/tar-tree.c [deleted file]
builtin/unpack-objects.c
builtin/update-ref.c
builtin/upload-archive.c
cache.h
command-list.txt
commit-slab.h
commit.c
commit.h
compat/mingw.c
compat/mingw.h
compat/vcbuild/scripts/clink.pl [changed mode: 0644->0755]
compat/vcbuild/scripts/lib.pl [changed mode: 0644->0755]
config.c
config.mak.uname
connect.c
connect.h
connected.c
connected.h
contrib/buildsystems/engine.pl [changed mode: 0644->0755]
contrib/buildsystems/generate [changed mode: 0644->0755]
contrib/buildsystems/parse.pl [changed mode: 0644->0755]
contrib/completion/git-completion.bash
contrib/completion/git-completion.tcsh
contrib/completion/git-completion.zsh
contrib/credential/gnome-keyring/git-credential-gnome-keyring.c
contrib/examples/git-whatchanged.sh
contrib/hooks/post-receive-email
contrib/hooks/pre-auto-gc-battery [changed mode: 0644->0755]
contrib/hooks/setgitperms.perl [changed mode: 0644->0755]
contrib/hooks/update-paranoid [changed mode: 0644->0755]
contrib/p4import/README [deleted file]
contrib/p4import/git-p4import.py [deleted file]
contrib/p4import/git-p4import.txt [deleted file]
contrib/remote-helpers/git-remote-bzr
contrib/remote-helpers/git-remote-hg
contrib/remote-helpers/test-hg.sh
contrib/subtree/git-subtree.sh
contrib/subtree/git-subtree.txt
convert.c
csum-file.c
csum-file.h
daemon.c
diff-no-index.c
diff.c
diff.h
diffcore-order.c
diffcore.h
dir.c
environment.c
fast-import.c
fetch-pack.c
fetch-pack.h
gettext.c
git-compat-util.h
git-difftool--helper.sh
git-gui/GIT-VERSION-GEN
git-gui/Makefile
git-gui/git-gui.sh
git-gui/lib/blame.tcl
git-gui/lib/choose_repository.tcl
git-gui/lib/index.tcl
git-gui/lib/option.tcl
git-gui/lib/spellcheck.tcl
git-gui/macosx/Info.plist
git-gui/po/bg.po [new file with mode: 0644]
git-gui/po/glossary/bg.po [new file with mode: 0644]
git-gui/po/glossary/git-gui-glossary.txt
git-gui/po/po2msg.sh [changed mode: 0644->0755]
git-gui/windows/git-gui.sh [changed mode: 0644->0755]
git-lost-found.sh [deleted file]
git-mergetool--lib.sh
git-p4.py
git-parse-remote.sh
git-pull.sh
git-rebase--am.sh
git-rebase--interactive.sh
git-rebase--merge.sh
git-rebase.sh
git-send-email.perl
git-sh-i18n.sh
git-sh-setup.sh
git-stash.sh
git-submodule.sh
git.c
git.rc
gitk-git/gitk
gitk-git/po/bg.po [new file with mode: 0644]
gitk-git/po/po2msg.sh [changed mode: 0644->0755]
gitweb/gitweb.perl
help.c
http-backend.c
http-push.c
http.c
imap-send.c
list-objects.c
log-tree.c
merge-recursive.c
notes-utils.c
notes.c
pack-write.c
pack.h
pager.c
parse-options.c
parse-options.h
pathspec.c
perl/Git/SVN.pm
perl/Git/SVN/Editor.pm
perl/Git/SVN/Log.pm
pkt-line.c
po/de.po
pretty.c
prompt.c
refs.c
remote-curl.c
remote-testsvn.c
remote.c
remote.h
replace_object.c
revision.c
send-pack.c
send-pack.h
sequencer.c
setup.c
sha1_file.c
sha1_name.c
shallow.c
shell.c
strbuf.c
streaming.c
submodule.c
t/Git-SVN/00compile.t [changed mode: 0644->0755]
t/Git-SVN/Utils/add_path_to_url.t [changed mode: 0644->0755]
t/Git-SVN/Utils/can_compress.t [changed mode: 0644->0755]
t/Git-SVN/Utils/canonicalize_url.t [changed mode: 0644->0755]
t/Git-SVN/Utils/collapse_dotdot.t [changed mode: 0644->0755]
t/Git-SVN/Utils/fatal.t [changed mode: 0644->0755]
t/Git-SVN/Utils/join_paths.t [changed mode: 0644->0755]
t/annotate-tests.sh
t/gitweb-lib.sh
t/lib-bash.sh
t/lib-cvs.sh
t/lib-diff-alternative.sh
t/lib-gettext.sh
t/lib-git-daemon.sh
t/lib-git-p4.sh
t/lib-httpd.sh
t/lib-httpd/passwd
t/lib-pack.sh
t/lib-pager.sh
t/lib-prereq-FILEMODE.sh [deleted file]
t/lib-read-tree.sh
t/lib-rebase.sh
t/lib-terminal.sh
t/perf/p0001-rev-list.sh
t/perf/p4001-diff-no-index.sh [new file with mode: 0755]
t/perf/perf-lib.sh
t/t0000-basic.sh
t/t0002-gitfile.sh
t/t0003-attributes.sh
t/t0202/test.pl [changed mode: 0644->0755]
t/t1006-cat-file.sh
t/t1303-wacky-config.sh
t/t1506-rev-parse-diagnosis.sh
t/t1507-rev-parse-upstream.sh
t/t1508-at-combinations.sh
t/t3400-rebase.sh
t/t3600-rm.sh
t/t3700-add.sh
t/t3701-add-interactive.sh
t/t3903-stash.sh
t/t4020-diff-external.sh
t/t4053-diff-no-index.sh
t/t4056-diff-order.sh [new file with mode: 0755]
t/t4102-apply-rename.sh
t/t4116-apply-reverse.sh
t/t4120-apply-popt.sh
t/t4129-apply-samemode.sh
t/t4205-log-pretty-formats.sh
t/t5000-tar-tree.sh
t/t5001-archive-attr.sh
t/t5302-pack-index.sh
t/t5304-prune.sh
t/t5500-fetch-pack.sh
t/t5510-fetch.sh
t/t5515/fetch.br-unconfig_--tags_.._.git
t/t5515/fetch.master_--tags_.._.git
t/t5516-fetch-push.sh
t/t5525-fetch-tagopt.sh
t/t5531-deep-submodule-push.sh
t/t5536-fetch-conflicts.sh [new file with mode: 0755]
t/t5537-fetch-shallow.sh [new file with mode: 0755]
t/t5538-push-shallow.sh [new file with mode: 0755]
t/t5540-http-push.sh
t/t5541-http-push.sh
t/t5550-http-fetch.sh
t/t5551-http-fetch.sh
t/t5601-clone.sh
t/t6000-rev-list-misc.sh
t/t6010-merge-base.sh
t/t6031-merge-recursive.sh
t/t6050-replace.sh
t/t6101-rev-parse-parents.sh
t/t7001-mv.sh
t/t7006-pager.sh
t/t7507-commit-verbose.sh
t/t9150/make-svk-dump [changed mode: 0644->0755]
t/t9151/make-svnmerge-dump [changed mode: 0644->0755]
t/t9200-git-cvsexportcommit.sh
t/t9802-git-p4-filetype.sh
t/t9805-git-p4-skip-submit-edit.sh
t/t9807-git-p4-submit.sh
t/t9809-git-p4-client-view.sh
t/t9812-git-p4-wildcards.sh
t/t9813-git-p4-preserve-users.sh
t/t9816-git-p4-locked.sh [new file with mode: 0755]
t/test-lib-functions.sh
t/test-lib.sh
tag.c
test-line-buffer.c
test-string-list.c
trace.c
transport-helper.c
transport.c
transport.h
unpack-trees.c
upload-pack.c
vcs-svn/fast_export.c
wt-status.c
wt-status.h
index a78367c17f106e4adb5f5f04f2cbd266dd61425e..b5f9defed37c43b2c6075d7065c8cbae2b1797e1 100644 (file)
@@ -76,7 +76,6 @@
 /git-init-db
 /git-instaweb
 /git-log
-/git-lost-found
 /git-ls-files
 /git-ls-remote
 /git-ls-tree
 /git-pack-refs
 /git-parse-remote
 /git-patch-id
-/git-peek-remote
 /git-prune
 /git-prune-packed
 /git-pull
 /git-remote-testsvn
 /git-repack
 /git-replace
-/git-repo-config
 /git-request-pull
 /git-rerere
 /git-reset
 /git-svn
 /git-symbolic-ref
 /git-tag
-/git-tar-tree
 /git-unpack-file
 /git-unpack-objects
 /git-update-index
index 91a12c7e51e7869f4b64385aeffc1762c3ddfa38..36c58fc6460fb6e6fd2eefdc0d17384a4a65d851 100644 (file)
@@ -324,7 +324,7 @@ manpage-base-url.xsl: manpage-base-url.xsl.in
 
 user-manual.xml: user-manual.txt user-manual.conf
        $(QUIET_ASCIIDOC)$(RM) $@+ $@ && \
-       $(ASCIIDOC) $(ASCIIDOC_EXTRA) -b docbook -d book -o $@+ $< && \
+       $(ASCIIDOC) $(ASCIIDOC_EXTRA) -b docbook -d article -o $@+ $< && \
        mv $@+ $@
 
 technical/api-index.txt: technical/api-index-skel.txt \
diff --git a/Documentation/RelNotes/1.8.5.2.txt b/Documentation/RelNotes/1.8.5.2.txt
new file mode 100644 (file)
index 0000000..3ac4984
--- /dev/null
@@ -0,0 +1,20 @@
+Git v1.8.5.2 Release Notes
+==========================
+
+Fixes since v1.8.5.1
+--------------------
+
+ * "git diff -- ':(icase)makefile'" was unnecessarily rejected at the
+   command line parser.
+
+ * "git cat-file --batch-check=ok" did not check the existence of
+   the named object.
+
+ * "git am --abort" sometimes complained about not being able to write
+   a tree with an 0{40} object in it.
+
+ * Two processes creating loose objects at the same time could have
+   failed unnecessarily when the name of their new objects started
+   with the same byte value, due to a race condition.
+
+Also contains typofixes, documentation updates and trivial code clean-ups.
diff --git a/Documentation/RelNotes/1.8.5.3.txt b/Documentation/RelNotes/1.8.5.3.txt
new file mode 100644 (file)
index 0000000..3de2dd0
--- /dev/null
@@ -0,0 +1,27 @@
+Git v1.8.5.3 Release Notes
+==========================
+
+Fixes since v1.8.5.2
+--------------------
+
+ * The "--[no-]informative-errors" options to "git daemon" were parsed
+   a bit too loosely, allowing any other string after these option
+   names.
+
+ * A "gc" process running as a different user should be able to stop a
+   new "gc" process from starting.
+
+ * An earlier "clean-up" introduced an unnecessary memory leak to the
+   credential subsystem.
+
+ * "git mv A B/", when B does not exist as a directory, should error
+   out, but it didn't.
+
+ * "git rev-parse <revs> -- <paths>" did not implement the usual
+   disambiguation rules the commands in the "git log" family used in
+   the same way.
+
+ * "git cat-file --batch=", an admittedly useless command, did not
+   behave very well.
+
+Also contains typofixes, documentation updates and trivial code clean-ups.
index 9debcc41eb39e10b9b27d69d40b891beda9fc226..7bcf371c5f4b2c531ecd9fdc5cd2b3d5c2f1bf93 100644 (file)
@@ -15,6 +15,17 @@ Read-only support for experimental loose-object format, in which users
 could optionally choose to write in their loose objects for a short
 while between v1.4.3 to v1.5.3 era, has been dropped.
 
+The meanings of "--tags" option to "git fetch" has changed; the
+command fetches tags _in addition to_ what are fetched by the same
+command line without the option.
+
+The way "git push $there $what" interprets $what part given on the
+command line, when it does not have a colon that explicitly tells us
+what ref at the $there repository is to be updated, has been enhanced.
+
+A handful of ancient commands that have long been deprecated are
+finally gone (repo-config, tar-tree, lost-found, and peek-remote).
+
 
 Backward compatibility notes (for Git 2.0)
 ------------------------------------------
@@ -68,15 +79,71 @@ Updates since v1.8.5
 
 Foreign interfaces, subsystems and ports.
 
- * The HTTP transport, when talking GSS-Negotinate, uses "100
+ * The HTTP transport, when talking GSS-Negotiate, uses "100
    Continue" response to avoid having to rewind and resend a large
    payload, which may not be always doable.
 
  * Various bugfixes to remote-bzr and remote-hg (in contrib/).
 
+ * The build procedure is aware of MirBSD now.
+
 
 UI, Workflows & Features
 
+ * Fetching from a shallowly-cloned repository used to be forbidden,
+   primarily because the codepaths involved were not carefully vetted
+   and we did not bother supporting such usage. This release attempts
+   to allow object transfer out of a shallowly-cloned repository in a
+   more controlled way (i.e. the receiver become a shallow repository
+   with a truncated history).
+
+ * Just like we give a reasonable default for "less" via the LESS
+   environment variable, we now specify a reasonable default for "lv"
+   via the "LV" environment variable when spawning the pager.
+
+ * Two-level configuration variable names in "branch.*" and "remote.*"
+   hierarchies, whose variables are predominantly three-level, were
+   not completed by hitting a <TAB> in bash and zsh completions.
+
+ * Fetching 'frotz' branch with "git fetch", while 'frotz/nitfol'
+   remote-tracking branch from an earlier fetch was still there, would
+   error out, primarily because the command was not told that it is
+   allowed to lose any information on our side.  "git fetch --prune"
+   now can be used to remove 'frotz/nitfol' to make room to fetch and
+   store 'frotz' remote-tracking branch.
+
+ * "diff.orderfile=<file>" configuration variable can be used to
+   pretend as if the "-O<file>" option were given from the command
+   line of "git diff", etc.
+
+ * The negative pathspec syntax allows "git log -- . ':!dir'" to tell
+   us "I am interested in everything but 'dir' directory".
+
+ * "git difftool" shows how many different paths there are in total,
+   and how many of them have been shown so far, to indicate progress.
+
+ * "git push origin master" used to push our 'master' branch to update
+   the 'master' branch at the 'origin' repository.  This has been
+   enhanced to use the same ref mapping "git push origin" would use to
+   determine what ref at the 'origin' to be updated with our 'master'.
+   For example, with this configuration
+
+   [remote "origin"]
+      push = refs/heads/*:refs/review/*
+
+   that would cause "git push origin" to push out our local branches
+   to corresponding refs under refs/review/ hierarchy at 'origin',
+   "git push origin master" would update 'refs/review/master' over
+   there.  Alternatively, if push.default is set to 'upstream' and our
+   'master' is set to integrate with 'topic' from the 'origin' branch,
+   running "git push origin" while on our 'master' would update their
+   'topic' branch, and running "git push origin master" while on any
+   of our branches does the same.
+
+ * "gitweb" learned to treat ref hierarchies other than refs/heads as
+   if they are additional branch namespaces (e.g. refs/changes/ in
+   Gerrit).
+
  * "git for-each-ref --format=..." learned a few formatting directives;
    e.g. "%(color:red)%(HEAD)%(color:reset) %(refname:short) %(subject)".
 
@@ -91,9 +158,36 @@ UI, Workflows & Features
  * "git rev-parse --parseopt" learned a new "--stuck-long" option to
    help scripts parse options with an optional parameter.
 
+ * The "--tags" option to "git fetch" no longer tells the command to
+   fetch _only_ the tags. It instead fetches tags _in addition to_
+   what are fetched by the same command line without the option.
+
 
 Performance, Internal Implementation, etc.
 
+ * When parsing a 40-hex string into the object name, the string is
+   checked to see if it can be interpreted as a ref so that a warning
+   can be given for ambiguity. The code kicked in even when the
+   core.warnambiguousrefs is set to false to squelch this warning, in
+   which case the cycles spent to look at the ref namespace were an
+   expensive no-op, as the result was discarded without being used.
+
+ * The naming convention of the packfiles has been updated; it used to
+   be based on the enumeration of names of the objects that are
+   contained in the pack, but now it also depends on how the packed
+   result is represented---packing the same set of objects using
+   different settings (or delta order) would produce a pack with
+   different name.
+
+ * "git diff --no-index" mode used to unnecessarily attempt to read
+   the index when there is one.
+
+ * The deprecated parse-options macro OPT_BOOLEAN has been removed;
+   use OPT_BOOL or OPT_COUNTUP in new code.
+
+ * A few duplicate implementations of prefix/suffix string comparison
+   functions have been unified to starts_with() and ends_with().
+
  * The new PERLLIB_EXTRA makefile variable can be used to specify
    additional directories Perl modules (e.g. the ones necessary to run
    git-svn) are installed on the platform when building.
@@ -101,8 +195,14 @@ Performance, Internal Implementation, etc.
  * "git merge-base" learned the "--fork-point" mode, that implements
    the same logic used in "git pull --rebase" to find a suitable fork
    point out of the reflog entries for the remote-tracking branch the
-   work has been based on.
+   work has been based on.  "git rebase" has the same logic that can be
+   triggered with the "--fork-point" option.
 
+ * A third-party "receive-pack" (the responder to "git push") can
+   advertise the "no-thin" capability to tell "git push" not to use
+   the thin-pack optimization. Our receive-pack has always been
+   capable of accepting and fattening a thin-pack, and will continue
+   not to ask "git push" to use a non-thin pack.
 
 
 Also contains various documentation updates and code clean-ups.
@@ -115,6 +215,99 @@ Unless otherwise noted, all the fixes since v1.8.5 in the maintenance
 track are contained in this release (see the maintenance releases' notes
 for details).
 
+ * "submodule.*.update=checkout", when propagated from .gitmodules to
+   .git/config, turned into a "submodule.*.update=none", which did not
+   make much sense.
+   (merge efa8fd7 fp/submodule-checkout-mode later to maint).
+
+ * The implementation of 'git stash $cmd "stash@{...}"' did not quote
+   the stash argument properly and left it split at IFS whitespace.
+   (merge 2a07e43 ow/stash-with-ifs later to maint).
+
+ * The "--[no-]informative-errors" options to "git daemon" were parsed
+   a bit too loosely, allowing any other string after these option
+   names.
+   (merge 82246b7 nd/daemon-informative-errors-typofix later to maint).
+
+ * There is no reason to have a hardcoded upper limit of the number of
+   parents for an octopus merge, created via the graft mechanism, but
+   there was.
+   (merge e228c17 js/lift-parent-count-limit later to maint).
+
+ * The basic test used to leave unnecessary trash directories in the
+   t/ directory.
+   (merge 738a8be jk/test-framework-updates later to maint).
+
+ * "git merge-base --octopus" used to leave cleaning up suboptimal
+   result to the caller, but now it does the clean-up itself.
+   (merge 8f29299 bm/merge-base-octopus-dedup later to maint).
+
+ * A "gc" process running as a different user should be able to stop a
+   new "gc" process from starting, but it didn't.
+   (merge ed7eda8 km/gc-eperm later to maint).
+
+ * An earlier "clean-up" introduced an unnecessary memory leak.
+   (merge e1c1a32 jk/credential-plug-leak later to maint).
+
+ * "git add -A" (no other arguments) in a totally empty working tree
+   used to emit an error.
+   (merge 64ed07c nd/add-empty-fix later to maint).
+
+ * "git log --decorate" did not handle a tag pointed by another tag
+   nicely.
+   (merge 5e1361c bc/log-decoration later to maint).
+
+ * When we figure out how many file descriptors to allocate for
+   keeping packfiles open, a system with non-working getrlimit() could
+   cause us to die(), but because we make this call only to get a
+   rough estimate of how many is available and we do not even attempt
+   to use up all file descriptors available ourselves, it is nicer to
+   fall back to a reasonable low value rather than dying.
+   (merge 491a8de jh/rlimit-nofile-fallback later to maint).
+
+ * read_sha1_file(), that is the workhorse to read the contents given
+   an object name, honoured object replacements, but there was no
+   corresponding mechanism to sha1_object_info() that was used to
+   obtain the metainfo (e.g. type & size) about the object.  This led
+   callers to weird inconsistencies.
+   (merge 663a856 cc/replace-object-info later to maint).
+
+ * "git cat-file --batch=", an admittedly useless command, did not
+   behave very well.
+   (merge 6554dfa jk/cat-file-regression-fix later to maint).
+
+ * "git rev-parse <revs> -- <paths>" did not implement the usual
+   disambiguation rules the commands in the "git log" family used in
+   the same way.
+   (merge 62f162f jk/rev-parse-double-dashes later to maint).
+
+ * "git mv A B/", when B does not exist as a directory, should error
+   out, but it didn't.
+   (merge c57f628 mm/mv-file-to-no-such-dir-with-slash later to maint).
+
+ * A workaround to an old bug in glibc prior to glibc 2.17 has been
+   retired; this would remove a side effect of the workaround that
+   corrupts system error messages in non-C locales.
+
+ * SSL-related options were not passed correctly to underlying socket
+   layer in "git send-email".
+   (merge 5508f3e tr/send-email-ssl later to maint).
+
+ * "git commit -v" appends the patch to the log message before
+   editing, and then removes the patch when the editor returned
+   control. However, the patch was not stripped correctly when the
+   first modified path was a submodule.
+   (merge 1a72cfd jl/commit-v-strip-marker later to maint).
+
+ * "git fetch --depth=0" was a no-op, and was silently ignored.
+   Diagnose it as an error.
+   (merge 5594bca nd/transport-positive-depth-only later to maint).
+
+ * Remote repository URL expressed in scp-style host:path notation are
+   parsed more carefully (e.g. "foo/bar:baz" is local, "[::1]:/~user" asks
+   to connect to user's home directory on host at address ::1.
+   (merge a2036d7 tb/clone-ssh-with-colon-for-port later to maint).
+
  * "git diff -- ':(icase)makefile'" was unnecessarily rejected at the
    command line parser.
    (merge 887c6c1 nd/magic-pathspec later to maint).
index 705557689d7fa46f88f936bb8c42471ffbfb052b..e6d46edbe7376a9f15d4f337a341a344b5197ca4 100644 (file)
@@ -139,8 +139,15 @@ People on the Git mailing list need to be able to read and
 comment on the changes you are submitting.  It is important for
 a developer to be able to "quote" your changes, using standard
 e-mail tools, so that they may comment on specific portions of
-your code.  For this reason, all patches should be submitted
-"inline".  If your log message (including your name on the
+your code.  For this reason, each patch should be submitted
+"inline" in a separate message.
+
+Multiple related patches should be grouped into their own e-mail
+thread to help readers find all parts of the series.  To that end,
+send them as replies to either an additional "cover letter" message
+(see below), the first patch, or the respective preceding patch.
+
+If your log message (including your name on the
 Signed-off-by line) is not writable in ASCII, make sure that
 you send off a message in the correct encoding.
 
index ab26963d61877a2f8e03a3532ace5b31bc68738e..5f4d7939ed1ec267e7f282624e5bb480e72d7a33 100644 (file)
@@ -567,6 +567,10 @@ be passed to the shell by Git, which will translate the final
 command to `LESS=FRSX less -+S`. The environment tells the command
 to set the `S` option to chop long lines but the command line
 resets it to the default to fold long lines.
++
+Likewise, when the `LV` environment variable is unset, Git sets it
+to `-c`.  You can override this setting by exporting `LV` with
+another value or setting `core.pager` to `lv +c`.
 
 core.whitespace::
        A comma separated list of common whitespace problems to
@@ -2026,6 +2030,10 @@ receive.updateserverinfo::
        If set to true, git-receive-pack will run git-update-server-info
        after receiving data from git-push and updating refs.
 
+receive.shallowupdate::
+       If set to true, .git/shallow can be updated when new refs
+       require new shallow roots. Otherwise those refs are rejected.
+
 remote.pushdefault::
        The remote to push to by default.  Overrides
        `branch.<name>.remote` for all branches, and is overridden by
@@ -2087,8 +2095,8 @@ remote.<name>.vcs::
 
 remote.<name>.prune::
        When set to true, fetching from this remote by default will also
-       remove any remote-tracking branches which no longer exist on the
-       remote (as if the `--prune` option was give on the command line).
+       remove any remote-tracking references that no longer exist on the
+       remote (as if the `--prune` option was given on the command line).
        Overrides `fetch.prune` settings, if any.
 
 remotes.<group>::
index 223b9310df0990accdb0d43b096bd52856f56bef..f07b4513ed76978e3bb7ef02ebf31d504f7098ff 100644 (file)
@@ -98,6 +98,11 @@ diff.mnemonicprefix::
 diff.noprefix::
        If set, 'git diff' does not show any source or destination prefix.
 
+diff.orderfile::
+       File indicating how to order files within a diff, using
+       one shell glob pattern per line.
+       Can be overridden by the '-O' option to linkgit:git-diff[1].
+
 diff.renameLimit::
        The number of files to consider when performing the copy/rename
        detection; equivalent to the 'git diff' option '-l'.
index bbed2cd79c6ebbb95e3b66b5f1ccaa3762d9dfce..9b37b2a10b94449b73aeceadae175d52ee081e4e 100644 (file)
@@ -432,6 +432,9 @@ endif::git-format-patch[]
 -O<orderfile>::
        Output the patch in the order specified in the
        <orderfile>, which has one shell glob pattern per line.
+       This overrides the `diff.orderfile` configuration variable
+       (see linkgit:git-config[1]).  To cancel `diff.orderfile`,
+       use `-O/dev/null`.
 
 ifndef::git-format-patch[]
 -R::
index ba1fe4958227dce5ab146a39374cf02c127c0c65..92c68c3fdabafd9ac5afe74256dbd2d56218c247 100644 (file)
        branch history. Tags for the deepened commits are not fetched.
 
 --unshallow::
-       Convert a shallow repository to a complete one, removing all
-       the limitations imposed by shallow repositories.
+       If the source repository is complete, convert a shallow
+       repository to a complete one, removing all the limitations
+       imposed by shallow repositories.
++
+If the source repository is shallow, fetch as much as possible so that
+the current repository has the same history as the source repository.
+
+--update-shallow::
+       By default when fetching from a shallow repository,
+       `git fetch` refuses refs that require updating
+       .git/shallow. This option updates .git/shallow and accept such
+       refs.
 
 ifndef::git-pull[]
 --dry-run::
@@ -41,17 +51,20 @@ ifndef::git-pull[]
 
 -p::
 --prune::
-       After fetching, remove any remote-tracking branches which
-       no longer exist on the remote.
+       After fetching, remove any remote-tracking references that no
+       longer exist on the remote.  Tags are not subject to pruning
+       if they are fetched only because of the default tag
+       auto-following or due to a --tags option.  However, if tags
+       are fetched due to an explicit refspec (either on the command
+       line or in the remote configuration, for example if the remote
+       was cloned with the --mirror option), then they are also
+       subject to pruning.
 endif::git-pull[]
 
-ifdef::git-pull[]
---no-tags::
-endif::git-pull[]
 ifndef::git-pull[]
 -n::
---no-tags::
 endif::git-pull[]
+--no-tags::
        By default, tags that point at objects that are downloaded
        from the remote repository are fetched and stored locally.
        This option disables this automatic tag following. The default
@@ -61,11 +74,12 @@ endif::git-pull[]
 ifndef::git-pull[]
 -t::
 --tags::
-       This is a short-hand for giving `refs/tags/*:refs/tags/*`
-       refspec from the command line, to ask all tags to be fetched
-       and stored locally.  Because this acts as an explicit
-       refspec, the default refspecs (configured with the
-       remote.$name.fetch variable) are overridden and not used.
+       Fetch all tags from the remote (i.e., fetch remote tags
+       `refs/tags/*` into local tags with the same name), in addition
+       to whatever else would otherwise be fetched.  Using this
+       option alone does not subject tags to pruning, even if --prune
+       is used (though tags may be pruned anyway if they are also the
+       destination of an explicit refspec; see '--prune').
 
 --recurse-submodules[=yes|on-demand|no]::
        This option controls if and under what conditions new commits of
index 322f5ed3155887ef69876bd439a12c4641007c9e..f6a16f4300b15669a518faa3d8a63d7586233258 100644 (file)
@@ -109,6 +109,11 @@ newline. The available atoms are:
        The size, in bytes, that the object takes up on disk. See the
        note about on-disk sizes in the `CAVEATS` section below.
 
+`deltabase`::
+       If the object is stored as a delta on-disk, this expands to the
+       40-hex sha1 of the delta base object. Otherwise, expands to the
+       null sha1 (40 zeroes). See `CAVEATS` below.
+
 `rest`::
        If this atom is used in the output string, input lines are split
        at the first whitespace boundary. All characters before that
@@ -152,10 +157,11 @@ should be taken in drawing conclusions about which refs or objects are
 responsible for disk usage. The size of a packed non-delta object may be
 much larger than the size of objects which delta against it, but the
 choice of which object is the base and which is the delta is arbitrary
-and is subject to change during a repack. Note also that multiple copies
-of an object may be present in the object database; in this case, it is
-undefined which copy's size will be reported.
+and is subject to change during a repack.
 
+Note also that multiple copies of an object may be present in the object
+database; in this case, it is undefined which copy's size or delta base
+will be reported.
 
 GIT
 ---
index 91294f89c89dbbfef93bbb7f38074827938f48f6..33ad2adf5ce86119e55f94fa1d29233e8caf4a67 100644 (file)
@@ -232,8 +232,8 @@ section of linkgit:git-add[1] to learn how to operate the `--patch` mode.
        commit, your HEAD becomes "detached" and you are no longer on
        any branch (see below for details).
 +
-As a special case, the `"@{-N}"` syntax for the N-th last branch
-checks out the branch (instead of detaching).  You may also specify
+As a special case, the `"@{-N}"` syntax for the N-th last branch/commit
+checks out branches (instead of detaching).  You may also specify
 `-` which is synonymous with `"@{-1}"`.
 +
 As a further special case, you may use `"A...B"` as a shortcut for the
index 2d0daae626dd8e58c5a5c72ef999e36f56bcdce7..0ea921a5931647f21f74a44a80622c3d3c16827c 100644 (file)
@@ -3,7 +3,7 @@ git-cherry(1)
 
 NAME
 ----
-git-cherry - Find commits not merged upstream
+git-cherry - Find commits yet to be applied to upstream
 
 SYNOPSIS
 --------
@@ -12,46 +12,26 @@ SYNOPSIS
 
 DESCRIPTION
 -----------
-The changeset (or "diff") of each commit between the fork-point and <head>
-is compared against each commit between the fork-point and <upstream>.
-The diffs are compared after removing any whitespace and line numbers.
+Determine whether there are commits in `<head>..<upstream>` that are
+equivalent to those in the range `<limit>..<head>`.
 
-Every commit that doesn't exist in the <upstream> branch
-has its id (sha1) reported, prefixed by a symbol.  The ones that have
-equivalent change already
-in the <upstream> branch are prefixed with a minus (-) sign, and those
-that only exist in the <head> branch are prefixed with a plus (+) symbol:
-
-               __*__*__*__*__> <upstream>
-              /
-    fork-point
-              \__+__+__-__+__+__-__+__> <head>
-
-
-If a <limit> has been given then the commits along the <head> branch up
-to and including <limit> are not reported:
-
-               __*__*__*__*__> <upstream>
-              /
-    fork-point
-              \__*__*__<limit>__-__+__> <head>
-
-
-Because 'git cherry' compares the changeset rather than the commit id
-(sha1), you can use 'git cherry' to find out if a commit you made locally
-has been applied <upstream> under a different commit id.  For example,
-this will happen if you're feeding patches <upstream> via email rather
-than pushing or pulling commits directly.
+The equivalence test is based on the diff, after removing whitespace
+and line numbers.  git-cherry therefore detects when commits have been
+"copied" by means of linkgit:git-cherry-pick[1], linkgit:git-am[1] or
+linkgit:git-rebase[1].
 
+Outputs the SHA1 of every commit in `<limit>..<head>`, prefixed with
+`-` for commits that have an equivalent in <upstream>, and `+` for
+commits that do not.
 
 OPTIONS
 -------
 -v::
-       Verbose.
+       Show the commit subjects next to the SHA1s.
 
 <upstream>::
-       Upstream branch to compare against.
-       Defaults to the first tracked remote branch, if available.
+       Upstream branch to search for equivalent commits.
+       Defaults to the upstream branch of HEAD.
 
 <head>::
        Working branch; defaults to HEAD.
@@ -59,6 +39,103 @@ OPTIONS
 <limit>::
        Do not report commits up to (and including) limit.
 
+EXAMPLES
+--------
+
+Patch workflows
+~~~~~~~~~~~~~~~
+
+git-cherry is frequently used in patch-based workflows (see
+linkgit:gitworkflows[7]) to determine if a series of patches has been
+applied by the upstream maintainer.  In such a workflow you might
+create and send a topic branch like this:
+
+------------
+$ git checkout -b topic origin/master
+# work and create some commits
+$ git format-patch origin/master
+$ git send-email ... 00*
+------------
+
+Later, you can see whether your changes have been applied by saying
+(still on `topic`):
+
+------------
+$ git fetch  # update your notion of origin/master
+$ git cherry -v
+------------
+
+Concrete example
+~~~~~~~~~~~~~~~~
+
+In a situation where topic consisted of three commits, and the
+maintainer applied two of them, the situation might look like:
+
+------------
+$ git log --graph --oneline --decorate --boundary origin/master...topic
+* 7654321 (origin/master) upstream tip commit
+[... snip some other commits ...]
+* cccc111 cherry-pick of C
+* aaaa111 cherry-pick of A
+[... snip a lot more that has happened ...]
+| * cccc000 (topic) commit C
+| * bbbb000 commit B
+| * aaaa000 commit A
+|/
+o 1234567 branch point
+------------
+
+In such cases, git-cherry shows a concise summary of what has yet to
+be applied:
+
+------------
+$ git cherry origin/master topic
+- cccc000... commit C
++ bbbb000... commit B
+- aaaa000... commit A
+------------
+
+Here, we see that the commits A and C (marked with `-`) can be
+dropped from your `topic` branch when you rebase it on top of
+`origin/master`, while the commit B (marked with `+`) still needs to
+be kept so that it will be sent to be applied to `origin/master`.
+
+
+Using a limit
+~~~~~~~~~~~~~
+
+The optional <limit> is useful in cases where your topic is based on
+other work that is not in upstream.  Expanding on the previous
+example, this might look like:
+
+------------
+$ git log --graph --oneline --decorate --boundary origin/master...topic
+* 7654321 (origin/master) upstream tip commit
+[... snip some other commits ...]
+* cccc111 cherry-pick of C
+* aaaa111 cherry-pick of A
+[... snip a lot more that has happened ...]
+| * cccc000 (topic) commit C
+| * bbbb000 commit B
+| * aaaa000 commit A
+| * 0000fff (base) unpublished stuff F
+[... snip ...]
+| * 0000aaa unpublished stuff A
+|/
+o 1234567 merge-base between upstream and topic
+------------
+
+By specifying `base` as the limit, you can avoid listing commits
+between `base` and `topic`:
+
+------------
+$ git cherry origin/master topic base
+- cccc000... commit C
++ bbbb000... commit B
+- aaaa000... commit A
+------------
+
+
 SEE ALSO
 --------
 linkgit:git-patch-id[1]
index 450f158779fbfda31c0b3f85d4768a294ef782ef..49878570bfabd3871d2c2c04cadcac52c66a448d 100644 (file)
@@ -181,12 +181,7 @@ objects from the source repository into a pack in the cloned repository.
 
 --depth <depth>::
        Create a 'shallow' clone with a history truncated to the
-       specified number of revisions.  A shallow repository has a
-       number of limitations (you cannot clone or fetch from
-       it, nor push from nor into it), but is adequate if you
-       are only interested in the recent history of a large project
-       with a long history, and would want to send in fixes
-       as patches.
+       specified number of revisions.
 
 --[no-]single-branch::
        Clone only the history leading to the tip of a single branch,
index e08a028946978882cc1a70d3ab205490fd62b2c6..5809aa4eb94e969bf700806b1ef3df504ebcfd2b 100644 (file)
@@ -24,19 +24,22 @@ The ref names and their object names of fetched refs are stored
 in `.git/FETCH_HEAD`.  This information is left for a later merge
 operation done by 'git merge'.
 
-When <refspec> stores the fetched result in remote-tracking branches,
-the tags that point at these branches are automatically
-followed.  This is done by first fetching from the remote using
-the given <refspec>s, and if the repository has objects that are
-pointed by remote tags that it does not yet have, then fetch
-those missing tags.  If the other end has tags that point at
-branches you are not interested in, you will not get them.
+By default, tags are auto-followed.  This means that when fetching
+from a remote, any tags on the remote that point to objects that exist
+in the local repository are fetched.  The effect is to fetch tags that
+point at branches that you are interested in.  This default behavior
+can be changed by using the --tags or --no-tags options, by
+configuring remote.<name>.tagopt, or by using a refspec that fetches
+tags explicitly.
 
 'git fetch' can fetch from either a single named repository,
 or from several repositories at once if <group> is given and
 there is a remotes.<group> entry in the configuration file.
 (See linkgit:git-config[1]).
 
+When no remote is specified, by default the `origin` remote will be used,
+unless there's an upstream branch configured for the current branch.
+
 OPTIONS
 -------
 include::fetch-options.txt[]
index e4c8e82660ddf9aace717e269a50ffa9adf4405e..2eba6271704adb6179a1c31d9d6974fbe70b66f4 100644 (file)
@@ -393,7 +393,7 @@ git filter-branch --index-filter \
 Checklist for Shrinking a Repository
 ------------------------------------
 
-git-filter-branch is often used to get rid of a subset of files,
+git-filter-branch can be used to get rid of a subset of files,
 usually with some combination of `--index-filter` and
 `--subdirectory-filter`.  People expect the resulting repository to
 be smaller than the original, but you need a few more steps to
@@ -429,6 +429,37 @@ warned.
   (or if your git-gc is not new enough to support arguments to
   `--prune`, use `git repack -ad; git prune` instead).
 
+Notes
+-----
+
+git-filter-branch allows you to make complex shell-scripted rewrites
+of your Git history, but you probably don't need this flexibility if
+you're simply _removing unwanted data_ like large files or passwords.
+For those operations you may want to consider
+link:http://rtyley.github.io/bfg-repo-cleaner/[The BFG Repo-Cleaner],
+a JVM-based alternative to git-filter-branch, typically at least
+10-50x faster for those use-cases, and with quite different
+characteristics:
+
+* Any particular version of a file is cleaned exactly _once_. The BFG,
+  unlike git-filter-branch, does not give you the opportunity to
+  handle a file differently based on where or when it was committed
+  within your history. This constraint gives the core performance
+  benefit of The BFG, and is well-suited to the task of cleansing bad
+  data - you don't care _where_ the bad data is, you just want it
+  _gone_.
+
+* By default The BFG takes full advantage of multi-core machines,
+  cleansing commit file-trees in parallel. git-filter-branch cleans
+  commits sequentially (ie in a single-threaded manner), though it
+  _is_ possible to write filters that include their own parallellism,
+  in the scripts executed against each commit.
+
+* The link:http://rtyley.github.io/bfg-repo-cleaner/#examples[command options]
+  are much more restrictive than git-filter branch, and dedicated just
+  to the tasks of removing unwanted data- e.g:
+  `--strip-blobs-bigger-than 1M`.
+
 GIT
 ---
 Part of the linkgit:git[1] suite
index 94f5c465da008cc8ed009c5b1e1c2b262612b380..d639abe17a0e948c3e8a7f06e3f4f42c2e69193e 100644 (file)
@@ -227,6 +227,10 @@ Documentation
 -------------
 Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
 
+SEE ALSO
+--------
+linkgit:git-show-ref[1]
+
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-lost-found.txt b/Documentation/git-lost-found.txt
deleted file mode 100644 (file)
index d549328..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-git-lost-found(1)
-=================
-
-NAME
-----
-git-lost-found - Recover lost refs that luckily have not yet been pruned
-
-SYNOPSIS
---------
-[verse]
-'git lost-found'
-
-DESCRIPTION
------------
-
-*NOTE*: this command is deprecated.  Use linkgit: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,
-and other objects are stored in .git/lost-found/other.
-
-
-OUTPUT
-------
-Prints to standard output the object names and one-line descriptions
-of any commits or tags found.
-
-EXAMPLE
--------
-
-Suppose you run 'git tag -f' and mistype the tag to overwrite.
-The ref to your tag is overwritten, but until you run 'git
-prune', the tag itself is still there.
-
-------------
-$ git lost-found
-[1ef2b196d909eed523d4f3c9bf54b78cdd6843c6] GIT 0.99.9c
-...
-------------
-
-Also you can use gitk to browse how any tags found relate to each
-other.
-
-------------
-$ gitk $(cd .git/lost-found/commit && echo ??*)
-------------
-
-After making sure you know which the object is the tag you are looking
-for, you can reconnect it to your regular `refs` hierarchy by using
-the `update-ref` command.
-
-------------
-$ git cat-file -t 1ef2b196
-tag
-$ git cat-file tag 1ef2b196
-object fa41bbce8e38c67a218415de6cfa510c7e50032a
-type commit
-tag v0.99.9c
-tagger Junio C Hamano <junkio@cox.net> 1131059594 -0800
-
-GIT 0.99.9c
-
-This contains the following changes from the "master" branch, since
-...
-$ git update-ref refs/tags/not-lost-anymore 1ef2b196
-$ git rev-parse not-lost-anymore
-1ef2b196d909eed523d4f3c9bf54b78cdd6843c6
-------------
-
-GIT
----
-Part of the linkgit:git[1] suite
index b1f79881efdd181cf996672bc47c7772f21df8fd..e4531325cd0e00000670692ee3e5a26387460d9e 100644 (file)
@@ -52,6 +52,18 @@ core.worktree setting to make the submodule work in the new location.
 It also will attempt to update the submodule.<name>.path setting in
 the linkgit:gitmodules[5] file and stage that file (unless -n is used).
 
+BUGS
+----
+Each time a superproject update moves a populated submodule (e.g. when
+switching between commits before and after the move) a stale submodule
+checkout will remain in the old location and an empty directory will
+appear in the new location. To populate the submodule again in the new
+location the user will have to run "git submodule update"
+afterwards. Removing the old directory is only safe when it uses a
+gitfile, as otherwise the history of the submodule will be deleted
+too. Both steps will be obsolete when recursive submodule update has
+been implemented.
+
 GIT
 ---
 Part of the linkgit:git[1] suite
index 8cba16d67f29ec48738b00a607add4a20bc7d4b2..6ab5f9497ab353e38c15708443527e1d047d39e8 100644 (file)
@@ -168,7 +168,8 @@ All commands except clone accept these options.
 --git-dir <dir>::
        Set the 'GIT_DIR' environment variable.  See linkgit:git[1].
 
---verbose, -v::
+-v::
+--verbose::
        Provide more progress information.
 
 Sync options
@@ -279,7 +280,8 @@ These options can be used to modify 'git p4 submit' behavior.
        Export tags from Git as p4 labels. Tags found in Git are applied
        to the perforce working directory.
 
---dry-run, -n::
+-n::
+--dry-run::
        Show just what commits would be submitted to p4; do not change
        state in Git or p4.
 
index d94edcd4b4fdd8bd57150bd0ad8f870a45e0249c..cdab9ed503a5adaace14b06863f31347725d06db 100644 (file)
@@ -51,8 +51,7 @@ base-name::
        <base-name> to determine the name of the created file.
        When this option is used, the two files are written in
        <base-name>-<SHA-1>.{pack,idx} files.  <SHA-1> is a hash
-       of the sorted object names to make the resulting filename
-       based on the pack content, and written to the standard
+       based on the pack content and is written to the standard
        output of the command.
 
 --stdout::
diff --git a/Documentation/git-peek-remote.txt b/Documentation/git-peek-remote.txt
deleted file mode 100644 (file)
index 87ea3fb..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-git-peek-remote(1)
-==================
-
-NAME
-----
-git-peek-remote - List the references in a remote repository
-
-
-SYNOPSIS
---------
-[verse]
-'git peek-remote' [--upload-pack=<git-upload-pack>] [<host>:]<directory>
-
-DESCRIPTION
------------
-This command is deprecated; use 'git ls-remote' instead.
-
-OPTIONS
--------
---upload-pack=<git-upload-pack>::
-       Use this to specify the path to 'git-upload-pack' on the
-       remote side, if it is not found on your $PATH. Some
-       installations of sshd ignores the user's environment
-       setup scripts for login shells (e.g. .bash_profile) and
-       your privately installed git may not be found on the system
-       default $PATH.  Another workaround suggested is to set
-       up your $PATH in ".bashrc", but this flag is for people
-       who do not want to pay the overhead for non-interactive
-       shells, but prefer having a lean .bashrc file (they set most of
-       the things up in .bash_profile).
-
-<host>::
-       A remote host that houses the repository.  When this
-       part is specified, 'git-upload-pack' is invoked via
-       ssh.
-
-<directory>::
-       The repository to sync from.
-
-
-GIT
----
-Part of the linkgit:git[1] suite
index bf824108c116a34972d1dacf1e9fb77db097b2c5..058ac0dc854bfd55a13dd0776beaf36b37e8b8a9 100644 (file)
@@ -24,6 +24,8 @@ objects unreachable from any of these head objects from the object database.
 In addition, it
 prunes the unpacked objects that are also found in packs by
 running 'git prune-packed'.
+It also removes entries from .git/shallow that are not reachable by
+any ref.
 
 Note that unreachable, packed objects will remain.  If this is
 not desired, see linkgit:git-repack[1].
index 6083aab87bd947c8528cbf347769b6621531b035..200eb22260069af7a9e2e248f0794a73e2cf4a9f 100644 (file)
@@ -99,10 +99,10 @@ must be given before the options meant for 'git fetch'.
 Options related to merging
 ~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-include::merge-options.txt[]
-
 :git-pull: 1
 
+include::merge-options.txt[]
+
 -r::
 --rebase[=false|true|preserve]::
        When true, rebase the current branch on top of the upstream
index 9eec74091097c0ea5c9fea69a09eb63e3cebd8d9..2b7f4f939f38626dbd796b808cc21285ad5c8c3d 100644 (file)
@@ -56,8 +56,13 @@ it can be any arbitrary "SHA-1 expression", such as `master~4` or
 +
 The <dst> tells which ref on the remote side is updated with this
 push. Arbitrary expressions cannot be used here, an actual ref must
-be named. If `:`<dst> is omitted, the same ref as <src> will be
-updated.
+be named.
+If `git push [<repository>]` without any `<refspec>` argument is set to
+update some ref at the destination with `<src>` with
+`remote.<repository>.push` configuration variable, `:<dst>` part can
+be omitted---such a push will update a ref that `<src>` normally updates
+without any `<refspec>` on the command line.  Otherwise, missing
+`:<dst>` means to update the same ref as the `<src>`.
 +
 The object referenced by <src> is used to update the <dst> reference
 on the remote side.  By default this is only allowed if <dst> is not
index 94e07fdab550dcac70734fb79724b00cf8d23edc..2889be6bdc4a1a243585e43def2e7416a9aa7038 100644 (file)
@@ -324,6 +324,16 @@ fresh commits so it can be remerged successfully without needing to "revert
 the reversion" (see the
 link:howto/revert-a-faulty-merge.html[revert-a-faulty-merge How-To] for details).
 
+--fork-point::
+--no-fork-point::
+       Use 'git merge-base --fork-point' to find a better common ancestor
+       between `upstream` and `branch` when calculating which commits have
+       have been introduced by `branch` (see linkgit:git-merge-base[1]).
++
+If no non-option arguments are given on the command line, then the default is
+`--fork-point @{u}` otherwise the `upstream` argument is interpreted literally
+unless the `--fork-point` option is specified.
+
 --ignore-whitespace::
 --whitespace=<option>::
        These flag are passed to the 'git apply' program
index f373ab48d4d575d847408e333b5bfb747d1a75a2..0a02f70657776bc5d862add34f466b4fe98f6e35 100644 (file)
@@ -10,7 +10,7 @@ SYNOPSIS
 [verse]
 'git replace' [-f] <object> <replacement>
 'git replace' -d <object>...
-'git replace' -l [<pattern>]
+'git replace' [--format=<format>] [-l [<pattern>]]
 
 DESCRIPTION
 -----------
@@ -70,6 +70,23 @@ OPTIONS
        Typing "git replace" without arguments, also lists all replace
        refs.
 
+--format=<format>::
+       When listing, use the specified <format>, which can be one of
+       'short', 'medium' and 'long'. When omitted, the format
+       defaults to 'short'.
+
+FORMATS
+-------
+
+The following format are available:
+
+* 'short':
+       <replaced sha1>
+* 'medium':
+       <replaced sha1> -> <replacement sha1>
+* 'long':
+       <replaced sha1> (<replaced type>) -> <replacement sha1> (<replacement type>)
+
 CREATING REPLACEMENT OBJECTS
 ----------------------------
 
diff --git a/Documentation/git-repo-config.txt b/Documentation/git-repo-config.txt
deleted file mode 100644 (file)
index 9ec115b..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-git-repo-config(1)
-==================
-
-NAME
-----
-git-repo-config - Get and set repository or global options
-
-
-SYNOPSIS
---------
-[verse]
-'git repo-config' ...
-
-
-DESCRIPTION
------------
-
-This is a synonym for linkgit:git-config[1].  Please refer to the
-documentation of that command.
-
-GIT
----
-Part of the linkgit:git[1] suite
index 9d731b453d1af4cbdbf559b72e8064a77696f4a3..f1efc116ebb88a3bc31898f6a18837f8b951ad09 100644 (file)
@@ -170,6 +170,15 @@ of files and subdirectories under the `Documentation/` directory.
        (i.e. you are listing the files explicitly), it
        does not remove `subdir/git-foo.sh`.
 
+BUGS
+----
+Each time a superproject update removes a populated submodule
+(e.g. when switching between commits before and after the removal) a
+stale submodule checkout will remain in the old location. Removing the
+old directory is only safe when it uses a gitfile, as otherwise the
+history of the submodule will be deleted too. This step will be
+obsolete when recursive submodule update has been implemented.
+
 SEE ALSO
 --------
 linkgit:git-add[1]
index b0a309b117bd12170ccd9194d1cf5083d5675365..ffd1b03a9c0ee583effe6200fbabd327e7ac60f6 100644 (file)
@@ -175,6 +175,7 @@ FILES
 
 SEE ALSO
 --------
+linkgit:git-for-each-ref[1],
 linkgit:git-ls-remote[1],
 linkgit:git-update-ref[1],
 linkgit:gitrepository-layout[5]
diff --git a/Documentation/git-tar-tree.txt b/Documentation/git-tar-tree.txt
deleted file mode 100644 (file)
index f7362dc..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-git-tar-tree(1)
-===============
-
-NAME
-----
-git-tar-tree - Create a tar archive of the files in the named tree object
-
-
-SYNOPSIS
---------
-[verse]
-'git tar-tree' [--remote=<repo>] <tree-ish> [ <base> ]
-
-DESCRIPTION
------------
-THIS COMMAND IS DEPRECATED.  Use 'git archive' with `--format=tar`
-option instead (and move the <base> argument to `--prefix=base/`).
-
-Creates a tar archive containing the tree structure for the named tree.
-When <base> is specified it is added as a leading path to the files in the
-generated tar archive.
-
-'git tar-tree' behaves differently when given a tree ID versus when given
-a commit ID or tag ID.  In the first case the current time is used as
-modification time of each file in the archive.  In the latter case the
-commit time as recorded in the referenced commit object is used instead.
-Additionally the commit ID is stored in a global extended pax header.
-It can be extracted using 'git get-tar-commit-id'.
-
-OPTIONS
--------
-
-<tree-ish>::
-       The tree or commit to produce tar archive for.  If it is
-       the object name of a commit object.
-
-<base>::
-       Leading path to the files in the resulting tar archive.
-
---remote=<repo>::
-       Instead of making a tar archive from local repository,
-       retrieve a tar archive from a remote repository.
-
-CONFIGURATION
--------------
-
-tar.umask::
-       This variable can be used to restrict the permission bits of
-       tar archive entries.  The default is 0002, which turns off the
-       world write bit.  The special value "user" indicates that the
-       archiving user's umask will be used instead.  See umask(2) for
-       details.
-
-EXAMPLES
---------
-`git tar-tree HEAD junk | (cd /var/tmp/ && tar xf -)`::
-
-       Create a tar archive that contains the contents of the
-       latest commit on the current branch, and extracts it in
-       `/var/tmp/junk` directory.
-
-`git tar-tree v1.4.0 git-1.4.0 | gzip >git-1.4.0.tar.gz`::
-
-       Create a tarball for v1.4.0 release.
-
-`git tar-tree v1.4.0^{tree} git-1.4.0 | gzip >git-1.4.0.tar.gz`::
-
-       Create a tarball for v1.4.0 release, but without a
-       global extended pax header.
-
-`git tar-tree --remote=example.com:git.git v1.4.0 >git-1.4.0.tar`::
-
-       Get a tarball v1.4.0 from example.com.
-
-`git tar-tree HEAD:Documentation/ git-docs > git-1.4.0-docs.tar`::
-
-       Put everything in the current head's Documentation/ directory
-       into 'git-1.4.0-docs.tar', with the prefix 'git-docs/'.
-
-GIT
----
-Part of the linkgit:git[1] suite
index 4448ce2704284bde15a8a3222545993035e56467..274d816b438137dd47a121d4f4ca2171abb0cbc2 100644 (file)
@@ -43,9 +43,11 @@ unreleased) version of Git, that is available from 'master'
 branch of the `git.git` repository.
 Documentation for older releases are available here:
 
-* link:v1.8.5.1/git.html[documentation for release 1.8.5.1]
+* link:v1.8.5.3/git.html[documentation for release 1.8.5.3]
 
 * release notes for
+  link:RelNotes/1.8.5.3.txt[1.8.5.3],
+  link:RelNotes/1.8.5.2.txt[1.8.5.2],
   link:RelNotes/1.8.5.1.txt[1.8.5.1],
   link:RelNotes/1.8.5.txt[1.8.5].
 
@@ -806,6 +808,15 @@ temporary file --- it is removed when 'GIT_EXTERNAL_DIFF' exits.
 +
 For a path that is unmerged, 'GIT_EXTERNAL_DIFF' is called with 1
 parameter, <path>.
++
+For each path 'GIT_EXTERNAL_DIFF' is called, two environment variables,
+'GIT_DIFF_PATH_COUNTER' and 'GIT_DIFF_PATH_TOTAL' are set.
+
+'GIT_DIFF_PATH_COUNTER'::
+       A 1-based counter incremented by one for every path.
+
+'GIT_DIFF_PATH_TOTAL'::
+       The total number of paths.
 
 other
 ~~~~~
index b322a2666ce38debd993e330f5ded9bf2e32b578..643c1ba9290ff10813eab57b95f7c26d8c43c7e2 100644 (file)
@@ -930,9 +930,12 @@ state.
 DEFINING MACRO ATTRIBUTES
 -------------------------
 
-Custom macro attributes can be defined only in the `.gitattributes`
-file at the toplevel (i.e. not in any subdirectory).  The built-in
-macro attribute "binary" is equivalent to:
+Custom macro attributes can be defined only in top-level gitattributes
+files (`$GIT_DIR/info/attributes`, the `.gitattributes` file at the
+top level of the working tree, or the global or system-wide
+gitattributes files), not in `.gitattributes` files in working tree
+subdirectories.  The built-in macro attribute "binary" is equivalent
+to:
 
 ------------
 [attr]binary -diff -merge -text
index f9719605123003c97bdf2999f912712536fa927a..b08d34d84ec92eec6aa54572e3823062424c23bf 100644 (file)
@@ -7,7 +7,7 @@ gitignore - Specifies intentionally untracked files to ignore
 
 SYNOPSIS
 --------
-$GIT_DIR/info/exclude, .gitignore
+$HOME/.config/git/ignore, $GIT_DIR/info/exclude, .gitignore
 
 DESCRIPTION
 -----------
@@ -79,8 +79,10 @@ PATTERN FORMAT
 
  - An optional prefix "`!`" which negates the pattern; any
    matching file excluded by a previous pattern will become
-   included again.  If a negated pattern matches, this will
-   override lower precedence patterns sources.
+   included again. It is not possible to re-include a file if a parent
+   directory of that file is excluded. Git doesn't list excluded
+   directories for performance reasons, so any patterns on contained
+   files have no effect, no matter where they are defined.
    Put a backslash ("`\`") in front of the first "`!`" for patterns
    that begin with a literal "`!`", for example, "`\!important!.txt`".
 
@@ -182,6 +184,19 @@ Another example:
 The second .gitignore prevents Git from ignoring
 `arch/foo/kernel/vmlinux.lds.S`.
 
+Example to exclude everything except a specific directory `foo/bar`
+(note the `/*` - without the slash, the wildcard would also exclude
+everything within `foo/bar`):
+
+--------------------------------------------------------------
+    $ cat .gitignore
+    # exclude everything except directory foo/bar
+    /*
+    !/foo
+    /foo/*
+    !/foo/bar
+--------------------------------------------------------------
+
 SEE ALSO
 --------
 linkgit:git-rm[1],
index d44e14c13850a1b98cae914357cc7eed7676e65f..1e9e38ae40e8396af82c2d6f47d3e8e8508a921d 100644 (file)
@@ -98,6 +98,22 @@ linkgit:git-rev-list[1] for a complete list.
        (See "History simplification" in linkgit:git-log[1] for a more
        detailed explanation.)
 
+-L<start>,<end>:<file>::
+-L:<regex>:<file>::
+
+       Trace the evolution of the line range given by "<start>,<end>"
+       (or the funcname regex <regex>) within the <file>.  You may
+       not give any pathspec limiters.  This is currently limited to
+       a walk starting from a single revision, i.e., you may only
+       give zero or one positive revision arguments.
+       You can specify this option more than once.
++
+*Note:* gitk (unlike linkgit:git-log[1]) currently only understands
+this option if you specify it "glued together" with its argument.  Do
+*not* put a space after `-L`.
++
+include::line-range-format.txt[]
+
 <revision range>::
 
        Limit the revisions to show. This can be either a single revision
index f7be93f6317c7170af642c43d107a6617eb33a18..347a9f76ee809c3691dc6580b0fba3ebde0b0e3c 100644 (file)
@@ -35,6 +35,8 @@ submodule.<name>.url::
        linkgit:git-clone[1] or (if it begins with ./ or ../) a location
        relative to the superproject's origin repository.
 
+In addition, there are a number of optional keys:
+
 submodule.<name>.update::
        Defines what to do when the submodule is updated by the superproject.
        If 'checkout' (the default), the new commit specified in the
index f1f4ca972791892126f757b2f697758e4e5fe26d..c2908db76317c91cf5502065e9c4bf1d97da19bd 100644 (file)
@@ -437,6 +437,13 @@ set by Git if the remote helper has the 'option' capability.
 'option check-connectivity' \{'true'|'false'\}::
        Request the helper to check connectivity of a clone.
 
+'option cloning \{'true'|'false'\}::
+       Notify the helper this is a clone request (i.e. the current
+       repository is guaranteed empty).
+
+'option update-shallow \{'true'|'false'\}::
+       Allow to extend .git/shallow if the new refs require it.
+
 SEE ALSO
 --------
 linkgit:git-remote[1]
index e2113d93c9d34aa0af3fd421d5f6ed9bf01ad896..db4154f9d5c84bfb1dc4453f3c3d5be1a525fc02 100644 (file)
@@ -849,6 +849,43 @@ time zones in the form of "+/-HHMM", such as "+0200".
 +
 Project specific override is not supported.
 
+extra-branch-refs::
+       List of additional directories under "refs" which are going to
+       be used as branch refs. For example if you have a gerrit setup
+       where all branches under refs/heads/ are official,
+       push-after-review ones and branches under refs/sandbox/,
+       refs/wip and refs/other are user ones where permissions are
+       much wider, then you might want to set this variable as
+       follows:
++
+--------------------------------------------------------------------------------
+$feature{'extra-branch-refs'}{'default'} =
+       ['sandbox', 'wip', 'other'];
+--------------------------------------------------------------------------------
++
+This feature can be configured on per-repository basis after setting
+$feature{'extra-branch-refs'}{'override'} to true, via repository's
+`gitweb.extraBranchRefs` configuration variable, which contains a
+space separated list of refs. An example:
++
+--------------------------------------------------------------------------------
+[gitweb]
+       extraBranchRefs = sandbox wip other
+--------------------------------------------------------------------------------
++
+The gitweb.extraBranchRefs is actually a multi-valued configuration
+variable, so following example is also correct and the result is the
+same as of the snippet above:
++
+--------------------------------------------------------------------------------
+[gitweb]
+       extraBranchRefs = sandbox
+       extraBranchRefs = wip other
+--------------------------------------------------------------------------------
++
+It is an error to specify a ref that does not pass "git check-ref-format"
+scrutiny. Duplicated values are filtered.
+
 
 EXAMPLES
 --------
index afba8d4f3b2dcc952310c270daf26e01f8b30800..e1343155fad7b2709d2f3700ea3d5b4c4d1a7a89 100644 (file)
@@ -14,9 +14,12 @@ inspect and further tweak the merge result before committing.
        further edit the auto-generated merge message, so that the user
        can explain and justify the merge. The `--no-edit` option can be
        used to accept the auto-generated message (this is generally
-       discouraged). The `--edit` (or `-e`) option is still useful if you are
-       giving a draft message with the `-m` option from the command line
-       and want to edit it in the editor.
+       discouraged).
+ifndef::git-pull[]
+The `--edit` (or `-e`) option is still useful if you are
+giving a draft message with the `-m` option from the command line
+and want to edit it in the editor.
+endif::git-pull[]
 +
 Older scripts may depend on the historical behaviour of not allowing the
 user to edit the merge log message. They will see an editor opened when
index 49a9a7d53f5836ecf2c5bcadb30f0d314414afe0..fb6e593e7c6f287612f30be6206c0492d77e38d3 100644 (file)
@@ -113,3 +113,11 @@ subtree::
        match the tree structure of A, instead of reading the trees at
        the same level. This adjustment is also done to the common
        ancestor tree.
+
+With the strategies that use 3-way merge (including the default, 'recursive'),
+if a change is made on both branches, but later reverted on one of the
+branches, that change will be present in the merged result; some people find
+this behavior confusing.  It occurs because only the heads and the merge base
+are considered when performing a merge, not the individual commits.  The merge
+algorithm therefore considers the reverted change as no change at all, and
+substitutes the changed version instead.
index 2c06ed34ad2ed72f9cf856fb617e54a1956da599..5a286d0d61ffc5b3daa00d7d550d5931a91b35ec 100644 (file)
@@ -88,7 +88,7 @@ some output processing may assume ref names in UTF-8.
   branch 'blabla' then '@\{1\}' means the same as 'blabla@\{1\}'.
 
 '@\{-<n>\}', e.g. '@\{-1\}'::
-  The construct '@\{-<n>\}' means the <n>th branch checked out
+  The construct '@\{-<n>\}' means the <n>th branch/commit checked out
   before the current one.
 
 '<branchname>@\{upstream\}', e.g. 'master@\{upstream\}', '@\{u\}'::
index f3c1357b7cc037681b5d0f28974b199e326f8c2d..e3d6e7a79a6c2136a796c5409d16feb8058e96d2 100644 (file)
@@ -14,8 +14,8 @@ Git:
 
 . Add the external declaration for the function to `builtin.h`.
 
-. Add the command to `commands[]` table in `handle_internal_command()`,
-  defined in `git.c`.  The entry should look like:
+. Add the command to the `commands[]` table defined in `git.c`.
+  The entry should look like:
 
        { "foo", cmd_foo, <options> },
 +
index 4be87768f62c004d22e2e28603e9e17625aa93b9..5d245aa9d1b1654749ee51495e9eebda261c3681 100644 (file)
@@ -58,16 +58,16 @@ default remote, given the current branch and configuration.
 struct refspec
 --------------
 
-A struct refspec holds the parsed interpretation of a refspec. If it
-will force updates (starts with a '+'), force is true. If it is a
-pattern (sides end with '*') pattern is true. src and dest are the two
-sides (if a pattern, only the part outside of the wildcards); if there
-is only one side, it is src, and dst is NULL; if sides exist but are
-empty (i.e., the refspec either starts or ends with ':'), the
-corresponding side is "".
-
-This parsing can be done to an array of strings to give an array of
-struct refpsecs with parse_ref_spec().
+A struct refspec holds the parsed interpretation of a refspec.  If it
+will force updates (starts with a '+'), force is true.  If it is a
+pattern (sides end with '*') pattern is true.  src and dest are the
+two sides (including '*' characters if present); if there is only one
+side, it is src, and dst is NULL; if sides exist but are empty (i.e.,
+the refspec either starts or ends with ':'), the corresponding side is
+"".
+
+An array of strings can be parsed into an array of struct refspecs
+using parse_fetch_refspec() or parse_push_refspec().
 
 remote_find_tracking(), given a remote and a struct refspec with
 either src or dst filled out, will fill out the other such that the
index b7bd95152ea2526cf000911d81b02fe46539816f..95a07db6e82b5f5f3261d75e5e97fb4b674580a8 100644 (file)
@@ -1,5 +1,5 @@
-        Concerning Git's Packing Heuristics
-        ===================================
+Concerning Git's Packing Heuristics
+===================================
 
         Oh, here's a really stupid question:
 
index b898e97988311fe411c02f9f3fe45608e862a71a..c73b62f5e1ced76d783dcd5e7aef64016ca1fb4c 100644 (file)
@@ -161,6 +161,7 @@ MUST peel the ref if it's an annotated tag.
 
 ----
   advertised-refs  =  (no-refs / list-of-refs)
+                     *shallow
                      flush-pkt
 
   no-refs          =  PKT-LINE(zero-id SP "capabilities^{}"
@@ -174,6 +175,8 @@ MUST peel the ref if it's an annotated tag.
   other-tip        =  obj-id SP refname LF
   other-peeled     =  obj-id SP refname "^{}" LF
 
+  shallow          =  PKT-LINE("shallow" SP obj-id)
+
   capability-list  =  capability *(SP capability)
   capability       =  1*(LC_ALPHA / DIGIT / "-" / "_")
   LC_ALPHA         =  %x61-7A
@@ -461,7 +464,9 @@ contain all the objects that the server will need to complete the new
 references.
 
 ----
-  update-request    =  command-list [pack-file]
+  update-request    =  *shallow command-list [pack-file]
+
+  shallow           =  PKT-LINE("shallow" SP obj-id)
 
   command-list      =  PKT-LINE(command NUL capability-list LF)
                       *PKT-LINE(command LF)
index fd8ffa5df38c8dd1a682ab77327654a4c2b80cd4..e3e792476e7a6b7554582469b2c5ac172b2f17dd 100644 (file)
@@ -72,14 +72,29 @@ interleaved with S-R-Q.
 thin-pack
 ---------
 
-This capability means that the server can send a 'thin' pack, a pack
-which does not contain base objects; if those base objects are available
-on client side. Client requests 'thin-pack' capability when it
-understands how to "thicken" it by adding required delta bases making
-it self-contained.
-
-Client MUST NOT request 'thin-pack' capability if it cannot turn a thin
-pack into a self-contained pack.
+A thin pack is one with deltas which reference base objects not
+contained within the pack (but are known to exist at the receiving
+end). This can reduce the network traffic significantly, but it
+requires the receiving end to know how to "thicken" these packs by
+adding the missing bases to the pack.
+
+The upload-pack server advertises 'thin-pack' when it can generate
+and send a thin pack. A client requests the 'thin-pack' capability
+when it understands how to "thicken" it, notifying the server that
+it can receive such a pack. A client MUST NOT request the
+'thin-pack' capability if it cannot turn a thin pack into a
+self-contained pack.
+
+Receive-pack, on the other hand, is assumed by default to be able to
+handle thin packs, but can ask the client not to use the feature by
+advertising the 'no-thin' capability. A client MUST NOT send a thin
+pack if the server advertises the 'no-thin' capability.
+
+The reasons for this asymmetry are historical. The receive-pack
+program did not exist until after the invention of thin packs, so
+historically the reference implementation of receive-pack always
+understood thin packs. Adding 'no-thin' later allowed receive-pack
+to disable the feature in a backwards-compatible manner.
 
 
 side-band, side-band-64k
index cbb01a1ea2e05c7adb1be3d5fde1261ef9ff71e5..248dcabd50fb1fb844eaf27090b2f7f45bb0e661 100644 (file)
@@ -1,5 +1,5 @@
 Git User Manual
-_______________
+===============
 
 Git is a fast distributed revision control system.
 
index b9532f0ce4e308ccbe92ebc929a596c8f8589084..e66a5abb0b83d073f254dab178b79fdae00be7b2 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v1.8.5.GIT
+DEF_VER=v1.9-rc0
 
 LF='
 '
index 67f1754903b9220c19597f17f26e03a58681dfd2..99b2b8931a882afcc280b7098b3a916559eb39cb 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -452,7 +452,6 @@ SCRIPT_SH += git-am.sh
 SCRIPT_SH += git-bisect.sh
 SCRIPT_SH += git-difftool--helper.sh
 SCRIPT_SH += git-filter-branch.sh
-SCRIPT_SH += git-lost-found.sh
 SCRIPT_SH += git-merge-octopus.sh
 SCRIPT_SH += git-merge-one-file.sh
 SCRIPT_SH += git-merge-resolve.sh
@@ -587,11 +586,8 @@ BUILT_INS += git-cherry$X
 BUILT_INS += git-cherry-pick$X
 BUILT_INS += git-format-patch$X
 BUILT_INS += git-fsck-objects$X
-BUILT_INS += git-get-tar-commit-id$X
 BUILT_INS += git-init$X
 BUILT_INS += git-merge-subtree$X
-BUILT_INS += git-peek-remote$X
-BUILT_INS += git-repo-config$X
 BUILT_INS += git-show$X
 BUILT_INS += git-stage$X
 BUILT_INS += git-status$X
@@ -932,6 +928,7 @@ BUILTIN_OBJS += builtin/fmt-merge-msg.o
 BUILTIN_OBJS += builtin/for-each-ref.o
 BUILTIN_OBJS += builtin/fsck.o
 BUILTIN_OBJS += builtin/gc.o
+BUILTIN_OBJS += builtin/get-tar-commit-id.o
 BUILTIN_OBJS += builtin/grep.o
 BUILTIN_OBJS += builtin/hash-object.o
 BUILTIN_OBJS += builtin/help.o
@@ -983,7 +980,6 @@ BUILTIN_OBJS += builtin/show-ref.o
 BUILTIN_OBJS += builtin/stripspace.o
 BUILTIN_OBJS += builtin/symbolic-ref.o
 BUILTIN_OBJS += builtin/tag.o
-BUILTIN_OBJS += builtin/tar-tree.o
 BUILTIN_OBJS += builtin/unpack-file.o
 BUILTIN_OBJS += builtin/unpack-objects.o
 BUILTIN_OBJS += builtin/update-index.o
@@ -1777,7 +1773,7 @@ $(SCRIPT_LIB) : % : %.sh GIT-SCRIPT-DEFINES
 
 git.res: git.rc GIT-VERSION-FILE
        $(QUIET_RC)$(RC) \
-         $(join -DMAJOR= -DMINOR= -DPATCH=, $(wordlist 1,3,$(subst -, ,$(subst ., ,$(GIT_VERSION))))) \
+         $(join -DMAJOR= -DMINOR=, $(wordlist 1,2,$(subst -, ,$(subst ., ,$(GIT_VERSION))))) \
          -DGIT_VERSION="\\\"$(GIT_VERSION)\\\"" $< -o $@
 
 ifndef NO_PERL
index e390994abff32054452e878484e79c249474389f..ca33558a91c5259a793fc56b571fa683e16b134f 100644 (file)
--- a/abspath.c
+++ b/abspath.c
@@ -143,7 +143,7 @@ static const char *real_path_internal(const char *path, int die_on_error)
 error_out:
        free(last_elem);
        if (*cwd && chdir(cwd))
-               die_errno ("Could not change back to '%s'", cwd);
+               die_errno("Could not change back to '%s'", cwd);
 
        return retval;
 }
@@ -215,23 +215,25 @@ const char *absolute_path(const char *path)
  */
 const char *prefix_filename(const char *pfx, int pfx_len, const char *arg)
 {
-       static char path[PATH_MAX];
+       static struct strbuf path = STRBUF_INIT;
 #ifndef GIT_WINDOWS_NATIVE
        if (!pfx_len || is_absolute_path(arg))
                return arg;
-       memcpy(path, pfx, pfx_len);
-       strcpy(path + pfx_len, arg);
+       strbuf_reset(&path);
+       strbuf_add(&path, pfx, pfx_len);
+       strbuf_addstr(&path, arg);
 #else
        char *p;
        /* don't add prefix to absolute paths, but still replace '\' by '/' */
+       strbuf_reset(&path);
        if (is_absolute_path(arg))
                pfx_len = 0;
        else if (pfx_len)
-               memcpy(path, pfx, pfx_len);
-       strcpy(path + pfx_len, arg);
-       for (p = path + pfx_len; *p; p++)
+               strbuf_add(&path, pfx, pfx_len);
+       strbuf_addstr(&path, arg);
+       for (p = path.buf + pfx_len; *p; p++)
                if (*p == '\\')
                        *p = '/';
 #endif
-       return path;
+       return path.buf;
 }
diff --git a/alias.c b/alias.c
index 9938f03c255029ee8f5b10623e5e7bb65f2d7921..5efc3d69866731d0f56464c522b00f253f84c187 100644 (file)
--- a/alias.c
+++ b/alias.c
@@ -5,7 +5,7 @@ static char *alias_val;
 
 static int alias_lookup_cb(const char *k, const char *v, void *cb)
 {
-       if (!prefixcmp(k, "alias.") && !strcmp(k + 6, alias_key)) {
+       if (starts_with(k, "alias.") && !strcmp(k + 6, alias_key)) {
                if (!v)
                        return config_error_nonbool(k);
                alias_val = xstrdup(v);
diff --git a/attr.c b/attr.c
index 0e774c6f22f9fd6c7e27490e5bed5be1f9f10de5..8d13d70e80d6d52794d53ed071b8800f9c905a05 100644 (file)
--- a/attr.c
+++ b/attr.c
@@ -211,7 +211,7 @@ static struct match_attr *parse_attr_line(const char *line, const char *src,
        name = cp;
        namelen = strcspn(name, blank);
        if (strlen(ATTRIBUTE_MACRO_PREFIX) < namelen &&
-           !prefixcmp(name, ATTRIBUTE_MACRO_PREFIX)) {
+           starts_with(name, ATTRIBUTE_MACRO_PREFIX)) {
                if (!macro_ok) {
                        fprintf(stderr, "%s not allowed: %s:%d\n",
                                name, src, lineno);
index 1e46a4f50e16f059fa768cecc4c8331a88997021..37200b41f1dce3359c491039aeebea0108597833 100644 (file)
--- a/bisect.c
+++ b/bisect.c
@@ -406,9 +406,9 @@ static int register_ref(const char *refname, const unsigned char *sha1,
        if (!strcmp(refname, "bad")) {
                current_bad_sha1 = xmalloc(20);
                hashcpy(current_bad_sha1, sha1);
-       } else if (!prefixcmp(refname, "good-")) {
+       } else if (starts_with(refname, "good-")) {
                sha1_array_append(&good_revs, sha1);
-       } else if (!prefixcmp(refname, "skip-")) {
+       } else if (starts_with(refname, "skip-")) {
                sha1_array_append(&skipped_revs, sha1);
        }
 
index 9e6c68edaf90f27cf447ad1522da6d7de7827141..723a36bc540e41fe30b7b4fc2c8e893c4744ef3a 100644 (file)
--- a/branch.c
+++ b/branch.c
@@ -50,7 +50,7 @@ static int should_setup_rebase(const char *origin)
 void install_branch_config(int flag, const char *local, const char *origin, const char *remote)
 {
        const char *shortname = remote + 11;
-       int remote_is_branch = !prefixcmp(remote, "refs/heads/");
+       int remote_is_branch = starts_with(remote, "refs/heads/");
        struct strbuf key = STRBUF_INIT;
        int rebasing = should_setup_rebase(origin);
 
@@ -272,7 +272,7 @@ void create_branch(const char *head,
                break;
        case 1:
                /* Unique completion -- good, only if it is a real branch */
-               if (prefixcmp(real_ref, "refs/heads/") &&
+               if (!starts_with(real_ref, "refs/heads/") &&
                    validate_remote_tracking_branch(real_ref)) {
                        if (explicit_tracking)
                                die(_(upstream_not_branch), start_name);
index b56cf07abc488909a2a38c0b0a7db988902aba96..c47c110e0f18f3183d54c9afb63cffef686a21db 100644 (file)
--- a/builtin.h
+++ b/builtin.h
@@ -27,6 +27,8 @@ extern int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
 
 extern int textconv_object(const char *path, unsigned mode, const unsigned char *sha1, int sha1_valid, char **buf, unsigned long *buf_size);
 
+extern int is_builtin(const char *s);
+
 extern int cmd_add(int argc, const char **argv, const char *prefix);
 extern int cmd_annotate(int argc, const char **argv, const char *prefix);
 extern int cmd_apply(int argc, const char **argv, const char *prefix);
@@ -103,7 +105,6 @@ extern int cmd_remote(int argc, const char **argv, const char *prefix);
 extern int cmd_remote_ext(int argc, const char **argv, const char *prefix);
 extern int cmd_remote_fd(int argc, const char **argv, const char *prefix);
 extern int cmd_repack(int argc, const char **argv, const char *prefix);
-extern int cmd_repo_config(int argc, const char **argv, const char *prefix);
 extern int cmd_rerere(int argc, const char **argv, const char *prefix);
 extern int cmd_reset(int argc, const char **argv, const char *prefix);
 extern int cmd_rev_list(int argc, const char **argv, const char *prefix);
index 0df73ae73596546966af18e7076d9a6f54964786..2a2722fa10aa981695b21984e7c425c35b4f18d0 100644 (file)
@@ -547,7 +547,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
                        const char *path = pathspec.items[i].match;
                        if (pathspec.items[i].magic & PATHSPEC_EXCLUDE)
                                continue;
-                       if (!seen[i] &&
+                       if (!seen[i] && path[0] &&
                            ((pathspec.items[i].magic &
                              (PATHSPEC_GLOB | PATHSPEC_ICASE)) ||
                             !file_exists(path))) {
index ef32e4f62415956aa8e2366589d4cd6200d3bf37..b0d0986226ccb7fa06b08bacba6269b8834e8dd6 100644 (file)
@@ -1409,10 +1409,10 @@ static void recount_diff(const char *line, int size, struct fragment *fragment)
                case '\\':
                        continue;
                case '@':
-                       ret = size < 3 || prefixcmp(line, "@@ ");
+                       ret = size < 3 || !starts_with(line, "@@ ");
                        break;
                case 'd':
-                       ret = size < 5 || prefixcmp(line, "diff ");
+                       ret = size < 5 || !starts_with(line, "diff ");
                        break;
                default:
                        ret = -1;
@@ -1798,11 +1798,11 @@ static struct fragment *parse_binary_hunk(char **buf_p,
 
        *status_p = 0;
 
-       if (!prefixcmp(buffer, "delta ")) {
+       if (starts_with(buffer, "delta ")) {
                patch_method = BINARY_DELTA_DEFLATED;
                origlen = strtoul(buffer + 6, NULL, 10);
        }
-       else if (!prefixcmp(buffer, "literal ")) {
+       else if (starts_with(buffer, "literal ")) {
                patch_method = BINARY_LITERAL_DEFLATED;
                origlen = strtoul(buffer + 8, NULL, 10);
        }
@@ -3627,12 +3627,12 @@ static int preimage_sha1_in_gitlink_patch(struct patch *p, unsigned char sha1[20
            hunk->oldpos == 1 && hunk->oldlines == 1 &&
            /* does preimage begin with the heading? */
            (preimage = memchr(hunk->patch, '\n', hunk->size)) != NULL &&
-           !prefixcmp(++preimage, heading) &&
+           starts_with(++preimage, heading) &&
            /* does it record full SHA-1? */
            !get_sha1_hex(preimage + sizeof(heading) - 1, sha1) &&
            preimage[sizeof(heading) + 40 - 1] == '\n' &&
            /* does the abbreviated name on the index line agree with it? */
-           !prefixcmp(preimage + sizeof(heading) - 1, p->old_sha1_prefix))
+           starts_with(preimage + sizeof(heading) - 1, p->old_sha1_prefix))
                return 0; /* it all looks fine */
 
        /* we may have full object name on the index line */
index 49178f159e246c9a99805d806fa5f096337e8d2f..a1e3b940c25ac20927d4daefa417d5b626e38a24 100644 (file)
@@ -57,9 +57,9 @@ static int run_remote_archiver(int argc, const char **argv,
        if (!buf)
                die(_("git archive: expected ACK/NAK, got EOF"));
        if (strcmp(buf, "ACK")) {
-               if (!prefixcmp(buf, "NACK "))
+               if (starts_with(buf, "NACK "))
                        die(_("git archive: NACK %s"), buf + 5);
-               if (!prefixcmp(buf, "ERR "))
+               if (starts_with(buf, "ERR "))
                        die(_("remote error: %s"), buf + 4);
                die(_("git archive: protocol error"));
        }
index 4916eb2bd26bed670aa3662e790abad9b336ebe3..e44a6bb30a5a5299c6d57150dd2f23454b25891a 100644 (file)
@@ -1803,17 +1803,17 @@ static int prepare_lines(struct scoreboard *sb)
 static int read_ancestry(const char *graft_file)
 {
        FILE *fp = fopen(graft_file, "r");
-       char buf[1024];
+       struct strbuf buf = STRBUF_INIT;
        if (!fp)
                return -1;
-       while (fgets(buf, sizeof(buf), fp)) {
+       while (!strbuf_getwholeline(&buf, fp, '\n')) {
                /* The format is just "Commit Parent1 Parent2 ...\n" */
-               int len = strlen(buf);
-               struct commit_graft *graft = read_graft_line(buf, len);
+               struct commit_graft *graft = read_graft_line(buf.buf, buf.len);
                if (graft)
                        register_commit_graft(graft, 0);
        }
        fclose(fp);
+       strbuf_release(&buf);
        return 0;
 }
 
index 636a16ea4e3af71aef80ba42639f0933c286f485..b4d771673e7e9dccacbae8b29f4f1cd6a835151f 100644 (file)
@@ -81,13 +81,13 @@ static int parse_branch_color_slot(const char *var, int ofs)
 
 static int git_branch_config(const char *var, const char *value, void *cb)
 {
-       if (!prefixcmp(var, "column."))
+       if (starts_with(var, "column."))
                return git_column_config(var, value, "branch", &colopts);
        if (!strcmp(var, "color.branch")) {
                branch_use_color = git_config_colorbool(var, value);
                return 0;
        }
-       if (!prefixcmp(var, "color.branch.")) {
+       if (starts_with(var, "color.branch.")) {
                int slot = parse_branch_color_slot(var, 13);
                if (slot < 0)
                        return 0;
@@ -868,7 +868,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
        if (!strcmp(head, "HEAD")) {
                detached = 1;
        } else {
-               if (prefixcmp(head, "refs/heads/"))
+               if (!starts_with(head, "refs/heads/"))
                        die(_("HEAD not found below refs/heads!"));
                head += 11;
        }
index b2ca775a80f54fcceba344d5545fe3cb34ba0184..d5a93e0e911ccbe4560a03ed78436bfb174e0f68 100644 (file)
@@ -118,6 +118,7 @@ struct expand_data {
        unsigned long size;
        unsigned long disk_size;
        const char *rest;
+       unsigned char delta_base_sha1[20];
 
        /*
         * If mark_query is true, we do not expand anything, but rather
@@ -174,6 +175,11 @@ static void expand_atom(struct strbuf *sb, const char *atom, int len,
                        data->split_on_whitespace = 1;
                else if (data->rest)
                        strbuf_addstr(sb, data->rest);
+       } else if (is_atom("deltabase", atom, len)) {
+               if (data->mark_query)
+                       data->info.delta_base_sha1 = data->delta_base_sha1;
+               else
+                       strbuf_addstr(sb, sha1_to_hex(data->delta_base_sha1));
        } else
                die("unknown format element: %.*s", len, atom);
 }
@@ -193,25 +199,28 @@ static size_t expand_format(struct strbuf *sb, const char *start, void *data)
        return end - start + 1;
 }
 
-static void print_object_or_die(int fd, const unsigned char *sha1,
-                               enum object_type type, unsigned long size)
+static void print_object_or_die(int fd, struct expand_data *data)
 {
-       if (type == OBJ_BLOB) {
+       const unsigned char *sha1 = data->sha1;
+
+       assert(data->info.typep);
+
+       if (data->type == OBJ_BLOB) {
                if (stream_blob_to_fd(fd, sha1, NULL, 0) < 0)
                        die("unable to stream %s to stdout", sha1_to_hex(sha1));
        }
        else {
-               enum object_type rtype;
-               unsigned long rsize;
+               enum object_type type;
+               unsigned long size;
                void *contents;
 
-               contents = read_sha1_file(sha1, &rtype, &rsize);
+               contents = read_sha1_file(sha1, &type, &size);
                if (!contents)
                        die("object %s disappeared", sha1_to_hex(sha1));
-               if (rtype != type)
+               if (type != data->type)
                        die("object %s changed type!?", sha1_to_hex(sha1));
-               if (rsize != size)
-                       die("object %s change size!?", sha1_to_hex(sha1));
+               if (data->info.sizep && size != data->size)
+                       die("object %s changed size!?", sha1_to_hex(sha1));
 
                write_or_die(fd, contents, size);
                free(contents);
@@ -238,7 +247,7 @@ static int batch_one_object(const char *obj_name, struct batch_options *opt,
                return 0;
        }
 
-       if (sha1_object_info_extended(data->sha1, &data->info) < 0) {
+       if (sha1_object_info_extended(data->sha1, &data->info, LOOKUP_REPLACE_OBJECT) < 0) {
                printf("%s missing\n", obj_name);
                fflush(stdout);
                return 0;
@@ -250,7 +259,7 @@ static int batch_one_object(const char *obj_name, struct batch_options *opt,
        strbuf_release(&buf);
 
        if (opt->print_contents) {
-               print_object_or_die(1, data->sha1, data->type, data->size);
+               print_object_or_die(1, data);
                write_or_die(1, "\n", 1);
        }
        return 0;
@@ -274,6 +283,13 @@ static int batch_objects(struct batch_options *opt)
        strbuf_expand(&buf, opt->format, expand_format, &data);
        data.mark_query = 0;
 
+       /*
+        * If we are printing out the object, then always fill in the type,
+        * since we will want to decide whether or not to stream.
+        */
+       if (opt->print_contents)
+               data.info.typep = &data.type;
+
        /*
         * We are going to call get_sha1 on a potentially very large number of
         * objects. In most large cases, these will be actual object sha1s. The
index 904fd715f0f27af0ac3694209ba3ffe61f174fa8..5df3837e3102e2f7d432ec11bb4685535bfaa0ac 100644 (file)
@@ -781,7 +781,7 @@ static int switch_branches(const struct checkout_opts *opts,
        if (!(flag & REF_ISSYMREF))
                old.path = NULL;
 
-       if (old.path && !prefixcmp(old.path, "refs/heads/"))
+       if (old.path && starts_with(old.path, "refs/heads/"))
                old.name = old.path + strlen("refs/heads/");
 
        if (!new->name) {
@@ -816,7 +816,7 @@ static int git_checkout_config(const char *var, const char *value, void *cb)
                return 0;
        }
 
-       if (!prefixcmp(var, "submodule."))
+       if (starts_with(var, "submodule."))
                return parse_submodule_config_option(var, value);
 
        return git_xmerge_config(var, value, NULL);
@@ -1151,9 +1151,9 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
                const char *argv0 = argv[0];
                if (!argc || !strcmp(argv0, "--"))
                        die (_("--track needs a branch name"));
-               if (!prefixcmp(argv0, "refs/"))
+               if (starts_with(argv0, "refs/"))
                        argv0 += 5;
-               if (!prefixcmp(argv0, "remotes/"))
+               if (starts_with(argv0, "remotes/"))
                        argv0 += 8;
                argv0 = strchr(argv0, '/');
                if (!argv0 || !argv0[1])
index 615cd57caf1d4cbeafc73e7037bae81a6915df59..2f26297142fde89ec842d4e01665b3ebb24a9b59 100644 (file)
@@ -100,7 +100,7 @@ static int parse_clean_color_slot(const char *var)
 
 static int git_clean_config(const char *var, const char *value, void *cb)
 {
-       if (!prefixcmp(var, "column."))
+       if (starts_with(var, "column."))
                return git_column_config(var, value, "clean", &colopts);
 
        /* honors the color.interactive* config variables which also
@@ -109,7 +109,7 @@ static int git_clean_config(const char *var, const char *value, void *cb)
                clean_use_color = git_config_colorbool(var, value);
                return 0;
        }
-       if (!prefixcmp(var, "color.interactive.")) {
+       if (starts_with(var, "color.interactive.")) {
                int slot = parse_clean_color_slot(var +
                                                  strlen("color.interactive."));
                if (slot < 0)
index 874e0fd0b6e3ea4882783c0b2377016d93bd989d..43e772ccdbaba3172f7ee1a969f92b300cb9d5a1 100644 (file)
@@ -252,6 +252,12 @@ static int add_one_reference(struct string_list_item *item, void *cb_data)
                die(_("reference repository '%s' is not a local repository."),
                    item->string);
 
+       if (!access(mkpath("%s/shallow", ref_git), F_OK))
+               die(_("reference repository '%s' is shallow"), item->string);
+
+       if (!access(mkpath("%s/info/grafts", ref_git), F_OK))
+               die(_("reference repository '%s' is grafted"), item->string);
+
        strbuf_addf(&alternate, "%s/objects", ref_git);
        add_to_alternates_file(alternate.buf);
        strbuf_release(&alternate);
@@ -508,9 +514,9 @@ static void write_followtags(const struct ref *refs, const char *msg)
 {
        const struct ref *ref;
        for (ref = refs; ref; ref = ref->next) {
-               if (prefixcmp(ref->name, "refs/tags/"))
+               if (!starts_with(ref->name, "refs/tags/"))
                        continue;
-               if (!suffixcmp(ref->name, "^{}"))
+               if (ends_with(ref->name, "^{}"))
                        continue;
                if (!has_sha1_file(ref->old_sha1))
                        continue;
@@ -578,7 +584,7 @@ static void update_remote_refs(const struct ref *refs,
 static void update_head(const struct ref *our, const struct ref *remote,
                        const char *msg)
 {
-       if (our && !prefixcmp(our->name, "refs/heads/")) {
+       if (our && starts_with(our->name, "refs/heads/")) {
                /* Local default branch link */
                create_symref("HEAD", our->name, NULL);
                if (!option_bare) {
@@ -625,7 +631,7 @@ static int checkout(void)
                if (advice_detached_head)
                        detach_advice(sha1_to_hex(sha1));
        } else {
-               if (prefixcmp(head, "refs/heads/"))
+               if (!starts_with(head, "refs/heads/"))
                        die(_("HEAD not found below refs/heads!"));
        }
        free(head);
@@ -791,11 +797,22 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        else
                repo = repo_name;
        is_local = option_local != 0 && path && !is_bundle;
-       if (is_local && option_depth)
-               warning(_("--depth is ignored in local clones; use file:// instead."));
+       if (is_local) {
+               if (option_depth)
+                       warning(_("--depth is ignored in local clones; use file:// instead."));
+               if (!access(mkpath("%s/shallow", path), F_OK)) {
+                       if (option_local > 0)
+                               warning(_("source repository is shallow, ignoring --local"));
+                       is_local = 0;
+               }
+       }
        if (option_local > 0 && !is_local)
                warning(_("--local is ignored"));
 
+       /* no need to be strict, transport_set_option() will validate it again */
+       if (option_depth && atoi(option_depth) < 1)
+               die(_("depth %s is not a positive number"), option_depth);
+
        if (argc == 2)
                dir = xstrdup(argv[1]);
        else
@@ -883,6 +900,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 
        remote = remote_get(option_origin);
        transport = transport_get(remote, remote->url[0]);
+       transport->cloning = 1;
 
        if (!transport->get_refs_list || (!is_local && !transport->fetch))
                die(_("Don't know how to clone %s"), transport->url);
index e125a55fc9de2b8f6d82fd5574f109b3d245934c..75818520e1b74de607620e48d2d3afb131c6e896 100644 (file)
@@ -34,7 +34,7 @@ int cmd_column(int argc, const char **argv, const char *prefix)
        };
 
        /* This one is special and must be the first one */
-       if (argc > 1 && !prefixcmp(argv[1], "--command=")) {
+       if (argc > 1 && starts_with(argv[1], "--command=")) {
                command = argv[1] + 10;
                git_config(column_config, (void *)command);
        } else
index e89c519192121d5386ba42b88a0157b31d57c690..3767478c6ddb02cf8a4a418f8911102aeaacfbe8 100644 (file)
@@ -733,7 +733,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                                eol = nl - sb.buf;
                        else
                                eol = sb.len;
-                       if (!prefixcmp(sb.buf + previous, "\nConflicts:\n")) {
+                       if (starts_with(sb.buf + previous, "\nConflicts:\n")) {
                                ignore_footer = sb.len - previous;
                                break;
                        }
@@ -904,7 +904,7 @@ static int rest_is_empty(struct strbuf *sb, int start)
                        eol = sb->len;
 
                if (strlen(sign_off_header) <= eol - i &&
-                   !prefixcmp(sb->buf + i, sign_off_header)) {
+                   starts_with(sb->buf + i, sign_off_header)) {
                        i = eol;
                        continue;
                }
@@ -1183,7 +1183,7 @@ static int git_status_config(const char *k, const char *v, void *cb)
 {
        struct wt_status *s = cb;
 
-       if (!prefixcmp(k, "column."))
+       if (starts_with(k, "column."))
                return git_column_config(k, v, "status", &s->colopts);
        if (!strcmp(k, "status.submodulesummary")) {
                int is_bool;
@@ -1211,7 +1211,7 @@ static int git_status_config(const char *k, const char *v, void *cb)
                s->display_comment_prefix = git_config_bool(k, v);
                return 0;
        }
-       if (!prefixcmp(k, "status.color.") || !prefixcmp(k, "color.status.")) {
+       if (starts_with(k, "status.color.") || starts_with(k, "color.status.")) {
                int slot = parse_status_slot(k, 13);
                if (slot < 0)
                        return 0;
@@ -1377,7 +1377,7 @@ static void print_summary(const char *prefix, const unsigned char *sha1,
 
        head = resolve_ref_unsafe("HEAD", junk_sha1, 0, NULL);
        printf("[%s%s ",
-               !prefixcmp(head, "refs/heads/") ?
+               starts_with(head, "refs/heads/") ?
                        head + 11 :
                        !strcmp(head, "HEAD") ?
                                _("detached HEAD") :
@@ -1505,7 +1505,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
        struct strbuf sb = STRBUF_INIT;
        struct strbuf author_ident = STRBUF_INIT;
        const char *index_file, *reflog_msg;
-       char *nl, *p;
+       char *nl;
        unsigned char sha1[20];
        struct ref_lock *ref_lock;
        struct commit_list *parents = NULL, **pptr = &parents;
@@ -1601,11 +1601,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
        }
 
        /* Truncate the message just before the diff, if any. */
-       if (verbose) {
-               p = strstr(sb.buf, "\ndiff --git ");
-               if (p != NULL)
-                       strbuf_setlen(&sb, p - sb.buf + 1);
-       }
+       if (verbose)
+               wt_status_truncate_message_at_cut_line(&sb);
 
        if (cleanup_mode != CLEANUP_NONE)
                stripspace(&sb, cleanup_mode == CLEANUP_ALL);
index 20e89fe4e0c7db50267e8e9358c38faea2259fab..92ebf23f0a9aa0e052ad68d60e17d0783f633a1c 100644 (file)
@@ -671,9 +671,3 @@ int cmd_config(int argc, const char **argv, const char *prefix)
 
        return 0;
 }
-
-int cmd_repo_config(int argc, const char **argv, const char *prefix)
-{
-       fprintf(stderr, "WARNING: git repo-config is deprecated in favor of git config.\n");
-       return cmd_config(argc, argv, prefix);
-}
index 6f6210988709dc866bf79a5bb5523356773b93a0..7db43dae1be95e86c21249d9150fc8553c367527 100644 (file)
@@ -141,7 +141,7 @@ static void add_to_known_names(const char *path,
 
 static int get_name(const char *path, const unsigned char *sha1, int flag, void *cb_data)
 {
-       int is_tag = !prefixcmp(path, "refs/tags/");
+       int is_tag = starts_with(path, "refs/tags/");
        unsigned char peeled[20];
        int is_annotated, prio;
 
index fe0cc7f1b5b1451f267b1d9e24a6e40cd6e55325..0f247d24008a4dc7ba5958e716c13f7582a820f1 100644 (file)
@@ -16,6 +16,9 @@
 #include "submodule.h"
 #include "sha1-array.h"
 
+#define DIFF_NO_INDEX_EXPLICIT 1
+#define DIFF_NO_INDEX_IMPLICIT 2
+
 struct blobinfo {
        unsigned char sha1[20];
        const char *name;
@@ -259,7 +262,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
        struct object_array ent = OBJECT_ARRAY_INIT;
        int blobs = 0, paths = 0;
        struct blobinfo blob[2];
-       int nongit;
+       int nongit = 0, no_index = 0;
        int result = 0;
 
        /*
@@ -285,14 +288,59 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
         * Other cases are errors.
         */
 
-       prefix = setup_git_directory_gently(&nongit);
-       gitmodules_config();
+       /* Were we asked to do --no-index explicitly? */
+       for (i = 1; i < argc; i++) {
+               if (!strcmp(argv[i], "--")) {
+                       i++;
+                       break;
+               }
+               if (!strcmp(argv[i], "--no-index"))
+                       no_index = DIFF_NO_INDEX_EXPLICIT;
+               if (argv[i][0] != '-')
+                       break;
+       }
+
+       if (!no_index)
+               prefix = setup_git_directory_gently(&nongit);
+
+       /*
+        * Treat git diff with at least one path outside of the
+        * repo the same as if the command would have been executed
+        * outside of a git repository.  In this case it behaves
+        * the same way as "git diff --no-index <a> <b>", which acts
+        * as a colourful "diff" replacement.
+        */
+       if (nongit || ((argc == i + 2) &&
+                      (!path_inside_repo(prefix, argv[i]) ||
+                       !path_inside_repo(prefix, argv[i + 1]))))
+               no_index = DIFF_NO_INDEX_IMPLICIT;
+
+       if (!no_index)
+               gitmodules_config();
        git_config(git_diff_ui_config, NULL);
 
        init_revisions(&rev, prefix);
 
-       /* If this is a no-index diff, just run it and exit there. */
-       diff_no_index(&rev, argc, argv, nongit, prefix);
+       if (no_index && argc != i + 2) {
+               if (no_index == DIFF_NO_INDEX_IMPLICIT) {
+                       /*
+                        * There was no --no-index and there were not two
+                        * paths. It is possible that the user intended
+                        * to do an inside-repository operation.
+                        */
+                       fprintf(stderr, "Not a git repository\n");
+                       fprintf(stderr,
+                               "To compare two paths outside a working tree:\n");
+               }
+               /* Give the usage message for non-repository usage and exit. */
+               usagef("git diff %s <path> <path>",
+                      no_index == DIFF_NO_INDEX_EXPLICIT ?
+                      "--no-index" : "[--no-index]");
+
+       }
+       if (no_index)
+               /* If this is a no-index diff, just run it and exit there. */
+               diff_no_index(&rev, argc, argv, prefix);
 
        /* Otherwise, we are doing the usual "git" diff */
        rev.diffopt.skip_stat_unmatch = !!diff_auto_refresh_index;
index ea6305258d4d63e58f2453736cfc5aa38ec37282..b8d8a3aaf9ee8294be9a78956a70b476d327ee05 100644 (file)
@@ -476,7 +476,7 @@ static void handle_tag(const char *name, struct tag *tag)
                }
        }
 
-       if (!prefixcmp(name, "refs/tags/"))
+       if (starts_with(name, "refs/tags/"))
                name += 10;
        printf("tag %s\nfrom :%d\n%.*s%sdata %d\n%.*s\n",
               name, tagged_mark,
index c8e858232a8e7a3536ef4d296efdc618e034c36e..1262b405f8212e71d088f4e090b8121fa304699a 100644 (file)
@@ -3,16 +3,24 @@
 #include "fetch-pack.h"
 #include "remote.h"
 #include "connect.h"
+#include "sha1-array.h"
 
 static const char fetch_pack_usage[] =
 "git fetch-pack [--all] [--stdin] [--quiet|-q] [--keep|-k] [--thin] "
 "[--include-tag] [--upload-pack=<git-upload-pack>] [--depth=<n>] "
-"[--no-progress] [-v] [<host>:]<directory> [<refs>...]";
+"[--no-progress] [--diag-url] [-v] [<host>:]<directory> [<refs>...]";
 
 static void add_sought_entry_mem(struct ref ***sought, int *nr, int *alloc,
                                 const char *name, int namelen)
 {
        struct ref *ref = xcalloc(1, sizeof(*ref) + namelen + 1);
+       unsigned char sha1[20];
+
+       if (namelen > 41 && name[40] == ' ' && !get_sha1_hex(name, sha1)) {
+               hashcpy(ref->old_sha1, sha1);
+               name += 41;
+               namelen -= 41;
+       }
 
        memcpy(ref->name, name, namelen);
        ref->name[namelen] = '\0';
@@ -39,6 +47,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
        char **pack_lockfile_ptr = NULL;
        struct child_process *conn;
        struct fetch_pack_args args;
+       struct sha1_array shallow = SHA1_ARRAY_INIT;
 
        packet_trace_identity("fetch-pack");
 
@@ -48,11 +57,11 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
        for (i = 1; i < argc && *argv[i] == '-'; i++) {
                const char *arg = argv[i];
 
-               if (!prefixcmp(arg, "--upload-pack=")) {
+               if (starts_with(arg, "--upload-pack=")) {
                        args.uploadpack = arg + 14;
                        continue;
                }
-               if (!prefixcmp(arg, "--exec=")) {
+               if (starts_with(arg, "--exec=")) {
                        args.uploadpack = arg + 7;
                        continue;
                }
@@ -81,11 +90,15 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
                        args.stdin_refs = 1;
                        continue;
                }
+               if (!strcmp("--diag-url", arg)) {
+                       args.diag_url = 1;
+                       continue;
+               }
                if (!strcmp("-v", arg)) {
                        args.verbose = 1;
                        continue;
                }
-               if (!prefixcmp(arg, "--depth=")) {
+               if (starts_with(arg, "--depth=")) {
                        args.depth = strtol(arg + 8, NULL, 0);
                        continue;
                }
@@ -106,6 +119,14 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
                        args.check_self_contained_and_connected = 1;
                        continue;
                }
+               if (!strcmp("--cloning", arg)) {
+                       args.cloning = 1;
+                       continue;
+               }
+               if (!strcmp("--update-shallow", arg)) {
+                       args.update_shallow = 1;
+                       continue;
+               }
                usage(fetch_pack_usage);
        }
 
@@ -146,14 +167,18 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
                fd[0] = 0;
                fd[1] = 1;
        } else {
+               int flags = args.verbose ? CONNECT_VERBOSE : 0;
+               if (args.diag_url)
+                       flags |= CONNECT_DIAG_URL;
                conn = git_connect(fd, dest, args.uploadpack,
-                                  args.verbose ? CONNECT_VERBOSE : 0);
+                                  flags);
+               if (!conn)
+                       return args.diag_url ? 0 : 1;
        }
+       get_remote_heads(fd[0], NULL, 0, &ref, 0, NULL, &shallow);
 
-       get_remote_heads(fd[0], NULL, 0, &ref, 0, NULL);
-
-       ref = fetch_pack(&args, fd, conn, ref, dest,
-                        sought, nr_sought, pack_lockfile_ptr);
+       ref = fetch_pack(&args, fd, conn, ref, dest, sought, nr_sought,
+                        &shallow, pack_lockfile_ptr);
        if (pack_lockfile) {
                printf("lock %s\n", pack_lockfile);
                fflush(stdout);
index bd7a10164f4fed8aeb6e8148e2ae8c0a793e7ee0..025bc3e38d7d8055699ea15f241648602cfaf267 100644 (file)
@@ -36,7 +36,7 @@ static int prune = -1; /* unspecified */
 
 static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity;
 static int progress = -1, recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
-static int tags = TAGS_DEFAULT, unshallow;
+static int tags = TAGS_DEFAULT, unshallow, update_shallow;
 static const char *depth;
 static const char *upload_pack;
 static struct strbuf default_rla = STRBUF_INIT;
@@ -44,6 +44,7 @@ static struct transport *gtransport;
 static struct transport *gsecondary;
 static const char *submodule_prefix = "";
 static const char *recurse_submodules_default;
+static int shown_url = 0;
 
 static int option_parse_recurse_submodules(const struct option *opt,
                                   const char *arg, int unset)
@@ -104,6 +105,8 @@ static struct option builtin_fetch_options[] = {
        { OPTION_STRING, 0, "recurse-submodules-default",
                   &recurse_submodules_default, NULL,
                   N_("default mode for recursion"), PARSE_OPT_HIDDEN },
+       OPT_BOOL(0, "update-shallow", &update_shallow,
+                N_("accept refs that update .git/shallow")),
        OPT_END()
 };
 
@@ -160,48 +163,156 @@ static void add_merge_config(struct ref **head,
        }
 }
 
+static int add_existing(const char *refname, const unsigned char *sha1,
+                       int flag, void *cbdata)
+{
+       struct string_list *list = (struct string_list *)cbdata;
+       struct string_list_item *item = string_list_insert(list, refname);
+       item->util = xmalloc(20);
+       hashcpy(item->util, sha1);
+       return 0;
+}
+
+static int will_fetch(struct ref **head, const unsigned char *sha1)
+{
+       struct ref *rm = *head;
+       while (rm) {
+               if (!hashcmp(rm->old_sha1, sha1))
+                       return 1;
+               rm = rm->next;
+       }
+       return 0;
+}
+
 static void find_non_local_tags(struct transport *transport,
                        struct ref **head,
-                       struct ref ***tail);
+                       struct ref ***tail)
+{
+       struct string_list existing_refs = STRING_LIST_INIT_DUP;
+       struct string_list remote_refs = STRING_LIST_INIT_NODUP;
+       const struct ref *ref;
+       struct string_list_item *item = NULL;
+
+       for_each_ref(add_existing, &existing_refs);
+       for (ref = transport_get_remote_refs(transport); ref; ref = ref->next) {
+               if (!starts_with(ref->name, "refs/tags/"))
+                       continue;
+
+               /*
+                * The peeled ref always follows the matching base
+                * ref, so if we see a peeled ref that we don't want
+                * to fetch then we can mark the ref entry in the list
+                * as one to ignore by setting util to NULL.
+                */
+               if (ends_with(ref->name, "^{}")) {
+                       if (item && !has_sha1_file(ref->old_sha1) &&
+                           !will_fetch(head, ref->old_sha1) &&
+                           !has_sha1_file(item->util) &&
+                           !will_fetch(head, item->util))
+                               item->util = NULL;
+                       item = NULL;
+                       continue;
+               }
+
+               /*
+                * If item is non-NULL here, then we previously saw a
+                * ref not followed by a peeled reference, so we need
+                * to check if it is a lightweight tag that we want to
+                * fetch.
+                */
+               if (item && !has_sha1_file(item->util) &&
+                   !will_fetch(head, item->util))
+                       item->util = NULL;
+
+               item = NULL;
+
+               /* skip duplicates and refs that we already have */
+               if (string_list_has_string(&remote_refs, ref->name) ||
+                   string_list_has_string(&existing_refs, ref->name))
+                       continue;
+
+               item = string_list_insert(&remote_refs, ref->name);
+               item->util = (void *)ref->old_sha1;
+       }
+       string_list_clear(&existing_refs, 1);
+
+       /*
+        * We may have a final lightweight tag that needs to be
+        * checked to see if it needs fetching.
+        */
+       if (item && !has_sha1_file(item->util) &&
+           !will_fetch(head, item->util))
+               item->util = NULL;
+
+       /*
+        * For all the tags in the remote_refs string list,
+        * add them to the list of refs to be fetched
+        */
+       for_each_string_list_item(item, &remote_refs) {
+               /* Unless we have already decided to ignore this item... */
+               if (item->util)
+               {
+                       struct ref *rm = alloc_ref(item->string);
+                       rm->peer_ref = alloc_ref(item->string);
+                       hashcpy(rm->old_sha1, item->util);
+                       **tail = rm;
+                       *tail = &rm->next;
+               }
+       }
+
+       string_list_clear(&remote_refs, 0);
+}
 
 static struct ref *get_ref_map(struct transport *transport,
-                              struct refspec *refs, int ref_count, int tags,
-                              int *autotags)
+                              struct refspec *refspecs, int refspec_count,
+                              int tags, int *autotags)
 {
        int i;
        struct ref *rm;
        struct ref *ref_map = NULL;
        struct ref **tail = &ref_map;
 
-       const struct ref *remote_refs = transport_get_remote_refs(transport);
+       /* opportunistically-updated references: */
+       struct ref *orefs = NULL, **oref_tail = &orefs;
 
-       if (ref_count || tags == TAGS_SET) {
-               struct ref **old_tail;
+       const struct ref *remote_refs = transport_get_remote_refs(transport);
 
-               for (i = 0; i < ref_count; i++) {
-                       get_fetch_map(remote_refs, &refs[i], &tail, 0);
-                       if (refs[i].dst && refs[i].dst[0])
+       if (refspec_count) {
+               for (i = 0; i < refspec_count; i++) {
+                       get_fetch_map(remote_refs, &refspecs[i], &tail, 0);
+                       if (refspecs[i].dst && refspecs[i].dst[0])
                                *autotags = 1;
                }
-               /* Merge everything on the command line, but not --tags */
+               /* Merge everything on the command line (but not --tags) */
                for (rm = ref_map; rm; rm = rm->next)
                        rm->fetch_head_status = FETCH_HEAD_MERGE;
-               if (tags == TAGS_SET)
-                       get_fetch_map(remote_refs, tag_refspec, &tail, 0);
 
                /*
-                * For any refs that we happen to be fetching via command-line
-                * arguments, take the opportunity to update their configured
-                * counterparts. However, we do not want to mention these
-                * entries in FETCH_HEAD at all, as they would simply be
-                * duplicates of existing entries.
+                * For any refs that we happen to be fetching via
+                * command-line arguments, the destination ref might
+                * have been missing or have been different than the
+                * remote-tracking ref that would be derived from the
+                * configured refspec.  In these cases, we want to
+                * take the opportunity to update their configured
+                * remote-tracking reference.  However, we do not want
+                * to mention these entries in FETCH_HEAD at all, as
+                * they would simply be duplicates of existing
+                * entries, so we set them FETCH_HEAD_IGNORE below.
+                *
+                * We compute these entries now, based only on the
+                * refspecs specified on the command line.  But we add
+                * them to the list following the refspecs resulting
+                * from the tags option so that one of the latter,
+                * which has FETCH_HEAD_NOT_FOR_MERGE, is not removed
+                * by ref_remove_duplicates() in favor of one of these
+                * opportunistic entries with FETCH_HEAD_IGNORE.
                 */
-               old_tail = tail;
                for (i = 0; i < transport->remote->fetch_refspec_nr; i++)
                        get_fetch_map(ref_map, &transport->remote->fetch[i],
-                                     &tail, 1);
-               for (rm = *old_tail; rm; rm = rm->next)
-                       rm->fetch_head_status = FETCH_HEAD_IGNORE;
+                                     &oref_tail, 1);
+
+               if (tags == TAGS_SET)
+                       get_fetch_map(remote_refs, tag_refspec, &tail, 0);
        } else {
                /* Use the defaults */
                struct remote *remote = transport->remote;
@@ -238,11 +349,21 @@ static struct ref *get_ref_map(struct transport *transport,
                        tail = &ref_map->next;
                }
        }
-       if (tags == TAGS_DEFAULT && *autotags)
+
+       if (tags == TAGS_SET)
+               /* also fetch all tags */
+               get_fetch_map(remote_refs, tag_refspec, &tail, 0);
+       else if (tags == TAGS_DEFAULT && *autotags)
                find_non_local_tags(transport, &ref_map, &tail);
-       ref_remove_duplicates(ref_map);
 
-       return ref_map;
+       /* Now append any refs to be updated opportunistically: */
+       *tail = orefs;
+       for (rm = orefs; rm; rm = rm->next) {
+               rm->fetch_head_status = FETCH_HEAD_IGNORE;
+               tail = &rm->next;
+       }
+
+       return ref_remove_duplicates(ref_map);
 }
 
 #define STORE_REF_ERROR_OTHER 1
@@ -313,7 +434,7 @@ static int update_local_ref(struct ref *ref,
        }
 
        if (!is_null_sha1(ref->old_sha1) &&
-           !prefixcmp(ref->name, "refs/tags/")) {
+           starts_with(ref->name, "refs/tags/")) {
                int r;
                r = s_update_ref("updating tag", ref, 0);
                strbuf_addf(display, "%c %-*s %-*s -> %s%s",
@@ -336,10 +457,10 @@ static int update_local_ref(struct ref *ref,
                 * more likely to follow a standard layout.
                 */
                const char *name = remote_ref ? remote_ref->name : "";
-               if (!prefixcmp(name, "refs/tags/")) {
+               if (starts_with(name, "refs/tags/")) {
                        msg = "storing tag";
                        what = _("[new tag]");
-               } else if (!prefixcmp(name, "refs/heads/")) {
+               } else if (starts_with(name, "refs/heads/")) {
                        msg = "storing head";
                        what = _("[new branch]");
                } else {
@@ -405,6 +526,8 @@ static int iterate_ref_map(void *cb_data, unsigned char sha1[20])
        struct ref **rm = cb_data;
        struct ref *ref = *rm;
 
+       while (ref && ref->status == REF_STATUS_REJECT_SHALLOW)
+               ref = ref->next;
        if (!ref)
                return -1; /* end of the list */
        *rm = ref->next;
@@ -417,7 +540,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
 {
        FILE *fp;
        struct commit *commit;
-       int url_len, i, shown_url = 0, rc = 0;
+       int url_len, i, rc = 0;
        struct strbuf note = STRBUF_INIT;
        const char *what, *kind;
        struct ref *rm;
@@ -451,6 +574,13 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
                        struct ref *ref = NULL;
                        const char *merge_status_marker = "";
 
+                       if (rm->status == REF_STATUS_REJECT_SHALLOW) {
+                               if (want_status == FETCH_HEAD_MERGE)
+                                       warning(_("reject %s because shallow roots are not allowed to be updated"),
+                                               rm->peer_ref ? rm->peer_ref->name : rm->name);
+                               continue;
+                       }
+
                        commit = lookup_commit_reference_gently(rm->old_sha1, 1);
                        if (!commit)
                                rm->fetch_head_status = FETCH_HEAD_NOT_FOR_MERGE;
@@ -471,15 +601,15 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
                                kind = "";
                                what = "";
                        }
-                       else if (!prefixcmp(rm->name, "refs/heads/")) {
+                       else if (starts_with(rm->name, "refs/heads/")) {
                                kind = "branch";
                                what = rm->name + 11;
                        }
-                       else if (!prefixcmp(rm->name, "refs/tags/")) {
+                       else if (starts_with(rm->name, "refs/tags/")) {
                                kind = "tag";
                                what = rm->name + 10;
                        }
-                       else if (!prefixcmp(rm->name, "refs/remotes/")) {
+                       else if (starts_with(rm->name, "refs/remotes/")) {
                                kind = "remote-tracking branch";
                                what = rm->name + 13;
                        }
@@ -590,17 +720,36 @@ static int fetch_refs(struct transport *transport, struct ref *ref_map)
        return ret;
 }
 
-static int prune_refs(struct refspec *refs, int ref_count, struct ref *ref_map)
+static int prune_refs(struct refspec *refs, int ref_count, struct ref *ref_map,
+               const char *raw_url)
 {
-       int result = 0;
+       int url_len, i, result = 0;
        struct ref *ref, *stale_refs = get_stale_heads(refs, ref_count, ref_map);
+       char *url;
        const char *dangling_msg = dry_run
                ? _("   (%s will become dangling)")
                : _("   (%s has become dangling)");
 
+       if (raw_url)
+               url = transport_anonymize_url(raw_url);
+       else
+               url = xstrdup("foreign");
+
+       url_len = strlen(url);
+       for (i = url_len - 1; url[i] == '/' && 0 <= i; i--)
+               ;
+
+       url_len = i + 1;
+       if (4 < i && !strncmp(".git", url + i - 3, 4))
+               url_len = i - 3;
+
        for (ref = stale_refs; ref; ref = ref->next) {
                if (!dry_run)
                        result |= delete_ref(ref->name, NULL, 0);
+               if (verbosity >= 0 && !shown_url) {
+                       fprintf(stderr, _("From %.*s\n"), url_len, url);
+                       shown_url = 1;
+               }
                if (verbosity >= 0) {
                        fprintf(stderr, " x %-*s %-*s -> %s\n",
                                TRANSPORT_SUMMARY(_("[deleted]")),
@@ -608,110 +757,11 @@ static int prune_refs(struct refspec *refs, int ref_count, struct ref *ref_map)
                        warn_dangling_symref(stderr, dangling_msg, ref->name);
                }
        }
+       free(url);
        free_refs(stale_refs);
        return result;
 }
 
-static int add_existing(const char *refname, const unsigned char *sha1,
-                       int flag, void *cbdata)
-{
-       struct string_list *list = (struct string_list *)cbdata;
-       struct string_list_item *item = string_list_insert(list, refname);
-       item->util = xmalloc(20);
-       hashcpy(item->util, sha1);
-       return 0;
-}
-
-static int will_fetch(struct ref **head, const unsigned char *sha1)
-{
-       struct ref *rm = *head;
-       while (rm) {
-               if (!hashcmp(rm->old_sha1, sha1))
-                       return 1;
-               rm = rm->next;
-       }
-       return 0;
-}
-
-static void find_non_local_tags(struct transport *transport,
-                       struct ref **head,
-                       struct ref ***tail)
-{
-       struct string_list existing_refs = STRING_LIST_INIT_DUP;
-       struct string_list remote_refs = STRING_LIST_INIT_NODUP;
-       const struct ref *ref;
-       struct string_list_item *item = NULL;
-
-       for_each_ref(add_existing, &existing_refs);
-       for (ref = transport_get_remote_refs(transport); ref; ref = ref->next) {
-               if (prefixcmp(ref->name, "refs/tags/"))
-                       continue;
-
-               /*
-                * The peeled ref always follows the matching base
-                * ref, so if we see a peeled ref that we don't want
-                * to fetch then we can mark the ref entry in the list
-                * as one to ignore by setting util to NULL.
-                */
-               if (!suffixcmp(ref->name, "^{}")) {
-                       if (item && !has_sha1_file(ref->old_sha1) &&
-                           !will_fetch(head, ref->old_sha1) &&
-                           !has_sha1_file(item->util) &&
-                           !will_fetch(head, item->util))
-                               item->util = NULL;
-                       item = NULL;
-                       continue;
-               }
-
-               /*
-                * If item is non-NULL here, then we previously saw a
-                * ref not followed by a peeled reference, so we need
-                * to check if it is a lightweight tag that we want to
-                * fetch.
-                */
-               if (item && !has_sha1_file(item->util) &&
-                   !will_fetch(head, item->util))
-                       item->util = NULL;
-
-               item = NULL;
-
-               /* skip duplicates and refs that we already have */
-               if (string_list_has_string(&remote_refs, ref->name) ||
-                   string_list_has_string(&existing_refs, ref->name))
-                       continue;
-
-               item = string_list_insert(&remote_refs, ref->name);
-               item->util = (void *)ref->old_sha1;
-       }
-       string_list_clear(&existing_refs, 1);
-
-       /*
-        * We may have a final lightweight tag that needs to be
-        * checked to see if it needs fetching.
-        */
-       if (item && !has_sha1_file(item->util) &&
-           !will_fetch(head, item->util))
-               item->util = NULL;
-
-       /*
-        * For all the tags in the remote_refs string list,
-        * add them to the list of refs to be fetched
-        */
-       for_each_string_list_item(item, &remote_refs) {
-               /* Unless we have already decided to ignore this item... */
-               if (item->util)
-               {
-                       struct ref *rm = alloc_ref(item->string);
-                       rm->peer_ref = alloc_ref(item->string);
-                       hashcpy(rm->old_sha1, item->util);
-                       **tail = rm;
-                       *tail = &rm->next;
-               }
-       }
-
-       string_list_clear(&remote_refs, 0);
-}
-
 static void check_not_current_branch(struct ref *ref_map)
 {
        struct branch *current_branch = branch_get(NULL);
@@ -759,6 +809,8 @@ static struct transport *prepare_transport(struct remote *remote)
                set_option(transport, TRANS_OPT_KEEP, "yes");
        if (depth)
                set_option(transport, TRANS_OPT_DEPTH, depth);
+       if (update_shallow)
+               set_option(transport, TRANS_OPT_UPDATE_SHALLOW, "yes");
        return transport;
 }
 
@@ -824,39 +876,26 @@ static int do_fetch(struct transport *transport,
 
        if (tags == TAGS_DEFAULT && autotags)
                transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, "1");
-       if (fetch_refs(transport, ref_map)) {
-               free_refs(ref_map);
-               retcode = 1;
-               goto cleanup;
-       }
        if (prune) {
                /*
-                * If --tags was specified, pretend that the user gave us
-                * the canonical tags refspec
+                * We only prune based on refspecs specified
+                * explicitly (via command line or configuration); we
+                * don't care whether --tags was specified.
                 */
-               if (tags == TAGS_SET) {
-                       const char *tags_str = "refs/tags/*:refs/tags/*";
-                       struct refspec *tags_refspec, *refspec;
-
-                       /* Copy the refspec and add the tags to it */
-                       refspec = xcalloc(ref_count + 1, sizeof(struct refspec));
-                       tags_refspec = parse_fetch_refspec(1, &tags_str);
-                       memcpy(refspec, refs, ref_count * sizeof(struct refspec));
-                       memcpy(&refspec[ref_count], tags_refspec, sizeof(struct refspec));
-                       ref_count++;
-
-                       prune_refs(refspec, ref_count, ref_map);
-
-                       ref_count--;
-                       /* The rest of the strings belong to fetch_one */
-                       free_refspec(1, tags_refspec);
-                       free(refspec);
-               } else if (ref_count) {
-                       prune_refs(refs, ref_count, ref_map);
+               if (ref_count) {
+                       prune_refs(refs, ref_count, ref_map, transport->url);
                } else {
-                       prune_refs(transport->remote->fetch, transport->remote->fetch_refspec_nr, ref_map);
+                       prune_refs(transport->remote->fetch,
+                                  transport->remote->fetch_refspec_nr,
+                                  ref_map,
+                                  transport->url);
                }
        }
+       if (fetch_refs(transport, ref_map)) {
+               free_refs(ref_map);
+               retcode = 1;
+               goto cleanup;
+       }
        free_refs(ref_map);
 
        /* if neither --no-tags nor --tags was specified, do automated tag
@@ -892,7 +931,7 @@ static int get_remote_group(const char *key, const char *value, void *priv)
 {
        struct remote_group_data *g = priv;
 
-       if (!prefixcmp(key, "remotes.") &&
+       if (starts_with(key, "remotes.") &&
                        !strcmp(key + 8, g->name)) {
                /* split list by white space */
                int space = strcspn(value, " \t\n");
@@ -930,8 +969,8 @@ static void add_options_to_argv(struct argv_array *argv)
 {
        if (dry_run)
                argv_array_push(argv, "--dry-run");
-       if (prune > 0)
-               argv_array_push(argv, "--prune");
+       if (prune != -1)
+               argv_array_push(argv, prune ? "--prune" : "--no-prune");
        if (update_head_ok)
                argv_array_push(argv, "--update-head-ok");
        if (force)
@@ -1075,6 +1114,10 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
                }
        }
 
+       /* no need to be strict, transport_set_option() will validate it again */
+       if (depth && atoi(depth) < 1)
+               die(_("depth %s is not a positive number"), depth);
+
        if (recurse_submodules != RECURSE_SUBMODULES_OFF) {
                if (recurse_submodules_default) {
                        int arg = parse_fetch_recurse_submodules_arg("--recurse-submodules-default", recurse_submodules_default);
index 1c040708696c8d6e81023b3261966fa751f14ad4..3906eda87712ea099e0e5876fff1fe28fc5716be 100644 (file)
@@ -109,7 +109,7 @@ static int handle_line(char *line, struct merge_parents *merge_parents)
        if (len < 43 || line[40] != '\t')
                return 1;
 
-       if (!prefixcmp(line + 41, "not-for-merge"))
+       if (starts_with(line + 41, "not-for-merge"))
                return 0;
 
        if (line[41] != '\t')
@@ -155,16 +155,16 @@ static int handle_line(char *line, struct merge_parents *merge_parents)
        if (pulling_head) {
                origin = src;
                src_data->head_status |= 1;
-       } else if (!prefixcmp(line, "branch ")) {
+       } else if (starts_with(line, "branch ")) {
                origin_data->is_local_branch = 1;
                origin = line + 7;
                string_list_append(&src_data->branch, origin);
                src_data->head_status |= 2;
-       } else if (!prefixcmp(line, "tag ")) {
+       } else if (starts_with(line, "tag ")) {
                origin = line;
                string_list_append(&src_data->tag, origin + 4);
                src_data->head_status |= 2;
-       } else if (!prefixcmp(line, "remote-tracking branch ")) {
+       } else if (starts_with(line, "remote-tracking branch ")) {
                origin = line + strlen("remote-tracking branch ");
                string_list_append(&src_data->r_branch, origin);
                src_data->head_status |= 2;
@@ -605,7 +605,7 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
                resolve_refdup("HEAD", head_sha1, 1, NULL);
        if (!current_branch)
                die("No current branch");
-       if (!prefixcmp(current_branch, "refs/heads/"))
+       if (starts_with(current_branch, "refs/heads/"))
                current_branch += 11;
 
        find_merge_parents(&merge_parents, in, head_sha1);
index 875bd813d5ed6d09622fc3473fdb574e9714bf7f..51798b48b54266fccd2f7b4687646d152ab70588 100644 (file)
@@ -92,7 +92,7 @@ static struct {
  */
 static const char **used_atom;
 static cmp_type *used_atom_type;
-static int used_atom_cnt, sort_atom_limit, need_tagged, need_symref;
+static int used_atom_cnt, need_tagged, need_symref;
 static int need_color_reset_at_eol;
 
 /*
@@ -453,7 +453,7 @@ static void grab_person(const char *who, struct atom_value *val, int deref, stru
                if (name[wholen] != 0 &&
                    strcmp(name + wholen, "name") &&
                    strcmp(name + wholen, "email") &&
-                   prefixcmp(name + wholen, "date"))
+                   !starts_with(name + wholen, "date"))
                        continue;
                if (!wholine)
                        wholine = find_wholine(who, wholen, buf, sz);
@@ -465,7 +465,7 @@ static void grab_person(const char *who, struct atom_value *val, int deref, stru
                        v->s = copy_name(wholine);
                else if (!strcmp(name + wholen, "email"))
                        v->s = copy_email(wholine);
-               else if (!prefixcmp(name + wholen, "date"))
+               else if (starts_with(name + wholen, "date"))
                        grab_date(wholine, v, name);
        }
 
@@ -487,7 +487,7 @@ static void grab_person(const char *who, struct atom_value *val, int deref, stru
                if (deref)
                        name++;
 
-               if (!prefixcmp(name, "creatordate"))
+               if (starts_with(name, "creatordate"))
                        grab_date(wholine, v, name);
                else if (!strcmp(name, "creator"))
                        v->s = copy_line(wholine);
@@ -668,13 +668,13 @@ static void populate_value(struct refinfo *ref)
                        name++;
                }
 
-               if (!prefixcmp(name, "refname"))
+               if (starts_with(name, "refname"))
                        refname = ref->refname;
-               else if (!prefixcmp(name, "symref"))
+               else if (starts_with(name, "symref"))
                        refname = ref->symref ? ref->symref : "";
-               else if (!prefixcmp(name, "upstream")) {
+               else if (starts_with(name, "upstream")) {
                        /* only local branches may have an upstream */
-                       if (prefixcmp(ref->refname, "refs/heads/"))
+                       if (!starts_with(ref->refname, "refs/heads/"))
                                continue;
                        branch = branch_get(ref->refname + 11);
 
@@ -682,7 +682,7 @@ static void populate_value(struct refinfo *ref)
                            !branch->merge[0]->dst)
                                continue;
                        refname = branch->merge[0]->dst;
-               } else if (!prefixcmp(name, "color:")) {
+               } else if (starts_with(name, "color:")) {
                        char color[COLOR_MAXLEN] = "";
 
                        color_parse(name + 6, "--format", color);
@@ -725,7 +725,7 @@ static void populate_value(struct refinfo *ref)
                                refname = shorten_unambiguous_ref(refname,
                                                      warn_ambiguous_refs);
                        else if (!strcmp(formatp, "track") &&
-                               !prefixcmp(name, "upstream")) {
+                                starts_with(name, "upstream")) {
                                char buf[40];
 
                                stat_tracking_info(branch, &num_ours, &num_theirs);
@@ -744,7 +744,7 @@ static void populate_value(struct refinfo *ref)
                                }
                                continue;
                        } else if (!strcmp(formatp, "trackshort") &&
-                               !prefixcmp(name, "upstream")) {
+                                  starts_with(name, "upstream")) {
                                assert(branch);
                                stat_tracking_info(branch, &num_ours, &num_theirs);
                                if (!num_ours && !num_theirs)
@@ -1105,7 +1105,6 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
 
        if (!sort)
                sort = default_sort();
-       sort_atom_limit = used_atom_cnt;
 
        /* for warn_ambiguous_refs */
        git_config(git_default_config, NULL);
index 97ce678c6ba63afeb3deedfd469c4a53a475a1be..1affdd5e924c9e71605be603f253e34f6a995f36 100644 (file)
@@ -442,7 +442,7 @@ static void fsck_dir(int i, char *path)
                        add_sha1_list(sha1, DIRENT_SORT_HINT(de));
                        continue;
                }
-               if (!prefixcmp(de->d_name, "tmp_obj_"))
+               if (starts_with(de->d_name, "tmp_obj_"))
                        continue;
                fprintf(stderr, "bad sha1 file: %s/%s\n", path, de->d_name);
        }
@@ -484,7 +484,7 @@ static int fsck_handle_reflog(const char *logname, const unsigned char *sha1, in
 
 static int is_branch(const char *refname)
 {
-       return !strcmp(refname, "HEAD") || !prefixcmp(refname, "refs/heads/");
+       return !strcmp(refname, "HEAD") || starts_with(refname, "refs/heads/");
 }
 
 static int fsck_handle_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
@@ -566,7 +566,7 @@ static int fsck_head_link(void)
        if (!strcmp(head_points_at, "HEAD"))
                /* detached HEAD */
                null_is_error = 1;
-       else if (prefixcmp(head_points_at, "refs/heads/"))
+       else if (!starts_with(head_points_at, "refs/heads/"))
                return error("HEAD points to something strange (%s)",
                             head_points_at);
        if (is_null_sha1(head_sha1)) {
index c14190f840b0427820ebf69b209d86f0a21c62b7..c19545d49e217400ee1736183c870557b9c9d54b 100644 (file)
@@ -16,6 +16,7 @@
 #include "run-command.h"
 #include "sigchain.h"
 #include "argv-array.h"
+#include "commit.h"
 
 #define FAILED_RUN "failed to run %s"
 
@@ -222,7 +223,7 @@ static const char *lock_repo_for_gc(int force, pid_t* ret_pid)
                        time(NULL) - st.st_mtime <= 12 * 3600 &&
                        fscanf(fp, "%"PRIuMAX" %127c", &pid, locking_host) == 2 &&
                        /* be gentle to concurrent "gc" on remote hosts */
-                       (strcmp(locking_host, my_host) || !kill(pid, 0));
+                       (strcmp(locking_host, my_host) || !kill(pid, 0) || errno == EPERM);
                if (fp != NULL)
                        fclose(fp);
                if (should_exit) {
diff --git a/builtin/get-tar-commit-id.c b/builtin/get-tar-commit-id.c
new file mode 100644 (file)
index 0000000..aa72596
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2005, 2006 Rene Scharfe
+ */
+#include "cache.h"
+#include "commit.h"
+#include "tar.h"
+#include "builtin.h"
+#include "quote.h"
+
+static const char builtin_get_tar_commit_id_usage[] =
+"git get-tar-commit-id < <tarfile>";
+
+/* ustar header + extended global header content */
+#define RECORDSIZE     (512)
+#define HEADERSIZE (2 * RECORDSIZE)
+
+int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix)
+{
+       char buffer[HEADERSIZE];
+       struct ustar_header *header = (struct ustar_header *)buffer;
+       char *content = buffer + RECORDSIZE;
+       ssize_t n;
+
+       if (argc != 1)
+               usage(builtin_get_tar_commit_id_usage);
+
+       n = read_in_full(0, buffer, HEADERSIZE);
+       if (n < HEADERSIZE)
+               die("git get-tar-commit-id: read error");
+       if (header->typeflag[0] != 'g')
+               return 1;
+       if (memcmp(content, "52 comment=", 11))
+               return 1;
+
+       n = write_in_full(1, content + 11, 41);
+       if (n < 41)
+               die_errno("git get-tar-commit-id: write error");
+
+       return 0;
+}
index f1e236b9124f8b7bfd9aa5e926d525c6f4568268..1fdefeb6867cdd37beaf41c5fa9e113576b19f17 100644 (file)
@@ -100,7 +100,7 @@ static int check_emacsclient_version(void)
         */
        finish_command(&ec_process);
 
-       if (prefixcmp(buffer.buf, "emacsclient")) {
+       if (!starts_with(buffer.buf, "emacsclient")) {
                strbuf_release(&buffer);
                return error(_("Failed to parse emacsclient version."));
        }
@@ -258,7 +258,7 @@ static int add_man_viewer_info(const char *var, const char *value)
 
 static int git_help_config(const char *var, const char *value, void *cb)
 {
-       if (!prefixcmp(var, "column."))
+       if (starts_with(var, "column."))
                return git_column_config(var, value, "help", &colopts);
        if (!strcmp(var, "help.format")) {
                if (!value)
@@ -278,7 +278,7 @@ static int git_help_config(const char *var, const char *value, void *cb)
                add_man_viewer(value);
                return 0;
        }
-       if (!prefixcmp(var, "man."))
+       if (starts_with(var, "man."))
                return add_man_viewer_info(var, value);
 
        return git_default_config(var, value, cb);
@@ -288,6 +288,10 @@ static struct cmdnames main_cmds, other_cmds;
 
 static int is_git_command(const char *s)
 {
+       if (is_builtin(s))
+               return 1;
+
+       load_command_list("git-", &main_cmds, &other_cmds);
        return is_in_cmdlist(&main_cmds, s) ||
                is_in_cmdlist(&other_cmds, s);
 }
@@ -306,7 +310,7 @@ static const char *cmd_to_page(const char *git_cmd)
 {
        if (!git_cmd)
                return "git";
-       else if (!prefixcmp(git_cmd, "git"))
+       else if (starts_with(git_cmd, "git"))
                return git_cmd;
        else if (is_git_command(git_cmd))
                return prepend("git-", git_cmd);
@@ -449,7 +453,6 @@ int cmd_help(int argc, const char **argv, const char *prefix)
        int nongit;
        const char *alias;
        enum help_format parsed_help_format;
-       load_command_list("git-", &main_cmds, &other_cmds);
 
        argc = parse_options(argc, argv, prefix, builtin_help_options,
                        builtin_help_usage, 0);
@@ -458,6 +461,7 @@ int cmd_help(int argc, const char **argv, const char *prefix)
        if (show_all) {
                git_config(git_help_config, NULL);
                printf(_("usage: %s%s"), _(git_usage_string), "\n\n");
+               load_command_list("git-", &main_cmds, &other_cmds);
                list_commands(colopts, &main_cmds, &other_cmds);
        }
 
index 9e9eb4b74e8335f097c7cc9e261b051feda0bf0d..2f37a38fbcdf131ab5897e47728c5773ba36b861 100644 (file)
@@ -1534,9 +1534,9 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
                                stat_only = 1;
                        } else if (!strcmp(arg, "--keep")) {
                                keep_msg = "";
-                       } else if (!prefixcmp(arg, "--keep=")) {
+                       } else if (starts_with(arg, "--keep=")) {
                                keep_msg = arg + 7;
-                       } else if (!prefixcmp(arg, "--threads=")) {
+                       } else if (starts_with(arg, "--threads=")) {
                                char *end;
                                nr_threads = strtoul(arg+10, &end, 0);
                                if (!arg[10] || *end || nr_threads < 0)
@@ -1547,7 +1547,7 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
                                                  "ignoring %s"), arg);
                                nr_threads = 1;
 #endif
-                       } else if (!prefixcmp(arg, "--pack_header=")) {
+                       } else if (starts_with(arg, "--pack_header=")) {
                                struct pack_header *hdr;
                                char *c;
 
@@ -1566,7 +1566,7 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
                                if (index_name || (i+1) >= argc)
                                        usage(index_pack_usage);
                                index_name = argv[++i];
-                       } else if (!prefixcmp(arg, "--index-version=")) {
+                       } else if (starts_with(arg, "--index-version=")) {
                                char *c;
                                opts.version = strtoul(arg + 16, &c, 10);
                                if (opts.version > 2)
index 78aa3872dddbba8988b61c49996f9e63719bac75..c7c76bbf21fd5b5c9200b2535875b912e4d49000 100644 (file)
@@ -266,7 +266,7 @@ static int create_default_files(const char *template_path)
                /* allow template config file to override the default */
                if (log_all_ref_updates == -1)
                    git_config_set("core.logallrefupdates", "true");
-               if (prefixcmp(git_dir, work_tree) ||
+               if (!starts_with(git_dir, work_tree) ||
                    strcmp(git_dir + strlen(work_tree), "/.git")) {
                        git_config_set("core.worktree", work_tree);
                }
@@ -515,13 +515,14 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
                                saved = shared_repository;
                                shared_repository = 0;
                                switch (safe_create_leading_directories_const(argv[0])) {
-                               case -3:
+                               case SCLD_OK:
+                               case SCLD_PERMS:
+                                       break;
+                               case SCLD_EXISTS:
                                        errno = EEXIST;
                                        /* fallthru */
-                               case -1:
-                                       die_errno(_("cannot mkdir %s"), argv[0]);
-                                       break;
                                default:
+                                       die_errno(_("cannot mkdir %s"), argv[0]);
                                        break;
                                }
                                shared_repository = saved;
index b708517a3506517a25d4c05f3c9c92a660688f8d..b97373da3b2687e18b5eba96382049382d1536fd 100644 (file)
@@ -391,7 +391,7 @@ static int git_log_config(const char *var, const char *value, void *cb)
                default_show_root = git_config_bool(var, value);
                return 0;
        }
-       if (!prefixcmp(var, "color.decorate."))
+       if (starts_with(var, "color.decorate."))
                return parse_decorate_color_config(var, 15, value);
        if (!strcmp(var, "log.mailmap")) {
                use_mailmap_config = git_config_bool(var, value);
@@ -477,7 +477,7 @@ static int show_tag_object(const unsigned char *sha1, struct rev_info *rev)
                int new_offset = offset + 1;
                while (new_offset < size && buf[new_offset++] != '\n')
                        ; /* do nothing */
-               if (!prefixcmp(buf + offset, "tagger "))
+               if (starts_with(buf + offset, "tagger "))
                        show_tagger(buf + offset + 7,
                                    new_offset - offset - 7, rev);
                offset = new_offset;
@@ -882,7 +882,7 @@ static char *find_branch_name(struct rev_info *rev)
        ref = rev->cmdline.rev[positive].name;
        tip_sha1 = rev->cmdline.rev[positive].item->sha1;
        if (dwim_ref(ref, strlen(ref), branch_sha1, &full_ref) &&
-           !prefixcmp(full_ref, "refs/heads/") &&
+           starts_with(full_ref, "refs/heads/") &&
            !hashcmp(tip_sha1, branch_sha1))
                branch = xstrdup(full_ref + strlen("refs/heads/"));
        free(full_ref);
@@ -1388,7 +1388,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                        unsigned char sha1[20];
                        const char *ref;
                        ref = resolve_ref_unsafe("HEAD", sha1, 1, NULL);
-                       if (ref && !prefixcmp(ref, "refs/heads/"))
+                       if (ref && starts_with(ref, "refs/heads/"))
                                branch_name = xstrdup(ref + strlen("refs/heads/"));
                        else
                                branch_name = xstrdup(""); /* no branch */
index 25e83cfe9de3dc3cee19f03f82b56396eb6b65de..39e5144b9e38f1319f8fb881af6b1d34ad0c4089 100644 (file)
@@ -50,11 +50,11 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
                const char *arg = argv[i];
 
                if (*arg == '-') {
-                       if (!prefixcmp(arg, "--upload-pack=")) {
+                       if (starts_with(arg, "--upload-pack=")) {
                                uploadpack = arg + 14;
                                continue;
                        }
-                       if (!prefixcmp(arg, "--exec=")) {
+                       if (starts_with(arg, "--exec=")) {
                                uploadpack = arg + 7;
                                continue;
                        }
index 24a772d8e1b7355a58088d784fdc18cd54302b7d..2c3cd8eab7073669001ad605020577279ba7a07d 100644 (file)
@@ -328,11 +328,11 @@ static int check_header(const struct strbuf *line,
        }
 
        /* for inbody stuff */
-       if (!prefixcmp(line->buf, ">From") && isspace(line->buf[5])) {
+       if (starts_with(line->buf, ">From") && isspace(line->buf[5])) {
                ret = 1; /* Should this return 0? */
                goto check_header_out;
        }
-       if (!prefixcmp(line->buf, "[PATCH]") && isspace(line->buf[7])) {
+       if (starts_with(line->buf, "[PATCH]") && isspace(line->buf[7])) {
                for (i = 0; header[i]; i++) {
                        if (!memcmp("Subject", header[i], 7)) {
                                handle_header(&hdr_data[i], line);
@@ -361,7 +361,7 @@ static int is_rfc2822_header(const struct strbuf *line)
        char *cp = line->buf;
 
        /* Count mbox From headers as headers */
-       if (!prefixcmp(cp, "From ") || !prefixcmp(cp, ">From "))
+       if (starts_with(cp, "From ") || starts_with(cp, ">From "))
                return 1;
 
        while ((ch = *cp++)) {
@@ -671,11 +671,11 @@ static inline int patchbreak(const struct strbuf *line)
        size_t i;
 
        /* Beginning of a "diff -" header? */
-       if (!prefixcmp(line->buf, "diff -"))
+       if (starts_with(line->buf, "diff -"))
                return 1;
 
        /* CVS "Index: " line? */
-       if (!prefixcmp(line->buf, "Index: "))
+       if (starts_with(line->buf, "Index: "))
                return 1;
 
        /*
@@ -685,7 +685,7 @@ static inline int patchbreak(const struct strbuf *line)
        if (line->len < 4)
                return 0;
 
-       if (!prefixcmp(line->buf, "---")) {
+       if (starts_with(line->buf, "---")) {
                /* space followed by a filename? */
                if (line->buf[3] == ' ' && !isspace(line->buf[4]))
                        return 1;
@@ -986,7 +986,7 @@ static int mailinfo(FILE *in, FILE *out, const char *msg, const char *patch)
 
 static int git_mailinfo_config(const char *var, const char *value, void *unused)
 {
-       if (prefixcmp(var, "mailinfo."))
+       if (!starts_with(var, "mailinfo."))
                return git_default_config(var, value, unused);
        if (!strcmp(var, "mailinfo.scissors")) {
                use_scissors = git_config_bool(var, value);
@@ -1020,7 +1020,7 @@ int cmd_mailinfo(int argc, const char **argv, const char *prefix)
                        metainfo_charset = def_charset;
                else if (!strcmp(argv[1], "-n"))
                        metainfo_charset = NULL;
-               else if (!prefixcmp(argv[1], "--encoding="))
+               else if (starts_with(argv[1], "--encoding="))
                        metainfo_charset = argv[1] + 11;
                else if (!strcmp(argv[1], "--scissors"))
                        use_scissors = 1;
index a2923235e1024e554b0dc937c8222282d1855065..0ecde8da30615996f54563014e4774b67812c531 100644 (file)
@@ -48,19 +48,36 @@ static struct commit *get_commit_reference(const char *arg)
        return r;
 }
 
-static int handle_octopus(int count, const char **args, int reduce, int show_all)
+static int handle_independent(int count, const char **args)
 {
        struct commit_list *revs = NULL;
        struct commit_list *result;
        int i;
 
-       if (reduce)
-               show_all = 1;
+       for (i = count - 1; i >= 0; i--)
+               commit_list_insert(get_commit_reference(args[i]), &revs);
+
+       result = reduce_heads(revs);
+       if (!result)
+               return 1;
+
+       while (result) {
+               printf("%s\n", sha1_to_hex(result->item->object.sha1));
+               result = result->next;
+       }
+       return 0;
+}
+
+static int handle_octopus(int count, const char **args, int show_all)
+{
+       struct commit_list *revs = NULL;
+       struct commit_list *result;
+       int i;
 
        for (i = count - 1; i >= 0; i--)
                commit_list_insert(get_commit_reference(args[i]), &revs);
 
-       result = reduce ? reduce_heads(revs) : get_octopus_merge_bases(revs);
+       result = reduce_heads(get_octopus_merge_bases(revs));
 
        if (!result)
                return 1;
@@ -220,8 +237,11 @@ int cmd_merge_base(int argc, const char **argv, const char *prefix)
        if (cmdmode == 'r' && show_all)
                die("--independent cannot be used with --all");
 
-       if (cmdmode == 'r' || cmdmode == 'o')
-               return handle_octopus(argc, argv, cmdmode == 'r', show_all);
+       if (cmdmode == 'o')
+               return handle_octopus(argc, argv, show_all);
+
+       if (cmdmode == 'r')
+               return handle_independent(argc, argv);
 
        if (cmdmode == 'f') {
                if (argc < 1 || 2 < argc)
index 3a64f5d0bdbcc8d7d21edb01f285cc8b41755228..a90f28f34d2072b7d40964a7fa3cf46d3bb98cdd 100644 (file)
@@ -29,7 +29,7 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
        struct commit *result;
 
        init_merge_options(&o);
-       if (argv[0] && !suffixcmp(argv[0], "-subtree"))
+       if (argv[0] && ends_with(argv[0], "-subtree"))
                o.subtree_shift = "";
 
        if (argc < 4)
@@ -38,7 +38,7 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
        for (i = 1; i < argc; ++i) {
                const char *arg = argv[i];
 
-               if (!prefixcmp(arg, "--")) {
+               if (starts_with(arg, "--")) {
                        if (!arg[2])
                                break;
                        if (parse_merge_opt(&o, arg + 2))
index 41fb66dec2c2fd9645a72c28c362067fb3bcc16c..e576a7fdc619303a65e859920a1ab6936db2626c 100644 (file)
@@ -367,7 +367,7 @@ static void squash_message(struct commit *commit, struct commit_list *remotehead
                        sha1_to_hex(commit->object.sha1));
                pretty_print_commit(&ctx, commit, &out);
        }
-       if (write(fd, out.buf, out.len) < 0)
+       if (write_in_full(fd, out.buf, out.len) != out.len)
                die_errno(_("Writing SQUASH_MSG"));
        if (close(fd))
                die_errno(_("Finishing SQUASH_MSG"));
@@ -446,17 +446,17 @@ static void merge_name(const char *remote, struct strbuf *msg)
                die(_("'%s' does not point to a commit"), remote);
 
        if (dwim_ref(remote, strlen(remote), branch_head, &found_ref) > 0) {
-               if (!prefixcmp(found_ref, "refs/heads/")) {
+               if (starts_with(found_ref, "refs/heads/")) {
                        strbuf_addf(msg, "%s\t\tbranch '%s' of .\n",
                                    sha1_to_hex(branch_head), remote);
                        goto cleanup;
                }
-               if (!prefixcmp(found_ref, "refs/tags/")) {
+               if (starts_with(found_ref, "refs/tags/")) {
                        strbuf_addf(msg, "%s\t\ttag '%s' of .\n",
                                    sha1_to_hex(branch_head), remote);
                        goto cleanup;
                }
-               if (!prefixcmp(found_ref, "refs/remotes/")) {
+               if (starts_with(found_ref, "refs/remotes/")) {
                        strbuf_addf(msg, "%s\t\tremote-tracking branch '%s' of .\n",
                                    sha1_to_hex(branch_head), remote);
                        goto cleanup;
@@ -570,8 +570,8 @@ static int git_merge_config(const char *k, const char *v, void *cb)
 {
        int status;
 
-       if (branch && !prefixcmp(k, "branch.") &&
-               !prefixcmp(k + 7, branch) &&
+       if (branch && starts_with(k, "branch.") &&
+               starts_with(k + 7, branch) &&
                !strcmp(k + 7 + strlen(branch), ".mergeoptions")) {
                free(branch_mergeoptions);
                branch_mergeoptions = xstrdup(v);
@@ -1106,7 +1106,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
         * current branch.
         */
        branch = branch_to_free = resolve_refdup("HEAD", head_sha1, 0, &flag);
-       if (branch && !prefixcmp(branch, "refs/heads/"))
+       if (branch && starts_with(branch, "refs/heads/"))
                branch += 11;
        if (!branch || is_null_sha1(head_sha1))
                head_commit = NULL;
index 2e0e61b6517d74edc8b7ef2e56c75927385e7c85..21c46d1636e6e87dd9eaaca60ac08ebbbc4efec3 100644 (file)
@@ -16,9 +16,12 @@ static const char * const builtin_mv_usage[] = {
        NULL
 };
 
+#define DUP_BASENAME 1
+#define KEEP_TRAILING_SLASH 2
+
 static const char **internal_copy_pathspec(const char *prefix,
                                           const char **pathspec,
-                                          int count, int base_name)
+                                          int count, unsigned flags)
 {
        int i;
        const char **result = xmalloc((count + 1) * sizeof(const char *));
@@ -27,11 +30,12 @@ static const char **internal_copy_pathspec(const char *prefix,
        for (i = 0; i < count; i++) {
                int length = strlen(result[i]);
                int to_copy = length;
-               while (to_copy > 0 && is_dir_sep(result[i][to_copy - 1]))
+               while (!(flags & KEEP_TRAILING_SLASH) &&
+                      to_copy > 0 && is_dir_sep(result[i][to_copy - 1]))
                        to_copy--;
-               if (to_copy != length || base_name) {
+               if (to_copy != length || flags & DUP_BASENAME) {
                        char *it = xmemdupz(result[i], to_copy);
-                       if (base_name) {
+                       if (flags & DUP_BASENAME) {
                                result[i] = xstrdup(basename(it));
                                free(it);
                        } else
@@ -87,16 +91,21 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
 
        source = internal_copy_pathspec(prefix, argv, argc, 0);
        modes = xcalloc(argc, sizeof(enum update_mode));
-       dest_path = internal_copy_pathspec(prefix, argv + argc, 1, 0);
+       /*
+        * Keep trailing slash, needed to let
+        * "git mv file no-such-dir/" error out.
+        */
+       dest_path = internal_copy_pathspec(prefix, argv + argc, 1,
+                                          KEEP_TRAILING_SLASH);
        submodule_gitfile = xcalloc(argc, sizeof(char *));
 
        if (dest_path[0][0] == '\0')
                /* special case: "." was normalized to "" */
-               destination = internal_copy_pathspec(dest_path[0], argv, argc, 1);
+               destination = internal_copy_pathspec(dest_path[0], argv, argc, DUP_BASENAME);
        else if (!lstat(dest_path[0], &st) &&
                        S_ISDIR(st.st_mode)) {
                dest_path[0] = add_slash(dest_path[0]);
-               destination = internal_copy_pathspec(dest_path[0], argv, argc, 1);
+               destination = internal_copy_pathspec(dest_path[0], argv, argc, DUP_BASENAME);
        } else {
                if (argc != 1)
                        die("destination '%s' is not a directory", dest_path[0]);
@@ -205,6 +214,8 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
                        }
                } else if (string_list_has_string(&src_for_dst, dst))
                        bad = _("multiple sources for the same target");
+               else if (is_dir_sep(dst[strlen(dst) - 1]))
+                       bad = _("destination directory does not exist");
                else
                        string_list_insert(&src_for_dst, dst);
 
index 23daaa7d99b40e1bbd38a2743bc0d9ed0c38eda9..0b21d7e5b297ff3d2d1a73d8a74c8bc37e672ccb 100644 (file)
@@ -100,9 +100,9 @@ static const char *name_ref_abbrev(const char *refname, int shorten_unambiguous)
 {
        if (shorten_unambiguous)
                refname = shorten_unambiguous_ref(refname, 0);
-       else if (!prefixcmp(refname, "refs/heads/"))
+       else if (starts_with(refname, "refs/heads/"))
                refname = refname + 11;
-       else if (!prefixcmp(refname, "refs/"))
+       else if (starts_with(refname, "refs/"))
                refname = refname + 5;
        return refname;
 }
@@ -148,7 +148,7 @@ static int name_ref(const char *path, const unsigned char *sha1, int flags, void
        int can_abbreviate_output = data->tags_only && data->name_only;
        int deref = 0;
 
-       if (data->tags_only && prefixcmp(path, "refs/tags/"))
+       if (data->tags_only && !starts_with(path, "refs/tags/"))
                return 0;
 
        if (data->ref_filter) {
index d459e23c4225ae0f8084cfd756563c2b7ab85a60..2b24d059b509d28d587066440ed131ce2f85464f 100644 (file)
@@ -347,7 +347,7 @@ static struct notes_tree *init_notes_check(const char *subcommand)
        init_notes(NULL, NULL, NULL, 0);
        t = &default_notes_tree;
 
-       if (prefixcmp(t->ref, "refs/notes/"))
+       if (!starts_with(t->ref, "refs/notes/"))
                die("Refusing to %s notes in %s (outside of refs/notes/)",
                    subcommand, t->ref);
        return t;
index 36273dd6f0ebf3eab1062a0e24b9aa0c1834ce02..541667f1026d7ba62be8e029c31138d5661f65c7 100644 (file)
@@ -737,8 +737,6 @@ static void write_pack_file(void)
                        f = create_tmp_packfile(&pack_tmp_name);
 
                offset = write_pack_header(f, nr_remaining);
-               if (!offset)
-                       die_errno("unable to write pack header");
                nr_written = 0;
                for (; i < nr_objects; i++) {
                        struct object_entry *e = write_order[i];
@@ -2032,7 +2030,7 @@ static int add_ref_tag(const char *path, const unsigned char *sha1, int flag, vo
 {
        unsigned char peeled[20];
 
-       if (!prefixcmp(path, "refs/tags/") && /* is a tag? */
+       if (starts_with(path, "refs/tags/") && /* is a tag? */
            !peel_ref(path, peeled)        && /* peelable? */
            locate_object_entry(peeled))      /* object packed? */
                add_object_entry(sha1, OBJ_TAG, NULL, 0);
index fa6ce42f44f82d34299f2dc0109be4d831cb3d48..fcf5fb6129c05156c7fd7bbb22ac380dbd0b3245 100644 (file)
@@ -10,58 +10,62 @@ static const char * const prune_packed_usage[] = {
 
 static struct progress *progress;
 
-static void prune_dir(int i, DIR *dir, char *pathname, int len, int opts)
+static void prune_dir(int i, DIR *dir, struct strbuf *pathname, int opts)
 {
        struct dirent *de;
        char hex[40];
+       int top_len = pathname->len;
 
        sprintf(hex, "%02x", i);
        while ((de = readdir(dir)) != NULL) {
                unsigned char sha1[20];
                if (strlen(de->d_name) != 38)
                        continue;
-               memcpy(hex+2, de->d_name, 38);
+               memcpy(hex + 2, de->d_name, 38);
                if (get_sha1_hex(hex, sha1))
                        continue;
                if (!has_sha1_pack(sha1))
                        continue;
-               memcpy(pathname + len, de->d_name, 38);
+
+               strbuf_add(pathname, de->d_name, 38);
                if (opts & PRUNE_PACKED_DRY_RUN)
-                       printf("rm -f %s\n", pathname);
+                       printf("rm -f %s\n", pathname->buf);
                else
-                       unlink_or_warn(pathname);
+                       unlink_or_warn(pathname->buf);
                display_progress(progress, i + 1);
+               strbuf_setlen(pathname, top_len);
        }
 }
 
 void prune_packed_objects(int opts)
 {
        int i;
-       static char pathname[PATH_MAX];
        const char *dir = get_object_directory();
-       int len = strlen(dir);
+       struct strbuf pathname = STRBUF_INIT;
+       int top_len;
 
+       strbuf_addstr(&pathname, dir);
        if (opts & PRUNE_PACKED_VERBOSE)
                progress = start_progress_delay("Removing duplicate objects",
                        256, 95, 2);
 
-       if (len > PATH_MAX - 42)
-               die("impossible object directory");
-       memcpy(pathname, dir, len);
-       if (len && pathname[len-1] != '/')
-               pathname[len++] = '/';
+       if (pathname.len && pathname.buf[pathname.len - 1] != '/')
+               strbuf_addch(&pathname, '/');
+
+       top_len = pathname.len;
        for (i = 0; i < 256; i++) {
                DIR *d;
 
                display_progress(progress, i + 1);
-               sprintf(pathname + len, "%02x/", i);
-               d = opendir(pathname);
+               strbuf_setlen(&pathname, top_len);
+               strbuf_addf(&pathname, "%02x/", i);
+               d = opendir(pathname.buf);
                if (!d)
                        continue;
-               prune_dir(i, d, pathname, len + 3, opts);
+               prune_dir(i, d, &pathname, opts);
                closedir(d);
-               pathname[len + 2] = '\0';
-               rmdir(pathname);
+               strbuf_setlen(&pathname, top_len + 2);
+               rmdir(pathname.buf);
        }
        stop_progress(&progress);
 }
index 6366917c6de55e171a7f697b22f1de10293addf5..de43b26cfe9c7610c13e3320bdac9fa2049e4db6 100644 (file)
@@ -17,9 +17,8 @@ static int verbose;
 static unsigned long expire;
 static int show_progress = -1;
 
-static int prune_tmp_object(const char *path, const char *filename)
+static int prune_tmp_file(const char *fullpath)
 {
-       const char *fullpath = mkpath("%s/%s", path, filename);
        struct stat st;
        if (lstat(fullpath, &st))
                return error("Could not stat '%s'", fullpath);
@@ -32,9 +31,8 @@ static int prune_tmp_object(const char *path, const char *filename)
        return 0;
 }
 
-static int prune_object(char *path, const char *filename, const unsigned char *sha1)
+static int prune_object(const char *fullpath, const unsigned char *sha1)
 {
-       const char *fullpath = mkpath("%s/%s", path, filename);
        struct stat st;
        if (lstat(fullpath, &st))
                return error("Could not stat '%s'", fullpath);
@@ -50,9 +48,10 @@ static int prune_object(char *path, const char *filename, const unsigned char *s
        return 0;
 }
 
-static int prune_dir(int i, char *path)
+static int prune_dir(int i, struct strbuf *path)
 {
-       DIR *dir = opendir(path);
+       size_t baselen = path->len;
+       DIR *dir = opendir(path->buf);
        struct dirent *de;
 
        if (!dir)
@@ -77,28 +76,39 @@ static int prune_dir(int i, char *path)
                        if (lookup_object(sha1))
                                continue;
 
-                       prune_object(path, de->d_name, sha1);
+                       strbuf_addf(path, "/%s", de->d_name);
+                       prune_object(path->buf, sha1);
+                       strbuf_setlen(path, baselen);
                        continue;
                }
-               if (!prefixcmp(de->d_name, "tmp_obj_")) {
-                       prune_tmp_object(path, de->d_name);
+               if (starts_with(de->d_name, "tmp_obj_")) {
+                       strbuf_addf(path, "/%s", de->d_name);
+                       prune_tmp_file(path->buf);
+                       strbuf_setlen(path, baselen);
                        continue;
                }
-               fprintf(stderr, "bad sha1 file: %s/%s\n", path, de->d_name);
+               fprintf(stderr, "bad sha1 file: %s/%s\n", path->buf, de->d_name);
        }
        closedir(dir);
        if (!show_only)
-               rmdir(path);
+               rmdir(path->buf);
        return 0;
 }
 
 static void prune_object_dir(const char *path)
 {
+       struct strbuf buf = STRBUF_INIT;
+       size_t baselen;
        int i;
+
+       strbuf_addstr(&buf, path);
+       strbuf_addch(&buf, '/');
+       baselen = buf.len;
+
        for (i = 0; i < 256; i++) {
-               static char dir[4096];
-               sprintf(dir, "%s/%02x", path, i);
-               prune_dir(i, dir);
+               strbuf_addf(&buf, "%02x", i);
+               prune_dir(i, &buf);
+               strbuf_setlen(&buf, baselen);
        }
 }
 
@@ -119,8 +129,8 @@ static void remove_temporary_files(const char *path)
                return;
        }
        while ((de = readdir(dir)) != NULL)
-               if (!prefixcmp(de->d_name, "tmp_"))
-                       prune_tmp_object(path, de->d_name);
+               if (starts_with(de->d_name, "tmp_"))
+                       prune_tmp_file(mkpath("%s/%s", path, de->d_name));
        closedir(dir);
 }
 
@@ -170,5 +180,9 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
        s = mkpathdup("%s/pack", get_object_directory());
        remove_temporary_files(s);
        free(s);
+
+       if (is_repository_shallow())
+               prune_shallow(show_only);
+
        return 0;
 }
index a73982a30879f89be939034e462df68ee2c51c0b..0e50ddbb01342128d9118217118726000bdeaff6 100644 (file)
@@ -35,35 +35,75 @@ static void add_refspec(const char *ref)
        refspec[refspec_nr-1] = ref;
 }
 
-static void set_refspecs(const char **refs, int nr)
+static const char *map_refspec(const char *ref,
+                              struct remote *remote, struct ref *local_refs)
 {
+       struct ref *matched = NULL;
+
+       /* Does "ref" uniquely name our ref? */
+       if (count_refspec_match(ref, local_refs, &matched) != 1)
+               return ref;
+
+       if (remote->push) {
+               struct refspec query;
+               memset(&query, 0, sizeof(struct refspec));
+               query.src = matched->name;
+               if (!query_refspecs(remote->push, remote->push_refspec_nr, &query) &&
+                   query.dst) {
+                       struct strbuf buf = STRBUF_INIT;
+                       strbuf_addf(&buf, "%s%s:%s",
+                                   query.force ? "+" : "",
+                                   query.src, query.dst);
+                       return strbuf_detach(&buf, NULL);
+               }
+       }
+
+       if (push_default == PUSH_DEFAULT_UPSTREAM &&
+           !prefixcmp(matched->name, "refs/heads/")) {
+               struct branch *branch = branch_get(matched->name + 11);
+               if (branch->merge_nr == 1 && branch->merge[0]->src) {
+                       struct strbuf buf = STRBUF_INIT;
+                       strbuf_addf(&buf, "%s:%s",
+                                   ref, branch->merge[0]->src);
+                       return strbuf_detach(&buf, NULL);
+               }
+       }
+
+       return ref;
+}
+
+static void set_refspecs(const char **refs, int nr, const char *repo)
+{
+       struct remote *remote = NULL;
+       struct ref *local_refs = NULL;
        int i;
+
        for (i = 0; i < nr; i++) {
                const char *ref = refs[i];
                if (!strcmp("tag", ref)) {
-                       char *tag;
-                       int len;
+                       struct strbuf tagref = STRBUF_INIT;
                        if (nr <= ++i)
                                die(_("tag shorthand without <tag>"));
-                       len = strlen(refs[i]) + 11;
-                       if (deleterefs) {
-                               tag = xmalloc(len+1);
-                               strcpy(tag, ":refs/tags/");
-                       } else {
-                               tag = xmalloc(len);
-                               strcpy(tag, "refs/tags/");
+                       ref = refs[i];
+                       if (deleterefs)
+                               strbuf_addf(&tagref, ":refs/tags/%s", ref);
+                       else
+                               strbuf_addf(&tagref, "refs/tags/%s", ref);
+                       ref = strbuf_detach(&tagref, NULL);
+               } else if (deleterefs) {
+                       struct strbuf delref = STRBUF_INIT;
+                       if (strchr(ref, ':'))
+                               die(_("--delete only accepts plain target ref names"));
+                       strbuf_addf(&delref, ":%s", ref);
+                       ref = strbuf_detach(&delref, NULL);
+               } else if (!strchr(ref, ':')) {
+                       if (!remote) {
+                               /* lazily grab remote and local_refs */
+                               remote = remote_get(repo);
+                               local_refs = get_local_heads();
                        }
-                       strcat(tag, refs[i]);
-                       ref = tag;
-               } else if (deleterefs && !strchr(ref, ':')) {
-                       char *delref;
-                       int len = strlen(ref)+1;
-                       delref = xmalloc(len+1);
-                       strcpy(delref, ":");
-                       strcat(delref, ref);
-                       ref = delref;
-               } else if (deleterefs)
-                       die(_("--delete only accepts plain target ref names"));
+                       ref = map_refspec(ref, remote, local_refs);
+               }
                add_refspec(ref);
        }
 }
@@ -501,7 +541,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)
 
        if (argc > 0) {
                repo = argv[0];
-               set_refspecs(argv + 1, argc - 1);
+               set_refspecs(argv + 1, argc - 1, repo);
        }
 
        rc = do_push(repo, flags);
index 67ce1ef105d1494e7905e66750b14676e082e11c..85bba356fab7743506f00bb0c5ca955eb9112bd5 100644 (file)
@@ -13,6 +13,7 @@
 #include "string-list.h"
 #include "sha1-array.h"
 #include "connected.h"
+#include "argv-array.h"
 #include "version.h"
 
 static const char receive_pack_usage[] = "git receive-pack <git-dir>";
@@ -43,6 +44,8 @@ static int fix_thin = 1;
 static const char *head_name;
 static void *head_name_to_free;
 static int sent_capabilities;
+static int shallow_update;
+static const char *alt_shallow_file;
 
 static enum deny_action parse_deny_action(const char *var, const char *value)
 {
@@ -121,6 +124,11 @@ static int receive_pack_config(const char *var, const char *value, void *cb)
                return 0;
        }
 
+       if (strcmp(var, "receive.shallowupdate") == 0) {
+               shallow_update = git_config_bool(var, value);
+               return 0;
+       }
+
        return git_default_config(var, value, cb);
 }
 
@@ -178,6 +186,8 @@ static void write_head_info(void)
        if (!sent_capabilities)
                show_ref("capabilities^{}", null_sha1);
 
+       advertise_shallow_grafts(1);
+
        /* EOF */
        packet_flush(1);
 }
@@ -187,6 +197,7 @@ struct command {
        const char *error_string;
        unsigned int skip_update:1,
                     did_not_exist:1;
+       int index;
        unsigned char old_sha1[20];
        unsigned char new_sha1[20];
        char ref_name[FLEX_ARRAY]; /* more */
@@ -418,7 +429,46 @@ static void refuse_unconfigured_deny_delete_current(void)
                rp_error("%s", refuse_unconfigured_deny_delete_current_msg[i]);
 }
 
-static const char *update(struct command *cmd)
+static int command_singleton_iterator(void *cb_data, unsigned char sha1[20]);
+static int update_shallow_ref(struct command *cmd, struct shallow_info *si)
+{
+       static struct lock_file shallow_lock;
+       struct sha1_array extra = SHA1_ARRAY_INIT;
+       const char *alt_file;
+       uint32_t mask = 1 << (cmd->index % 32);
+       int i;
+
+       trace_printf_key("GIT_TRACE_SHALLOW",
+                        "shallow: update_shallow_ref %s\n", cmd->ref_name);
+       for (i = 0; i < si->shallow->nr; i++)
+               if (si->used_shallow[i] &&
+                   (si->used_shallow[i][cmd->index / 32] & mask) &&
+                   !delayed_reachability_test(si, i))
+                       sha1_array_append(&extra, si->shallow->sha1[i]);
+
+       setup_alternate_shallow(&shallow_lock, &alt_file, &extra);
+       if (check_shallow_connected(command_singleton_iterator,
+                                   0, cmd, alt_file)) {
+               rollback_lock_file(&shallow_lock);
+               sha1_array_clear(&extra);
+               return -1;
+       }
+
+       commit_lock_file(&shallow_lock);
+
+       /*
+        * Make sure setup_alternate_shallow() for the next ref does
+        * not lose these new roots..
+        */
+       for (i = 0; i < extra.nr; i++)
+               register_shallow(extra.sha1[i]);
+
+       si->shallow_ref[cmd->index] = 0;
+       sha1_array_clear(&extra);
+       return 0;
+}
+
+static const char *update(struct command *cmd, struct shallow_info *si)
 {
        const char *name = cmd->ref_name;
        struct strbuf namespaced_name_buf = STRBUF_INIT;
@@ -428,7 +478,7 @@ static const char *update(struct command *cmd)
        struct ref_lock *lock;
 
        /* only refs/... are allowed */
-       if (prefixcmp(name, "refs/") || check_refname_format(name + 5, 0)) {
+       if (!starts_with(name, "refs/") || check_refname_format(name + 5, 0)) {
                rp_error("refusing to create funny ref '%s' remotely", name);
                return "funny refname";
        }
@@ -459,7 +509,7 @@ static const char *update(struct command *cmd)
        }
 
        if (!is_null_sha1(old_sha1) && is_null_sha1(new_sha1)) {
-               if (deny_deletes && !prefixcmp(name, "refs/heads/")) {
+               if (deny_deletes && starts_with(name, "refs/heads/")) {
                        rp_error("denying ref deletion for %s", name);
                        return "deletion prohibited";
                }
@@ -483,7 +533,7 @@ static const char *update(struct command *cmd)
 
        if (deny_non_fast_forwards && !is_null_sha1(new_sha1) &&
            !is_null_sha1(old_sha1) &&
-           !prefixcmp(name, "refs/heads/")) {
+           starts_with(name, "refs/heads/")) {
                struct object *old_object, *new_object;
                struct commit *old_commit, *new_commit;
 
@@ -526,6 +576,10 @@ static const char *update(struct command *cmd)
                return NULL; /* good */
        }
        else {
+               if (shallow_update && si->shallow_ref[cmd->index] &&
+                   update_shallow_ref(cmd, si))
+                       return "shallow error";
+
                lock = lock_any_ref_for_update(namespaced_name, old_sha1,
                                               0, NULL);
                if (!lock) {
@@ -666,12 +720,16 @@ static int command_singleton_iterator(void *cb_data, unsigned char sha1[20])
        return 0;
 }
 
-static void set_connectivity_errors(struct command *commands)
+static void set_connectivity_errors(struct command *commands,
+                                   struct shallow_info *si)
 {
        struct command *cmd;
 
        for (cmd = commands; cmd; cmd = cmd->next) {
                struct command *singleton = cmd;
+               if (shallow_update && si->shallow_ref[cmd->index])
+                       /* to be checked in update_shallow_ref() */
+                       continue;
                if (!check_everything_connected(command_singleton_iterator,
                                                0, &singleton))
                        continue;
@@ -679,18 +737,26 @@ static void set_connectivity_errors(struct command *commands)
        }
 }
 
+struct iterate_data {
+       struct command *cmds;
+       struct shallow_info *si;
+};
+
 static int iterate_receive_command_list(void *cb_data, unsigned char sha1[20])
 {
-       struct command **cmd_list = cb_data;
+       struct iterate_data *data = cb_data;
+       struct command **cmd_list = &data->cmds;
        struct command *cmd = *cmd_list;
 
-       while (cmd) {
-               if (!is_null_sha1(cmd->new_sha1)) {
+       for (; cmd; cmd = cmd->next) {
+               if (shallow_update && data->si->shallow_ref[cmd->index])
+                       /* to be checked in update_shallow_ref() */
+                       continue;
+               if (!is_null_sha1(cmd->new_sha1) && !cmd->skip_update) {
                        hashcpy(sha1, cmd->new_sha1);
                        *cmd_list = cmd->next;
                        return 0;
                }
-               cmd = cmd->next;
        }
        *cmd_list = NULL;
        return -1; /* end of list */
@@ -710,10 +776,14 @@ static void reject_updates_to_hidden(struct command *commands)
        }
 }
 
-static void execute_commands(struct command *commands, const char *unpacker_error)
+static void execute_commands(struct command *commands,
+                            const char *unpacker_error,
+                            struct shallow_info *si)
 {
+       int checked_connectivity;
        struct command *cmd;
        unsigned char sha1[20];
+       struct iterate_data data;
 
        if (unpacker_error) {
                for (cmd = commands; cmd; cmd = cmd->next)
@@ -721,10 +791,10 @@ static void execute_commands(struct command *commands, const char *unpacker_erro
                return;
        }
 
-       cmd = commands;
-       if (check_everything_connected(iterate_receive_command_list,
-                                      0, &cmd))
-               set_connectivity_errors(commands);
+       data.cmds = commands;
+       data.si = si;
+       if (check_everything_connected(iterate_receive_command_list, 0, &data))
+               set_connectivity_errors(commands, si);
 
        reject_updates_to_hidden(commands);
 
@@ -741,6 +811,7 @@ static void execute_commands(struct command *commands, const char *unpacker_erro
        free(head_name_to_free);
        head_name = head_name_to_free = resolve_refdup("HEAD", sha1, 0, NULL);
 
+       checked_connectivity = 1;
        for (cmd = commands; cmd; cmd = cmd->next) {
                if (cmd->error_string)
                        continue;
@@ -748,11 +819,26 @@ static void execute_commands(struct command *commands, const char *unpacker_erro
                if (cmd->skip_update)
                        continue;
 
-               cmd->error_string = update(cmd);
+               cmd->error_string = update(cmd, si);
+               if (shallow_update && !cmd->error_string &&
+                   si->shallow_ref[cmd->index]) {
+                       error("BUG: connectivity check has not been run on ref %s",
+                             cmd->ref_name);
+                       checked_connectivity = 0;
+               }
+       }
+
+       if (shallow_update) {
+               if (!checked_connectivity)
+                       error("BUG: run 'git fsck' for safety.\n"
+                             "If there are errors, try to remove "
+                             "the reported refs above");
+               if (alt_shallow_file && *alt_shallow_file)
+                       unlink(alt_shallow_file);
        }
 }
 
-static struct command *read_head_info(void)
+static struct command *read_head_info(struct sha1_array *shallow)
 {
        struct command *commands = NULL;
        struct command **p = &commands;
@@ -766,6 +852,14 @@ static struct command *read_head_info(void)
                line = packet_read_line(0, &len);
                if (!line)
                        break;
+
+               if (len == 48 && starts_with(line, "shallow ")) {
+                       if (get_sha1_hex(line + 8, old_sha1))
+                               die("protocol error: expected shallow sha, got '%s'", line + 8);
+                       sha1_array_append(shallow, old_sha1);
+                       continue;
+               }
+
                if (len < 83 ||
                    line[40] != ' ' ||
                    line[81] != ' ' ||
@@ -817,11 +911,14 @@ static const char *parse_pack_header(struct pack_header *hdr)
 
 static const char *pack_lockfile;
 
-static const char *unpack(int err_fd)
+static const char *unpack(int err_fd, struct shallow_info *si)
 {
        struct pack_header hdr;
+       struct argv_array av = ARGV_ARRAY_INIT;
        const char *hdr_err;
+       int status;
        char hdr_arg[38];
+       struct child_process child;
        int fsck_objects = (receive_fsck_objects >= 0
                            ? receive_fsck_objects
                            : transfer_fsck_objects >= 0
@@ -838,72 +935,63 @@ static const char *unpack(int err_fd)
                        "--pack_header=%"PRIu32",%"PRIu32,
                        ntohl(hdr.hdr_version), ntohl(hdr.hdr_entries));
 
+       if (si->nr_ours || si->nr_theirs) {
+               alt_shallow_file = setup_temporary_shallow(si->shallow);
+               argv_array_pushl(&av, "--shallow-file", alt_shallow_file, NULL);
+       }
+
+       memset(&child, 0, sizeof(child));
        if (ntohl(hdr.hdr_entries) < unpack_limit) {
-               int code, i = 0;
-               struct child_process child;
-               const char *unpacker[5];
-               unpacker[i++] = "unpack-objects";
+               argv_array_pushl(&av, "unpack-objects", hdr_arg, NULL);
                if (quiet)
-                       unpacker[i++] = "-q";
+                       argv_array_push(&av, "-q");
                if (fsck_objects)
-                       unpacker[i++] = "--strict";
-               unpacker[i++] = hdr_arg;
-               unpacker[i++] = NULL;
-               memset(&child, 0, sizeof(child));
-               child.argv = unpacker;
+                       argv_array_push(&av, "--strict");
+               child.argv = av.argv;
                child.no_stdout = 1;
                child.err = err_fd;
                child.git_cmd = 1;
-               code = run_command(&child);
-               if (!code)
-                       return NULL;
-               return "unpack-objects abnormal exit";
+               status = run_command(&child);
+               if (status)
+                       return "unpack-objects abnormal exit";
        } else {
-               const char *keeper[7];
-               int s, status, i = 0;
+               int s;
                char keep_arg[256];
-               struct child_process ip;
 
                s = sprintf(keep_arg, "--keep=receive-pack %"PRIuMAX" on ", (uintmax_t) getpid());
                if (gethostname(keep_arg + s, sizeof(keep_arg) - s))
                        strcpy(keep_arg + s, "localhost");
 
-               keeper[i++] = "index-pack";
-               keeper[i++] = "--stdin";
+               argv_array_pushl(&av, "index-pack",
+                                "--stdin", hdr_arg, keep_arg, NULL);
                if (fsck_objects)
-                       keeper[i++] = "--strict";
+                       argv_array_push(&av, "--strict");
                if (fix_thin)
-                       keeper[i++] = "--fix-thin";
-               keeper[i++] = hdr_arg;
-               keeper[i++] = keep_arg;
-               keeper[i++] = NULL;
-               memset(&ip, 0, sizeof(ip));
-               ip.argv = keeper;
-               ip.out = -1;
-               ip.err = err_fd;
-               ip.git_cmd = 1;
-               status = start_command(&ip);
-               if (status) {
+                       argv_array_push(&av, "--fix-thin");
+               child.argv = av.argv;
+               child.out = -1;
+               child.err = err_fd;
+               child.git_cmd = 1;
+               status = start_command(&child);
+               if (status)
                        return "index-pack fork failed";
-               }
-               pack_lockfile = index_pack_lockfile(ip.out);
-               close(ip.out);
-               status = finish_command(&ip);
-               if (!status) {
-                       reprepare_packed_git();
-                       return NULL;
-               }
-               return "index-pack abnormal exit";
+               pack_lockfile = index_pack_lockfile(child.out);
+               close(child.out);
+               status = finish_command(&child);
+               if (status)
+                       return "index-pack abnormal exit";
+               reprepare_packed_git();
        }
+       return NULL;
 }
 
-static const char *unpack_with_sideband(void)
+static const char *unpack_with_sideband(struct shallow_info *si)
 {
        struct async muxer;
        const char *ret;
 
        if (!use_sideband)
-               return unpack(0);
+               return unpack(0, si);
 
        memset(&muxer, 0, sizeof(muxer));
        muxer.proc = copy_to_sideband;
@@ -911,12 +999,101 @@ static const char *unpack_with_sideband(void)
        if (start_async(&muxer))
                return NULL;
 
-       ret = unpack(muxer.in);
+       ret = unpack(muxer.in, si);
 
        finish_async(&muxer);
        return ret;
 }
 
+static void prepare_shallow_update(struct command *commands,
+                                  struct shallow_info *si)
+{
+       int i, j, k, bitmap_size = (si->ref->nr + 31) / 32;
+
+       si->used_shallow = xmalloc(sizeof(*si->used_shallow) *
+                                  si->shallow->nr);
+       assign_shallow_commits_to_refs(si, si->used_shallow, NULL);
+
+       si->need_reachability_test =
+               xcalloc(si->shallow->nr, sizeof(*si->need_reachability_test));
+       si->reachable =
+               xcalloc(si->shallow->nr, sizeof(*si->reachable));
+       si->shallow_ref = xcalloc(si->ref->nr, sizeof(*si->shallow_ref));
+
+       for (i = 0; i < si->nr_ours; i++)
+               si->need_reachability_test[si->ours[i]] = 1;
+
+       for (i = 0; i < si->shallow->nr; i++) {
+               if (!si->used_shallow[i])
+                       continue;
+               for (j = 0; j < bitmap_size; j++) {
+                       if (!si->used_shallow[i][j])
+                               continue;
+                       si->need_reachability_test[i]++;
+                       for (k = 0; k < 32; k++)
+                               if (si->used_shallow[i][j] & (1 << k))
+                                       si->shallow_ref[j * 32 + k]++;
+               }
+
+               /*
+                * true for those associated with some refs and belong
+                * in "ours" list aka "step 7 not done yet"
+                */
+               si->need_reachability_test[i] =
+                       si->need_reachability_test[i] > 1;
+       }
+
+       /*
+        * keep hooks happy by forcing a temporary shallow file via
+        * env variable because we can't add --shallow-file to every
+        * command. check_everything_connected() will be done with
+        * true .git/shallow though.
+        */
+       setenv(GIT_SHALLOW_FILE_ENVIRONMENT, alt_shallow_file, 1);
+}
+
+static void update_shallow_info(struct command *commands,
+                               struct shallow_info *si,
+                               struct sha1_array *ref)
+{
+       struct command *cmd;
+       int *ref_status;
+       remove_nonexistent_theirs_shallow(si);
+       if (!si->nr_ours && !si->nr_theirs) {
+               shallow_update = 0;
+               return;
+       }
+
+       for (cmd = commands; cmd; cmd = cmd->next) {
+               if (is_null_sha1(cmd->new_sha1))
+                       continue;
+               sha1_array_append(ref, cmd->new_sha1);
+               cmd->index = ref->nr - 1;
+       }
+       si->ref = ref;
+
+       if (shallow_update) {
+               prepare_shallow_update(commands, si);
+               return;
+       }
+
+       ref_status = xmalloc(sizeof(*ref_status) * ref->nr);
+       assign_shallow_commits_to_refs(si, NULL, ref_status);
+       for (cmd = commands; cmd; cmd = cmd->next) {
+               if (is_null_sha1(cmd->new_sha1))
+                       continue;
+               if (ref_status[cmd->index]) {
+                       cmd->error_string = "shallow update not allowed";
+                       cmd->skip_update = 1;
+               }
+       }
+       if (alt_shallow_file && *alt_shallow_file) {
+               unlink(alt_shallow_file);
+               alt_shallow_file = NULL;
+       }
+       free(ref_status);
+}
+
 static void report(struct command *commands, const char *unpack_status)
 {
        struct command *cmd;
@@ -958,6 +1135,9 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
        int i;
        char *dir = NULL;
        struct command *commands;
+       struct sha1_array shallow = SHA1_ARRAY_INIT;
+       struct sha1_array ref = SHA1_ARRAY_INIT;
+       struct shallow_info si;
 
        packet_trace_identity("receive-pack");
 
@@ -998,9 +1178,6 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
        if (!enter_repo(dir, 0))
                die("'%s' does not appear to be a git repository", dir);
 
-       if (is_repository_shallow())
-               die("attempt to push into a shallow repository");
-
        git_config(receive_pack_config, NULL);
 
        if (0 <= transfer_unpack_limit)
@@ -1014,12 +1191,17 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
        if (advertise_refs)
                return 0;
 
-       if ((commands = read_head_info()) != NULL) {
+       if ((commands = read_head_info(&shallow)) != NULL) {
                const char *unpack_status = NULL;
 
-               if (!delete_only(commands))
-                       unpack_status = unpack_with_sideband();
-               execute_commands(commands, unpack_status);
+               prepare_shallow_info(&si, &shallow);
+               if (!si.nr_ours && !si.nr_theirs)
+                       shallow_update = 0;
+               if (!delete_only(commands)) {
+                       unpack_status = unpack_with_sideband(&si);
+                       update_shallow_info(commands, &si, &ref);
+               }
+               execute_commands(commands, unpack_status, &si);
                if (pack_lockfile)
                        unlink_or_warn(pack_lockfile);
                if (report_status)
@@ -1035,8 +1217,11 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
                }
                if (auto_update_server_info)
                        update_server_info(0);
+               clear_shallow_info(&si);
        }
        if (use_sideband)
                packet_flush(1);
+       sha1_array_clear(&shallow);
+       sha1_array_clear(&ref);
        return 0;
 }
index 6eb24c8da27d265cad38628aeaa3e040dd298aab..852cff60b784d316cddb59234b6276dfe18fd75d 100644 (file)
@@ -610,12 +610,12 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
                const char *arg = argv[i];
                if (!strcmp(arg, "--dry-run") || !strcmp(arg, "-n"))
                        cb.dry_run = 1;
-               else if (!prefixcmp(arg, "--expire=")) {
+               else if (starts_with(arg, "--expire=")) {
                        if (parse_expiry_date(arg + 9, &cb.expire_total))
                                die(_("'%s' is not a valid timestamp"), arg);
                        explicit_expiry |= EXPIRE_TOTAL;
                }
-               else if (!prefixcmp(arg, "--expire-unreachable=")) {
+               else if (starts_with(arg, "--expire-unreachable=")) {
                        if (parse_expiry_date(arg + 21, &cb.expire_unreachable))
                                die(_("'%s' is not a valid timestamp"), arg);
                        explicit_expiry |= EXPIRE_UNREACH;
index 4e14891095e6ea016b9f44262ae300c42a75e1b2..b3ab4cf8f6517c715845db7e17c67cad5f50d730 100644 (file)
@@ -6,6 +6,7 @@
 #include "strbuf.h"
 #include "run-command.h"
 #include "refs.h"
+#include "argv-array.h"
 
 static const char * const builtin_remote_usage[] = {
        N_("git remote [-v | --verbose]"),
@@ -77,17 +78,6 @@ static const char * const builtin_remote_seturl_usage[] = {
 
 static int verbose;
 
-static int show_all(void);
-static int prune_remote(const char *remote, int dry_run);
-
-static inline int postfixcmp(const char *string, const char *postfix)
-{
-       int len1 = strlen(string), len2 = strlen(postfix);
-       if (len1 < len2)
-               return 1;
-       return strcmp(string + len1 - len2, postfix);
-}
-
 static int fetch_remote(const char *name)
 {
        const char *argv[] = { "fetch", name, NULL, NULL };
@@ -269,7 +259,7 @@ static const char *abbrev_ref(const char *name, const char *prefix)
 
 static int config_read_branches(const char *key, const char *value, void *cb)
 {
-       if (!prefixcmp(key, "branch.")) {
+       if (starts_with(key, "branch.")) {
                const char *orig_key = key;
                char *name;
                struct string_list_item *item;
@@ -277,13 +267,13 @@ static int config_read_branches(const char *key, const char *value, void *cb)
                enum { REMOTE, MERGE, REBASE } type;
 
                key += 7;
-               if (!postfixcmp(key, ".remote")) {
+               if (ends_with(key, ".remote")) {
                        name = xstrndup(key, strlen(key) - 7);
                        type = REMOTE;
-               } else if (!postfixcmp(key, ".merge")) {
+               } else if (ends_with(key, ".merge")) {
                        name = xstrndup(key, strlen(key) - 6);
                        type = MERGE;
-               } else if (!postfixcmp(key, ".rebase")) {
+               } else if (ends_with(key, ".rebase")) {
                        name = xstrndup(key, strlen(key) - 7);
                        type = REBASE;
                } else
@@ -309,8 +299,13 @@ static int config_read_branches(const char *key, const char *value, void *cb)
                                space = strchr(value, ' ');
                        }
                        string_list_append(&info->merge, xstrdup(value));
-               } else
-                       info->rebase = git_config_bool(orig_key, value);
+               } else {
+                       int v = git_config_maybe_bool(orig_key, value);
+                       if (v >= 0)
+                               info->rebase = v;
+                       else if (!strcmp(value, "preserve"))
+                               info->rebase = 1;
+               }
        }
        return 0;
 }
@@ -534,9 +529,9 @@ static int add_branch_for_removal(const char *refname,
        }
 
        /* don't delete non-remote-tracking refs */
-       if (prefixcmp(refname, "refs/remotes/")) {
+       if (!starts_with(refname, "refs/remotes/")) {
                /* advise user how to delete local branches */
-               if (!prefixcmp(refname, "refs/heads/"))
+               if (starts_with(refname, "refs/heads/"))
                        string_list_append(branches->skipped,
                                           abbrev_branch(refname));
                /* silently skip over other non-remote refs */
@@ -571,7 +566,7 @@ static int read_remote_branches(const char *refname,
        const char *symref;
 
        strbuf_addf(&buf, "refs/remotes/%s/", rename->old);
-       if (!prefixcmp(refname, buf.buf)) {
+       if (starts_with(refname, buf.buf)) {
                item = string_list_append(rename->remote_branches, xstrdup(refname));
                symref = resolve_ref_unsafe(refname, orig_sha1, 1, &flag);
                if (flag & REF_ISSYMREF)
@@ -1084,6 +1079,64 @@ static int show_push_info_item(struct string_list_item *item, void *cb_data)
        return 0;
 }
 
+static int get_one_entry(struct remote *remote, void *priv)
+{
+       struct string_list *list = priv;
+       struct strbuf url_buf = STRBUF_INIT;
+       const char **url;
+       int i, url_nr;
+
+       if (remote->url_nr > 0) {
+               strbuf_addf(&url_buf, "%s (fetch)", remote->url[0]);
+               string_list_append(list, remote->name)->util =
+                               strbuf_detach(&url_buf, NULL);
+       } else
+               string_list_append(list, remote->name)->util = NULL;
+       if (remote->pushurl_nr) {
+               url = remote->pushurl;
+               url_nr = remote->pushurl_nr;
+       } else {
+               url = remote->url;
+               url_nr = remote->url_nr;
+       }
+       for (i = 0; i < url_nr; i++)
+       {
+               strbuf_addf(&url_buf, "%s (push)", url[i]);
+               string_list_append(list, remote->name)->util =
+                               strbuf_detach(&url_buf, NULL);
+       }
+
+       return 0;
+}
+
+static int show_all(void)
+{
+       struct string_list list = STRING_LIST_INIT_NODUP;
+       int result;
+
+       list.strdup_strings = 1;
+       result = for_each_remote(get_one_entry, &list);
+
+       if (!result) {
+               int i;
+
+               sort_string_list(&list);
+               for (i = 0; i < list.nr; i++) {
+                       struct string_list_item *item = list.items + i;
+                       if (verbose)
+                               printf("%s\t%s\n", item->string,
+                                       item->util ? (const char *)item->util : "");
+                       else {
+                               if (i && !strcmp((item - 1)->string, item->string))
+                                       continue;
+                               printf("%s\n", item->string);
+                       }
+               }
+       }
+       string_list_clear(&list, 1);
+       return result;
+}
+
 static int show(int argc, const char **argv)
 {
        int no_query = 0, result = 0, query_flag = 0;
@@ -1246,26 +1299,6 @@ static int set_head(int argc, const char **argv)
        return result;
 }
 
-static int prune(int argc, const char **argv)
-{
-       int dry_run = 0, result = 0;
-       struct option options[] = {
-               OPT__DRY_RUN(&dry_run, N_("dry run")),
-               OPT_END()
-       };
-
-       argc = parse_options(argc, argv, NULL, options, builtin_remote_prune_usage,
-                            0);
-
-       if (argc < 1)
-               usage_with_options(builtin_remote_prune_usage, options);
-
-       for (; argc; argc--, argv++)
-               result |= prune_remote(*argv, dry_run);
-
-       return result;
-}
-
 static int prune_remote(const char *remote, int dry_run)
 {
        int result = 0, i;
@@ -1304,6 +1337,26 @@ static int prune_remote(const char *remote, int dry_run)
        return result;
 }
 
+static int prune(int argc, const char **argv)
+{
+       int dry_run = 0, result = 0;
+       struct option options[] = {
+               OPT__DRY_RUN(&dry_run, N_("dry run")),
+               OPT_END()
+       };
+
+       argc = parse_options(argc, argv, NULL, options, builtin_remote_prune_usage,
+                            0);
+
+       if (argc < 1)
+               usage_with_options(builtin_remote_prune_usage, options);
+
+       for (; argc; argc--, argv++)
+               result |= prune_remote(*argv, dry_run);
+
+       return result;
+}
+
 static int get_remote_default(const char *key, const char *value, void *priv)
 {
        if (strcmp(key, "remotes.default") == 0) {
@@ -1315,42 +1368,42 @@ static int get_remote_default(const char *key, const char *value, void *priv)
 
 static int update(int argc, const char **argv)
 {
-       int i, prune = 0;
+       int i, prune = -1;
        struct option options[] = {
                OPT_BOOL('p', "prune", &prune,
                         N_("prune remotes after fetching")),
                OPT_END()
        };
-       const char **fetch_argv;
-       int fetch_argc = 0;
+       struct argv_array fetch_argv = ARGV_ARRAY_INIT;
        int default_defined = 0;
-
-       fetch_argv = xmalloc(sizeof(char *) * (argc+5));
+       int retval;
 
        argc = parse_options(argc, argv, NULL, options, builtin_remote_update_usage,
                             PARSE_OPT_KEEP_ARGV0);
 
-       fetch_argv[fetch_argc++] = "fetch";
+       argv_array_push(&fetch_argv, "fetch");
 
-       if (prune)
-               fetch_argv[fetch_argc++] = "--prune";
+       if (prune != -1)
+               argv_array_push(&fetch_argv, prune ? "--prune" : "--no-prune");
        if (verbose)
-               fetch_argv[fetch_argc++] = "-v";
-       fetch_argv[fetch_argc++] = "--multiple";
+               argv_array_push(&fetch_argv, "-v");
+       argv_array_push(&fetch_argv, "--multiple");
        if (argc < 2)
-               fetch_argv[fetch_argc++] = "default";
+               argv_array_push(&fetch_argv, "default");
        for (i = 1; i < argc; i++)
-               fetch_argv[fetch_argc++] = argv[i];
+               argv_array_push(&fetch_argv, argv[i]);
 
-       if (strcmp(fetch_argv[fetch_argc-1], "default") == 0) {
+       if (strcmp(fetch_argv.argv[fetch_argv.argc-1], "default") == 0) {
                git_config(get_remote_default, &default_defined);
-               if (!default_defined)
-                       fetch_argv[fetch_argc-1] = "--all";
+               if (!default_defined) {
+                       argv_array_pop(&fetch_argv);
+                       argv_array_push(&fetch_argv, "--all");
+               }
        }
 
-       fetch_argv[fetch_argc] = NULL;
-
-       return run_command_v_opt(fetch_argv, RUN_GIT_CMD);
+       retval = run_command_v_opt(fetch_argv.argv, RUN_GIT_CMD);
+       argv_array_clear(&fetch_argv);
+       return retval;
 }
 
 static int remove_all_fetch_refspecs(const char *remote, const char *key)
@@ -1505,64 +1558,6 @@ static int set_url(int argc, const char **argv)
        return 0;
 }
 
-static int get_one_entry(struct remote *remote, void *priv)
-{
-       struct string_list *list = priv;
-       struct strbuf url_buf = STRBUF_INIT;
-       const char **url;
-       int i, url_nr;
-
-       if (remote->url_nr > 0) {
-               strbuf_addf(&url_buf, "%s (fetch)", remote->url[0]);
-               string_list_append(list, remote->name)->util =
-                               strbuf_detach(&url_buf, NULL);
-       } else
-               string_list_append(list, remote->name)->util = NULL;
-       if (remote->pushurl_nr) {
-               url = remote->pushurl;
-               url_nr = remote->pushurl_nr;
-       } else {
-               url = remote->url;
-               url_nr = remote->url_nr;
-       }
-       for (i = 0; i < url_nr; i++)
-       {
-               strbuf_addf(&url_buf, "%s (push)", url[i]);
-               string_list_append(list, remote->name)->util =
-                               strbuf_detach(&url_buf, NULL);
-       }
-
-       return 0;
-}
-
-static int show_all(void)
-{
-       struct string_list list = STRING_LIST_INIT_NODUP;
-       int result;
-
-       list.strdup_strings = 1;
-       result = for_each_remote(get_one_entry, &list);
-
-       if (!result) {
-               int i;
-
-               sort_string_list(&list);
-               for (i = 0; i < list.nr; i++) {
-                       struct string_list_item *item = list.items + i;
-                       if (verbose)
-                               printf("%s\t%s\n", item->string,
-                                       item->util ? (const char *)item->util : "");
-                       else {
-                               if (i && !strcmp((item - 1)->string, item->string))
-                                       continue;
-                               printf("%s\n", item->string);
-                       }
-               }
-       }
-       string_list_clear(&list, 1);
-       return result;
-}
-
 int cmd_remote(int argc, const char **argv, const char *prefix)
 {
        struct option options[] = {
index a0ff5c704f4e21c5021091aa7335e00959993470..ba66c6e37782fcf1bad53b7986e69ec113c4521c 100644 (file)
@@ -78,7 +78,7 @@ static void get_non_kept_pack_filenames(struct string_list *fname_list)
                return;
 
        while ((e = readdir(dir)) != NULL) {
-               if (suffixcmp(e->d_name, ".pack"))
+               if (!ends_with(e->d_name, ".pack"))
                        continue;
 
                len = strlen(e->d_name) - strlen(".pack");
@@ -123,7 +123,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
        struct string_list rollback = STRING_LIST_INIT_NODUP;
        struct string_list existing_packs = STRING_LIST_INIT_DUP;
        struct strbuf line = STRBUF_INIT;
-       int nr_packs, ext, ret, failed;
+       int ext, ret, failed;
        FILE *out;
 
        /* variables to be filled by option parsing */
@@ -233,13 +233,11 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
        if (ret)
                return ret;
 
-       nr_packs = 0;
        out = xfdopen(cmd.out, "r");
        while (strbuf_getline(&line, out, '\n') != EOF) {
                if (line.len != 40)
                        die("repack: Expecting 40 character sha1 lines only from pack-objects.");
                string_list_append(&names, line.buf);
-               nr_packs++;
        }
        fclose(out);
        ret = finish_command(&cmd);
@@ -247,7 +245,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
                return ret;
        argv_array_clear(&cmd_args);
 
-       if (!nr_packs && !quiet)
+       if (!names.nr && !quiet)
                printf("Nothing new to pack.\n");
 
        /*
index b1bd3ef9946761b146a38d5977a7378175cda047..2336325ce330f5268800a7bc949c066ce028852c 100644 (file)
 static const char * const git_replace_usage[] = {
        N_("git replace [-f] <object> <replacement>"),
        N_("git replace -d <object>..."),
-       N_("git replace -l [<pattern>]"),
+       N_("git replace [--format=<format>] [-l [<pattern>]]"),
        NULL
 };
 
+enum replace_format {
+      REPLACE_FORMAT_SHORT,
+      REPLACE_FORMAT_MEDIUM,
+      REPLACE_FORMAT_LONG
+};
+
+struct show_data {
+       const char *pattern;
+       enum replace_format format;
+};
+
 static int show_reference(const char *refname, const unsigned char *sha1,
                          int flag, void *cb_data)
 {
-       const char *pattern = cb_data;
+       struct show_data *data = cb_data;
 
-       if (!fnmatch(pattern, refname, 0))
-               printf("%s\n", refname);
+       if (!fnmatch(data->pattern, refname, 0)) {
+               if (data->format == REPLACE_FORMAT_SHORT)
+                       printf("%s\n", refname);
+               else if (data->format == REPLACE_FORMAT_MEDIUM)
+                       printf("%s -> %s\n", refname, sha1_to_hex(sha1));
+               else { /* data->format == REPLACE_FORMAT_LONG */
+                       unsigned char object[20];
+                       enum object_type obj_type, repl_type;
+
+                       if (get_sha1(refname, object))
+                               return error("Failed to resolve '%s' as a valid ref.", refname);
+
+                       obj_type = sha1_object_info(object, NULL);
+                       repl_type = sha1_object_info(sha1, NULL);
+
+                       printf("%s (%s) -> %s (%s)\n", refname, typename(obj_type),
+                              sha1_to_hex(sha1), typename(repl_type));
+               }
+       }
 
        return 0;
 }
 
-static int list_replace_refs(const char *pattern)
+static int list_replace_refs(const char *pattern, const char *format)
 {
+       struct show_data data;
+
        if (pattern == NULL)
                pattern = "*";
+       data.pattern = pattern;
 
-       for_each_replace_ref(show_reference, (void *) pattern);
+       if (format == NULL || *format == '\0' || !strcmp(format, "short"))
+               data.format = REPLACE_FORMAT_SHORT;
+       else if (!strcmp(format, "medium"))
+               data.format = REPLACE_FORMAT_MEDIUM;
+       else if (!strcmp(format, "long"))
+               data.format = REPLACE_FORMAT_LONG;
+       else
+               die("invalid replace format '%s'\n"
+                   "valid formats are 'short', 'medium' and 'long'\n",
+                   format);
+
+       for_each_replace_ref(show_reference, (void *) &data);
 
        return 0;
 }
@@ -127,19 +169,27 @@ static int replace_object(const char *object_ref, const char *replace_ref,
 int cmd_replace(int argc, const char **argv, const char *prefix)
 {
        int list = 0, delete = 0, force = 0;
+       const char *format = NULL;
        struct option options[] = {
                OPT_BOOL('l', "list", &list, N_("list replace refs")),
                OPT_BOOL('d', "delete", &delete, N_("delete replace refs")),
                OPT_BOOL('f', "force", &force, N_("replace the ref if it exists")),
+               OPT_STRING(0, "format", &format, N_("format"), N_("use this format")),
                OPT_END()
        };
 
+       read_replace_refs = 0;
+
        argc = parse_options(argc, argv, prefix, options, git_replace_usage, 0);
 
        if (list && delete)
                usage_msg_opt("-l and -d cannot be used together",
                              git_replace_usage, options);
 
+       if (format && delete)
+               usage_msg_opt("--format and -d cannot be used together",
+                             git_replace_usage, options);
+
        if (force && (list || delete))
                usage_msg_opt("-f cannot be used with -d or -l",
                              git_replace_usage, options);
@@ -157,6 +207,9 @@ int cmd_replace(int argc, const char **argv, const char *prefix)
                if (argc != 2)
                        usage_msg_opt("bad number of arguments",
                                      git_replace_usage, options);
+               if (format)
+                       usage_msg_opt("--format cannot be used when not listing",
+                                     git_replace_usage, options);
                return replace_object(argv[0], argv[1], force);
        }
 
@@ -168,5 +221,5 @@ int cmd_replace(int argc, const char **argv, const char *prefix)
                usage_msg_opt("-f needs some arguments",
                              git_replace_usage, options);
 
-       return list_replace_refs(argv[0]);
+       return list_replace_refs(argv[0], format);
 }
index 1d9ecafd417cf763d0c33438b74cec28c463ed74..aaeb611a9729938251b1c679e35c446bb737ff4f 100644 (file)
@@ -286,6 +286,7 @@ static int try_difference(const char *arg)
                                exclude = n;
                        }
                }
+               *dotdot = '.';
                return 1;
        }
        *dotdot = '.';
@@ -309,8 +310,10 @@ static int try_parent_shorthands(const char *arg)
                return 0;
 
        *dotdot = 0;
-       if (get_sha1_committish(arg, sha1))
+       if (get_sha1_committish(arg, sha1)) {
+               *dotdot = '^';
                return 0;
+       }
 
        if (!parents_only)
                show_rev(NORMAL, sha1, arg);
@@ -319,6 +322,7 @@ static int try_parent_shorthands(const char *arg)
                show_rev(parents_only ? NORMAL : REVERSED,
                                parents->item->object.sha1, arg);
 
+       *dotdot = '^';
        return 1;
 }
 
@@ -488,6 +492,7 @@ N_("git rev-parse --parseopt [options] -- [<args>...]\n"
 int cmd_rev_parse(int argc, const char **argv, const char *prefix)
 {
        int i, as_is = 0, verify = 0, quiet = 0, revs_count = 0, type = 0;
+       int has_dashdash = 0;
        int output_prefix = 0;
        unsigned char sha1[20];
        const char *name = NULL;
@@ -501,6 +506,13 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
        if (argc > 1 && !strcmp("-h", argv[1]))
                usage(builtin_rev_parse_usage);
 
+       for (i = 1; i < argc; i++) {
+               if (!strcmp(argv[i], "--")) {
+                       has_dashdash = 1;
+                       break;
+               }
+       }
+
        prefix = setup_git_directory();
        git_config(git_default_config, NULL);
        for (i = 1; i < argc; i++) {
@@ -520,7 +532,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                        }
                        continue;
                }
-               if (!prefixcmp(arg, "-n")) {
+               if (starts_with(arg, "-n")) {
                        if ((filter & DO_FLAGS) && (filter & DO_REVS))
                                show(arg);
                        continue;
@@ -572,7 +584,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                continue;
                        }
                        if (!strcmp(arg, "--short") ||
-                           !prefixcmp(arg, "--short=")) {
+                           starts_with(arg, "--short=")) {
                                filter &= ~(DO_FLAGS|DO_NOREV);
                                verify = 1;
                                abbrev = DEFAULT_ABBREV;
@@ -600,7 +612,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                symbolic = SHOW_SYMBOLIC_FULL;
                                continue;
                        }
-                       if (!prefixcmp(arg, "--abbrev-ref") &&
+                       if (starts_with(arg, "--abbrev-ref") &&
                            (!arg[12] || arg[12] == '=')) {
                                abbrev_ref = 1;
                                abbrev_ref_strict = warn_ambiguous_refs;
@@ -618,7 +630,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                for_each_ref(show_reference, NULL);
                                continue;
                        }
-                       if (!prefixcmp(arg, "--disambiguate=")) {
+                       if (starts_with(arg, "--disambiguate=")) {
                                for_each_abbrev(arg + 15, show_abbrev, NULL);
                                continue;
                        }
@@ -627,7 +639,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                for_each_ref_in("refs/bisect/good", anti_reference, NULL);
                                continue;
                        }
-                       if (!prefixcmp(arg, "--branches=")) {
+                       if (starts_with(arg, "--branches=")) {
                                for_each_glob_ref_in(show_reference, arg + 11,
                                        "refs/heads/", NULL);
                                clear_ref_exclusion(&ref_excludes);
@@ -638,7 +650,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                clear_ref_exclusion(&ref_excludes);
                                continue;
                        }
-                       if (!prefixcmp(arg, "--tags=")) {
+                       if (starts_with(arg, "--tags=")) {
                                for_each_glob_ref_in(show_reference, arg + 7,
                                        "refs/tags/", NULL);
                                clear_ref_exclusion(&ref_excludes);
@@ -649,12 +661,12 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                clear_ref_exclusion(&ref_excludes);
                                continue;
                        }
-                       if (!prefixcmp(arg, "--glob=")) {
+                       if (starts_with(arg, "--glob=")) {
                                for_each_glob_ref(show_reference, arg + 7, NULL);
                                clear_ref_exclusion(&ref_excludes);
                                continue;
                        }
-                       if (!prefixcmp(arg, "--remotes=")) {
+                       if (starts_with(arg, "--remotes=")) {
                                for_each_glob_ref_in(show_reference, arg + 10,
                                        "refs/remotes/", NULL);
                                clear_ref_exclusion(&ref_excludes);
@@ -665,7 +677,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                clear_ref_exclusion(&ref_excludes);
                                continue;
                        }
-                       if (!prefixcmp(arg, "--exclude=")) {
+                       if (starts_with(arg, "--exclude=")) {
                                add_ref_exclusion(&ref_excludes, arg + 10);
                                continue;
                        }
@@ -747,19 +759,19 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                                : "false");
                                continue;
                        }
-                       if (!prefixcmp(arg, "--since=")) {
+                       if (starts_with(arg, "--since=")) {
                                show_datestring("--max-age=", arg+8);
                                continue;
                        }
-                       if (!prefixcmp(arg, "--after=")) {
+                       if (starts_with(arg, "--after=")) {
                                show_datestring("--max-age=", arg+8);
                                continue;
                        }
-                       if (!prefixcmp(arg, "--before=")) {
+                       if (starts_with(arg, "--before=")) {
                                show_datestring("--min-age=", arg+9);
                                continue;
                        }
-                       if (!prefixcmp(arg, "--until=")) {
+                       if (starts_with(arg, "--until=")) {
                                show_datestring("--min-age=", arg+8);
                                continue;
                        }
@@ -788,6 +800,8 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                }
                if (verify)
                        die_no_single_rev(quiet);
+               if (has_dashdash)
+                       die("bad revision '%s'", arg);
                as_is = 1;
                if (!show_file(arg, output_prefix))
                        continue;
index 4482f16efb66c7fc06c7446a2e992840cc83f798..f420b74665bcf1746e94b4cc5eb66ded0a2235ff 100644 (file)
@@ -10,6 +10,7 @@
 #include "quote.h"
 #include "transport.h"
 #include "version.h"
+#include "sha1-array.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"
@@ -99,7 +100,8 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
        const char *dest = NULL;
        int fd[2];
        struct child_process *conn;
-       struct extra_have_objects extra_have;
+       struct sha1_array extra_have = SHA1_ARRAY_INIT;
+       struct sha1_array shallow = SHA1_ARRAY_INIT;
        struct ref *remote_refs, *local_refs;
        int ret;
        int helper_status = 0;
@@ -115,15 +117,15 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
                const char *arg = *argv;
 
                if (*arg == '-') {
-                       if (!prefixcmp(arg, "--receive-pack=")) {
+                       if (starts_with(arg, "--receive-pack=")) {
                                receivepack = arg + 15;
                                continue;
                        }
-                       if (!prefixcmp(arg, "--exec=")) {
+                       if (starts_with(arg, "--exec=")) {
                                receivepack = arg + 7;
                                continue;
                        }
-                       if (!prefixcmp(arg, "--remote=")) {
+                       if (starts_with(arg, "--remote=")) {
                                remote_name = arg + 9;
                                continue;
                        }
@@ -181,7 +183,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
                                        exit(1);
                                continue;
                        }
-                       if (!prefixcmp(arg, "--" CAS_OPT_NAME "=")) {
+                       if (starts_with(arg, "--" CAS_OPT_NAME "=")) {
                                if (parse_push_cas_option(&cas,
                                                          strchr(arg, '=') + 1, 0) < 0)
                                        exit(1);
@@ -228,9 +230,8 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
                        args.verbose ? CONNECT_VERBOSE : 0);
        }
 
-       memset(&extra_have, 0, sizeof(extra_have));
-
-       get_remote_heads(fd[0], NULL, 0, &remote_refs, REF_NORMAL, &extra_have);
+       get_remote_heads(fd[0], NULL, 0, &remote_refs, REF_NORMAL,
+                        &extra_have, &shallow);
 
        transport_verify_remote_names(nr_refspecs, refspecs);
 
index c226f767aa9b0e5633a3d5dde7b0b969fec39c7f..4b7e53623fc9cf0c86ed0e7e87d4b3357d765d86 100644 (file)
@@ -65,7 +65,7 @@ static void insert_one_record(struct shortlog *log,
        eol = strchr(oneline, '\n');
        if (!eol)
                eol = oneline + strlen(oneline);
-       if (!prefixcmp(oneline, "[PATCH")) {
+       if (starts_with(oneline, "[PATCH")) {
                char *eob = strchr(oneline, ']');
                if (eob && (!eol || eob < eol))
                        oneline = eob + 1;
@@ -95,7 +95,7 @@ static void read_from_stdin(struct shortlog *log)
 
        while (fgets(author, sizeof(author), stdin) != NULL) {
                if (!(author[0] == 'A' || author[0] == 'a') ||
-                   prefixcmp(author + 1, "uthor: "))
+                   !starts_with(author + 1, "uthor: "))
                        continue;
                while (fgets(oneline, sizeof(oneline), stdin) &&
                       oneline[0] != '\n')
@@ -123,7 +123,7 @@ void shortlog_add_commit(struct shortlog *log, struct commit *commit)
                else
                        eol++;
 
-               if (!prefixcmp(buffer, "author "))
+               if (starts_with(buffer, "author "))
                        author = buffer + 7;
                buffer = eol;
        }
index 46902c3de40b05b19ff1b5b0127b9f1cd11f6cde..d9217ce1e1a957462284224c08786db77f5a8f15 100644 (file)
@@ -284,7 +284,7 @@ static void show_one_commit(struct commit *commit, int no_name)
                pp_commit_easy(CMIT_FMT_ONELINE, commit, &pretty);
                pretty_str = pretty.buf;
        }
-       if (!prefixcmp(pretty_str, "[PATCH] "))
+       if (starts_with(pretty_str, "[PATCH] "))
                pretty_str += 8;
 
        if (!no_name) {
@@ -395,7 +395,7 @@ static int append_head_ref(const char *refname, const unsigned char *sha1, int f
 {
        unsigned char tmp[20];
        int ofs = 11;
-       if (prefixcmp(refname, "refs/heads/"))
+       if (!starts_with(refname, "refs/heads/"))
                return 0;
        /* If both heads/foo and tags/foo exists, get_sha1 would
         * get confused.
@@ -409,7 +409,7 @@ static int append_remote_ref(const char *refname, const unsigned char *sha1, int
 {
        unsigned char tmp[20];
        int ofs = 13;
-       if (prefixcmp(refname, "refs/remotes/"))
+       if (!starts_with(refname, "refs/remotes/"))
                return 0;
        /* If both heads/foo and tags/foo exists, get_sha1 would
         * get confused.
@@ -421,7 +421,7 @@ static int append_remote_ref(const char *refname, const unsigned char *sha1, int
 
 static int append_tag_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
 {
-       if (prefixcmp(refname, "refs/tags/"))
+       if (!starts_with(refname, "refs/tags/"))
                return 0;
        return append_ref(refname + 5, sha1, 0);
 }
@@ -452,9 +452,9 @@ static int append_matching_ref(const char *refname, const unsigned char *sha1, i
                return 0;
        if (fnmatch(match_ref_pattern, tail, 0))
                return 0;
-       if (!prefixcmp(refname, "refs/heads/"))
+       if (starts_with(refname, "refs/heads/"))
                return append_head_ref(refname, sha1, flag, cb_data);
-       if (!prefixcmp(refname, "refs/tags/"))
+       if (starts_with(refname, "refs/tags/"))
                return append_tag_ref(refname, sha1, flag, cb_data);
        return append_ref(refname, sha1, 0);
 }
@@ -479,11 +479,11 @@ static int rev_is_head(char *head, int headlen, char *name,
        if ((!head[0]) ||
            (head_sha1 && sha1 && hashcmp(head_sha1, sha1)))
                return 0;
-       if (!prefixcmp(head, "refs/heads/"))
+       if (starts_with(head, "refs/heads/"))
                head += 11;
-       if (!prefixcmp(name, "refs/heads/"))
+       if (starts_with(name, "refs/heads/"))
                name += 11;
-       else if (!prefixcmp(name, "heads/"))
+       else if (starts_with(name, "heads/"))
                name += 6;
        return !strcmp(head, name);
 }
@@ -812,7 +812,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
                                has_head++;
                }
                if (!has_head) {
-                       int offset = !prefixcmp(head, "refs/heads/") ? 11 : 0;
+                       int offset = starts_with(head, "refs/heads/") ? 11 : 0;
                        append_one_rev(head + offset);
                }
        }
index 9f3f5e370bfb123df5c662bead122b29622fc1dd..5ba1f3083890c693f8ce82166fe1cb1f6440b106 100644 (file)
@@ -37,8 +37,8 @@ static int show_ref(const char *refname, const unsigned char *sha1, int flag, vo
        if (tags_only || heads_only) {
                int match;
 
-               match = heads_only && !prefixcmp(refname, "refs/heads/");
-               match |= tags_only && !prefixcmp(refname, "refs/tags/");
+               match = heads_only && starts_with(refname, "refs/heads/");
+               match |= tags_only && starts_with(refname, "refs/tags/");
                if (!match)
                        return 0;
        }
@@ -210,7 +210,7 @@ int cmd_show_ref(int argc, const char **argv, const char *prefix)
                while (*pattern) {
                        unsigned char sha1[20];
 
-                       if (!prefixcmp(*pattern, "refs/") &&
+                       if (starts_with(*pattern, "refs/") &&
                            !read_ref(*pattern, sha1)) {
                                if (!quiet)
                                        show_one(*pattern, sha1);
index 71286b4fae4792dce73ab1533204e803e2a61334..b6a711d3191a729e5f07aae20e3fc5343148c9b2 100644 (file)
@@ -65,7 +65,7 @@ int cmd_symbolic_ref(int argc, const char **argv, const char *prefix)
                break;
        case 2:
                if (!strcmp(argv[0], "HEAD") &&
-                   prefixcmp(argv[1], "refs/"))
+                   !starts_with(argv[1], "refs/"))
                        die("Refusing to point HEAD outside of refs/");
                create_symref(argv[0], argv[1], msg);
                break;
index ea55f1d1bdd524abd46e6baa7a02ef85d56d5eb2..74d3780b77548fc8a7bb2f51b47a51cbfb51969a 100644 (file)
@@ -259,7 +259,7 @@ static int git_tag_config(const char *var, const char *value, void *cb)
        int status = git_gpg_config(var, value, cb);
        if (status)
                return status;
-       if (!prefixcmp(var, "column."))
+       if (starts_with(var, "column."))
                return git_column_config(var, value, "tag", &colopts);
        return git_default_config(var, value, cb);
 }
diff --git a/builtin/tar-tree.c b/builtin/tar-tree.c
deleted file mode 100644 (file)
index ba3ffe6..0000000
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (c) 2005, 2006 Rene Scharfe
- */
-#include "cache.h"
-#include "commit.h"
-#include "tar.h"
-#include "builtin.h"
-#include "quote.h"
-
-static const char tar_tree_usage[] =
-"git tar-tree [--remote=<repo>] <tree-ish> [basedir]\n"
-"*** Note that this command is now deprecated; use \"git archive\" instead.";
-
-static const char builtin_get_tar_commit_id_usage[] =
-"git get-tar-commit-id < <tarfile>";
-
-int cmd_tar_tree(int argc, const char **argv, const char *prefix)
-{
-       /*
-        * "git tar-tree" is now a wrapper around "git archive --format=tar"
-        *
-        * $0 --remote=<repo> arg... ==>
-        *      git archive --format=tar --remote=<repo> arg...
-        * $0 tree-ish ==>
-        *      git archive --format=tar tree-ish
-        * $0 tree-ish basedir ==>
-        *      git archive --format-tar --prefix=basedir tree-ish
-        */
-       const char **nargv = xcalloc(sizeof(*nargv), argc + 3);
-       struct strbuf sb = STRBUF_INIT;
-       char *basedir_arg;
-       int nargc = 0;
-
-       nargv[nargc++] = "archive";
-       nargv[nargc++] = "--format=tar";
-
-       if (2 <= argc && !prefixcmp(argv[1], "--remote=")) {
-               nargv[nargc++] = argv[1];
-               argv++;
-               argc--;
-       }
-
-       /*
-        * Because it's just a compatibility wrapper, tar-tree supports only
-        * the old behaviour of reading attributes from the work tree.
-        */
-       nargv[nargc++] = "--worktree-attributes";
-
-       switch (argc) {
-       default:
-               usage(tar_tree_usage);
-               break;
-       case 3:
-               /* base-path */
-               basedir_arg = xmalloc(strlen(argv[2]) + 11);
-               sprintf(basedir_arg, "--prefix=%s/", argv[2]);
-               nargv[nargc++] = basedir_arg;
-               /* fallthru */
-       case 2:
-               /* tree-ish */
-               nargv[nargc++] = argv[1];
-       }
-       nargv[nargc] = NULL;
-
-       fprintf(stderr,
-               "*** \"git tar-tree\" is now deprecated.\n"
-               "*** Running \"git archive\" instead.\n***");
-       sq_quote_argv(&sb, nargv, 0);
-       strbuf_addch(&sb, '\n');
-       fputs(sb.buf, stderr);
-       strbuf_release(&sb);
-       return cmd_archive(nargc, nargv, prefix);
-}
-
-/* ustar header + extended global header content */
-#define RECORDSIZE     (512)
-#define HEADERSIZE (2 * RECORDSIZE)
-
-int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix)
-{
-       char buffer[HEADERSIZE];
-       struct ustar_header *header = (struct ustar_header *)buffer;
-       char *content = buffer + RECORDSIZE;
-       ssize_t n;
-
-       if (argc != 1)
-               usage(builtin_get_tar_commit_id_usage);
-
-       n = read_in_full(0, buffer, HEADERSIZE);
-       if (n < HEADERSIZE)
-               die("git get-tar-commit-id: read error");
-       if (header->typeflag[0] != 'g')
-               return 1;
-       if (memcmp(content, "52 comment=", 11))
-               return 1;
-
-       n = write_in_full(1, content + 11, 41);
-       if (n < 41)
-               die_errno("git get-tar-commit-id: write error");
-
-       return 0;
-}
index 2217d7b3ae57235fc25ac3f5bf8429588aa034c4..62ff673f68b48cc2147fc7f7b17dc5269da022bf 100644 (file)
@@ -523,7 +523,7 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix)
                                strict = 1;
                                continue;
                        }
-                       if (!prefixcmp(arg, "--pack_header=")) {
+                       if (starts_with(arg, "--pack_header=")) {
                                struct pack_header *hdr;
                                char *c;
 
index 702e90db2a82a0eba2ff233b9ec46690da5afc3e..1292cfea11d87e8c98ba64ffae9d6a1aeffaca9f 100644 (file)
@@ -229,15 +229,15 @@ static void update_refs_stdin(void)
                        die("empty command in input");
                else if (isspace(*cmd.buf))
                        die("whitespace before command: %s", cmd.buf);
-               else if (!prefixcmp(cmd.buf, "update "))
+               else if (starts_with(cmd.buf, "update "))
                        parse_cmd_update(cmd.buf + 7);
-               else if (!prefixcmp(cmd.buf, "create "))
+               else if (starts_with(cmd.buf, "create "))
                        parse_cmd_create(cmd.buf + 7);
-               else if (!prefixcmp(cmd.buf, "delete "))
+               else if (starts_with(cmd.buf, "delete "))
                        parse_cmd_delete(cmd.buf + 7);
-               else if (!prefixcmp(cmd.buf, "verify "))
+               else if (starts_with(cmd.buf, "verify "))
                        parse_cmd_verify(cmd.buf + 7);
-               else if (!prefixcmp(cmd.buf, "option "))
+               else if (starts_with(cmd.buf, "option "))
                        parse_cmd_option(cmd.buf + 7);
                else
                        die("unknown command: %s", cmd.buf);
index af2da35e7d05f4de2d7a77a708ee3cf6f490266e..32ab94cd06b43cd05316f9a16981adc883c0d066 100644 (file)
@@ -37,7 +37,7 @@ int cmd_upload_archive_writer(int argc, const char **argv, const char *prefix)
                if (sent_argv.argc > MAX_ARGS)
                        die("Too many options (>%d)", MAX_ARGS - 1);
 
-               if (prefixcmp(buf, arg_cmd))
+               if (!starts_with(buf, arg_cmd))
                        die("'argument' token or flush expected");
                argv_array_push(&sent_argv, buf + strlen(arg_cmd));
        }
diff --git a/cache.h b/cache.h
index ce377e1354a4d0fd719b50edb32124110956467f..dc040fb1aa99b7970e85e5b175e60f862ff6a74a 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -354,6 +354,7 @@ static inline enum object_type object_type(unsigned int mode)
 #define DB_ENVIRONMENT "GIT_OBJECT_DIRECTORY"
 #define INDEX_ENVIRONMENT "GIT_INDEX_FILE"
 #define GRAFT_ENVIRONMENT "GIT_GRAFT_FILE"
+#define GIT_SHALLOW_FILE_ENVIRONMENT "GIT_SHALLOW_FILE"
 #define TEMPLATE_DIR_ENVIRONMENT "GIT_TEMPLATE_DIR"
 #define CONFIG_ENVIRONMENT "GIT_CONFIG"
 #define CONFIG_DATA_ENVIRONMENT "GIT_CONFIG_PARAMETERS"
@@ -736,8 +737,29 @@ enum sharedrepo {
 };
 int git_config_perm(const char *var, const char *value);
 int adjust_shared_perm(const char *path);
-int safe_create_leading_directories(char *path);
-int safe_create_leading_directories_const(const char *path);
+
+/*
+ * Create the directory containing the named path, using care to be
+ * somewhat safe against races.  Return one of the scld_error values
+ * to indicate success/failure.
+ *
+ * SCLD_VANISHED indicates that one of the ancestor directories of the
+ * path existed at one point during the function call and then
+ * suddenly vanished, probably because another process pruned the
+ * directory while we were working.  To be robust against this kind of
+ * race, callers might want to try invoking the function again when it
+ * returns SCLD_VANISHED.
+ */
+enum scld_error {
+       SCLD_OK = 0,
+       SCLD_FAILED = -1,
+       SCLD_PERMS = -2,
+       SCLD_EXISTS = -3,
+       SCLD_VANISHED = -4
+};
+enum scld_error safe_create_leading_directories(char *path);
+enum scld_error safe_create_leading_directories_const(const char *path);
+
 int mkdir_in_gitdir(const char *path);
 extern void home_config_paths(char **global, char **xdg, char *file);
 extern char *expand_user_path(const char *path);
@@ -760,11 +782,11 @@ int daemon_avoid_alias(const char *path);
 int offset_1st_component(const char *path);
 
 /* object replacement */
-#define READ_SHA1_FILE_REPLACE 1
+#define LOOKUP_REPLACE_OBJECT 1
 extern void *read_sha1_file_extended(const unsigned char *sha1, enum object_type *type, unsigned long *size, unsigned flag);
 static inline void *read_sha1_file(const unsigned char *sha1, enum object_type *type, unsigned long *size)
 {
-       return read_sha1_file_extended(sha1, type, size, READ_SHA1_FILE_REPLACE);
+       return read_sha1_file_extended(sha1, type, size, LOOKUP_REPLACE_OBJECT);
 }
 extern const unsigned char *do_lookup_replace_object(const unsigned char *sha1);
 static inline const unsigned char *lookup_replace_object(const unsigned char *sha1)
@@ -773,6 +795,12 @@ static inline const unsigned char *lookup_replace_object(const unsigned char *sh
                return sha1;
        return do_lookup_replace_object(sha1);
 }
+static inline const unsigned char *lookup_replace_object_extended(const unsigned char *sha1, unsigned flag)
+{
+       if (!(flag & LOOKUP_REPLACE_OBJECT))
+               return sha1;
+       return lookup_replace_object(sha1);
+}
 
 /* Read and unpack a sha1 file into memory, write memory to a sha1 file */
 extern int sha1_object_info(const unsigned char *, unsigned long *);
@@ -887,9 +915,12 @@ extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref);
 extern int interpret_branch_name(const char *str, int len, struct strbuf *);
 extern int get_sha1_mb(const char *str, unsigned char *sha1);
 
-extern int refname_match(const char *abbrev_name, const char *full_name, const char **rules);
-extern const char *ref_rev_parse_rules[];
-#define ref_fetch_rules ref_rev_parse_rules
+/*
+ * Return true iff abbrev_name is a possible abbreviation for
+ * full_name according to the rules defined by ref_rev_parse_rules in
+ * refs.c.
+ */
+extern int refname_match(const char *abbrev_name, const char *full_name);
 
 extern int create_symref(const char *ref, const char *refs_heads_master, const char *logmsg);
 extern int validate_headref(const char *ref);
@@ -1074,6 +1105,7 @@ struct object_info {
        enum object_type *typep;
        unsigned long *sizep;
        unsigned long *disk_sizep;
+       unsigned char *delta_base_sha1;
 
        /* Response */
        enum {
@@ -1098,7 +1130,7 @@ struct object_info {
                } packed;
        } u;
 };
-extern int sha1_object_info_extended(const unsigned char *, struct object_info *);
+extern int sha1_object_info_extended(const unsigned char *, struct object_info *, unsigned flags);
 
 /* Dumb servers support */
 extern int update_server_info(int);
@@ -1236,6 +1268,8 @@ __attribute__((format (printf, 2, 3)))
 extern void trace_argv_printf(const char **argv, const char *format, ...);
 extern void trace_repo_setup(const char *prefix);
 extern int trace_want(const char *key);
+__attribute__((format (printf, 2, 3)))
+extern void trace_printf_key(const char *key, const char *fmt, ...);
 extern void trace_strbuf(const char *key, const struct strbuf *buf);
 
 void packet_trace_identity(const char *prog);
index 08b04e2c66429a05f6fc5ac1f5b2aa94160a5c37..cf36c3d71e3b8fc3382162ef939f3ed1f7d20775 100644 (file)
@@ -64,7 +64,6 @@ 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
@@ -88,7 +87,6 @@ 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
@@ -102,7 +100,6 @@ git-relink                              ancillarymanipulators
 git-remote                              ancillarymanipulators
 git-repack                              ancillarymanipulators
 git-replace                             ancillarymanipulators
-git-repo-config                         ancillarymanipulators  deprecated
 git-request-pull                        foreignscminterface
 git-rerere                              ancillaryinterrogators
 git-reset                               mainporcelain common
@@ -127,7 +124,6 @@ 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
index d4c8286470c1adef499ca30e9f57c7fe4b2be2da..cc114b53b096c6fc8574629b86659a639fdfed61 100644 (file)
  *
  *   Initializes the indegree slab that associates an array of integers
  *   to each commit. 'stride' specifies how big each array is.  The slab
- *   that id initialied by the variant without "_with_stride" associates
+ *   that is initialized by the variant without "_with_stride" associates
  *   each commit with an array of one integer.
+ *
+ * - void clear_indegree(struct indegree *);
+ *
+ *   Empties the slab.  The slab can be reused with the same stride
+ *   without calling init_indegree() again or can be reconfigured to a
+ *   different stride by calling init_indegree_with_stride().
+ *
+ *   Call this function before the slab falls out of scope to avoid
+ *   leaking memory.
  */
 
 /* allocate ~512kB at once, allowing for malloc overhead */
@@ -31,6 +40,8 @@
 #define COMMIT_SLAB_SIZE (512*1024-32)
 #endif
 
+#define MAYBE_UNUSED __attribute__((__unused__))
+
 #define define_commit_slab(slabname, elemtype)                                 \
                                                                        \
 struct slabname {                                                      \
@@ -41,8 +52,8 @@ struct slabname {                                                     \
 };                                                                     \
 static int stat_ ##slabname## realloc;                                 \
                                                                        \
-static void init_ ##slabname## _with_stride(struct slabname *s,                \
-                                           unsigned stride)            \
+static MAYBE_UNUSED void init_ ##slabname## _with_stride(struct slabname *s, \
+                                                  unsigned stride)     \
 {                                                                      \
        unsigned int elem_size;                                         \
        if (!stride)                                                    \
@@ -54,12 +65,12 @@ static void init_ ##slabname## _with_stride(struct slabname *s,             \
        s->slab = NULL;                                                 \
 }                                                                      \
                                                                        \
-static void init_ ##slabname(struct slabname *s)                       \
+static MAYBE_UNUSED void init_ ##slabname(struct slabname *s)          \
 {                                                                      \
        init_ ##slabname## _with_stride(s, 1);                          \
 }                                                                      \
                                                                        \
-static void clear_ ##slabname(struct slabname *s)                      \
+static MAYBE_UNUSED void clear_ ##slabname(struct slabname *s)         \
 {                                                                      \
        int i;                                                          \
        for (i = 0; i < s->slab_count; i++)                             \
@@ -69,8 +80,8 @@ static void clear_ ##slabname(struct slabname *s)                     \
        s->slab = NULL;                                                 \
 }                                                                      \
                                                                        \
-static elemtype *slabname## _at(struct slabname *s,                    \
-                               const struct commit *c)                 \
+static MAYBE_UNUSED elemtype *slabname## _at(struct slabname *s,       \
+                                      const struct commit *c)          \
 {                                                                      \
        int nth_slab, nth_slot;                                         \
                                                                        \
@@ -80,7 +91,7 @@ static elemtype *slabname## _at(struct slabname *s,                   \
        if (s->slab_count <= nth_slab) {                                \
                int i;                                                  \
                s->slab = xrealloc(s->slab,                             \
-                                  (nth_slab + 1) * sizeof(s->slab));   \
+                                  (nth_slab + 1) * sizeof(*s->slab));  \
                stat_ ##slabname## realloc++;                           \
                for (i = s->slab_count; i <= nth_slab; i++)             \
                        s->slab[i] = NULL;                              \
@@ -94,4 +105,16 @@ static elemtype *slabname## _at(struct slabname *s,                 \
                                                                        \
 static int stat_ ##slabname## realloc
 
+/*
+ * Note that this seemingly redundant second declaration is required
+ * to allow a terminating semicolon, which makes instantiations look
+ * like function declarations.  I.e., the expansion of
+ *
+ *    define_commit_slab(indegree, int);
+ *
+ * ends in 'static int stat_indegreerealloc;'.  This would otherwise
+ * be a syntax error according (at least) to ISO C.  It's hard to
+ * catch because GCC silently parses it by default.
+ */
+
 #endif /* COMMIT_SLAB_H */
index 11509ffc471c548a1367068b3d7c2f15d19361f4..6bf4fe00d4b0365eca75fd9988852650660b6884 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -196,19 +196,19 @@ struct commit_graft *read_graft_line(char *buf, int len)
 static int read_graft_file(const char *graft_file)
 {
        FILE *fp = fopen(graft_file, "r");
-       char buf[1024];
+       struct strbuf buf = STRBUF_INIT;
        if (!fp)
                return -1;
-       while (fgets(buf, sizeof(buf), fp)) {
+       while (!strbuf_getwholeline(&buf, fp, '\n')) {
                /* The format is just "Commit Parent1 Parent2 ...\n" */
-               int len = strlen(buf);
-               struct commit_graft *graft = read_graft_line(buf, len);
+               struct commit_graft *graft = read_graft_line(buf.buf, buf.len);
                if (!graft)
                        continue;
                if (register_commit_graft(graft, 1))
-                       error("duplicate graft data: %s", buf);
+                       error("duplicate graft data: %s", buf.buf);
        }
        fclose(fp);
+       strbuf_release(&buf);
        return 0;
 }
 
@@ -566,7 +566,7 @@ static void record_author_date(struct author_date_slab *author_date,
             buf;
             buf = line_end + 1) {
                line_end = strchrnul(buf, '\n');
-               if (prefixcmp(buf, "author ")) {
+               if (!starts_with(buf, "author ")) {
                        if (!line_end[0] || line_end[1] == '\n')
                                return; /* end of header */
                        continue;
@@ -841,26 +841,26 @@ static struct commit_list *merge_bases_many(struct commit *one, int n, struct co
 struct commit_list *get_octopus_merge_bases(struct commit_list *in)
 {
        struct commit_list *i, *j, *k, *ret = NULL;
-       struct commit_list **pptr = &ret;
 
-       for (i = in; i; i = i->next) {
-               if (!ret)
-                       pptr = &commit_list_insert(i->item, pptr)->next;
-               else {
-                       struct commit_list *new = NULL, *end = NULL;
-
-                       for (j = ret; j; j = j->next) {
-                               struct commit_list *bases;
-                               bases = get_merge_bases(i->item, j->item, 1);
-                               if (!new)
-                                       new = bases;
-                               else
-                                       end->next = bases;
-                               for (k = bases; k; k = k->next)
-                                       end = k;
-                       }
-                       ret = new;
+       if (!in)
+               return ret;
+
+       commit_list_insert(in->item, &ret);
+
+       for (i = in->next; i; i = i->next) {
+               struct commit_list *new = NULL, *end = NULL;
+
+               for (j = ret; j; j = j->next) {
+                       struct commit_list *bases;
+                       bases = get_merge_bases(i->item, j->item, 1);
+                       if (!new)
+                               new = bases;
+                       else
+                               end->next = bases;
+                       for (k = bases; k; k = k->next)
+                               end = k;
                }
+               ret = new;
        }
        return ret;
 }
@@ -1113,7 +1113,7 @@ int parse_signed_commit(const unsigned char *sha1,
                next = next ? next + 1 : tail;
                if (in_signature && line[0] == ' ')
                        sig = line + 1;
-               else if (!prefixcmp(line, gpg_sig_header) &&
+               else if (starts_with(line, gpg_sig_header) &&
                         line[gpg_sig_header_len] == ' ')
                        sig = line + gpg_sig_header_len + 1;
                if (sig) {
@@ -1193,7 +1193,7 @@ static void parse_gpg_output(struct signature_check *sigc)
        for (i = 0; i < ARRAY_SIZE(sigcheck_gpg_status); i++) {
                const char *found, *next;
 
-               if (!prefixcmp(buf, sigcheck_gpg_status[i].check + 1)) {
+               if (starts_with(buf, sigcheck_gpg_status[i].check + 1)) {
                        /* At the very beginning of the buffer */
                        found = buf + strlen(sigcheck_gpg_status[i].check + 1);
                } else {
@@ -1356,7 +1356,7 @@ void free_commit_extra_headers(struct commit_extra_header *extra)
        }
 }
 
-int commit_tree(const struct strbuf *msg, unsigned char *tree,
+int commit_tree(const struct strbuf *msg, const unsigned char *tree,
                struct commit_list *parents, unsigned char *ret,
                const char *author, const char *sign_commit)
 {
@@ -1485,7 +1485,7 @@ static const char commit_utf8_warn[] =
 "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 commit_tree_extended(const struct strbuf *msg, unsigned char *tree,
+int commit_tree_extended(const struct strbuf *msg, const unsigned char *tree,
                         struct commit_list *parents, unsigned char *ret,
                         const char *author, const char *sign_commit,
                         struct commit_extra_header *extra)
index 934af889f7d8f1aa9fba0e9ff900b1f1132a44a7..16d9c4351395ac55d6856d8d92d24ce7064df974 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -194,6 +194,8 @@ extern struct commit_list *get_octopus_merge_bases(struct commit_list *in);
 /* largest positive number a signed 32-bit integer can contain */
 #define INFINITE_DEPTH 0x7fffffff
 
+struct sha1_array;
+struct ref;
 extern int register_shallow(const unsigned char *sha1);
 extern int unregister_shallow(const unsigned char *sha1);
 extern int for_each_commit_graft(each_commit_graft_fn, void *);
@@ -201,11 +203,38 @@ extern int is_repository_shallow(void);
 extern struct commit_list *get_shallow_commits(struct object_array *heads,
                int depth, int shallow_flag, int not_shallow_flag);
 extern void check_shallow_file_for_update(void);
-extern void set_alternate_shallow_file(const char *path);
-extern int write_shallow_commits(struct strbuf *out, int use_pack_protocol);
+extern void set_alternate_shallow_file(const char *path, int override);
+extern int write_shallow_commits(struct strbuf *out, int use_pack_protocol,
+                                const struct sha1_array *extra);
 extern void setup_alternate_shallow(struct lock_file *shallow_lock,
-                                   const char **alternate_shallow_file);
-extern char *setup_temporary_shallow(void);
+                                   const char **alternate_shallow_file,
+                                   const struct sha1_array *extra);
+extern char *setup_temporary_shallow(const struct sha1_array *extra);
+extern void advertise_shallow_grafts(int);
+
+struct shallow_info {
+       struct sha1_array *shallow;
+       int *ours, nr_ours;
+       int *theirs, nr_theirs;
+       struct sha1_array *ref;
+
+       /* for receive-pack */
+       uint32_t **used_shallow;
+       int *need_reachability_test;
+       int *reachable;
+       int *shallow_ref;
+       struct commit **commits;
+       int nr_commits;
+};
+
+extern void prepare_shallow_info(struct shallow_info *, struct sha1_array *);
+extern void clear_shallow_info(struct shallow_info *);
+extern void remove_nonexistent_theirs_shallow(struct shallow_info *);
+extern void assign_shallow_commits_to_refs(struct shallow_info *info,
+                                          uint32_t **used,
+                                          int *ref_status);
+extern int delayed_reachability_test(struct shallow_info *si, int c);
+extern void prune_shallow(int show_only);
 
 int is_descendant_of(struct commit *, struct commit_list *);
 int in_merge_bases(struct commit *, struct commit *);
@@ -232,11 +261,11 @@ struct commit_extra_header {
 extern void append_merge_tag_headers(struct commit_list *parents,
                                     struct commit_extra_header ***tail);
 
-extern int commit_tree(const struct strbuf *msg, unsigned char *tree,
+extern int commit_tree(const struct strbuf *msg, const unsigned char *tree,
                       struct commit_list *parents, unsigned char *ret,
                       const char *author, const char *sign_commit);
 
-extern int commit_tree_extended(const struct strbuf *msg, unsigned char *tree,
+extern int commit_tree_extended(const struct strbuf *msg, const unsigned char *tree,
                                struct commit_list *parents, unsigned char *ret,
                                const char *author, const char *sign_commit,
                                struct commit_extra_header *);
index fecb98bcff74d2cbcb56e8f9f56d1f3b06339469..e9892f8ee48db4739321d37726cff63dec8c0f13 100644 (file)
@@ -304,23 +304,6 @@ int mingw_open (const char *filename, int oflags, ...)
        return fd;
 }
 
-#undef write
-ssize_t mingw_write(int fd, const void *buf, size_t count)
-{
-       /*
-        * While write() calls to a file on a local disk are translated
-        * into WriteFile() calls with a maximum size of 64KB on Windows
-        * XP and 256KB on Vista, no such cap is placed on writes to
-        * files over the network on Windows XP.  Unfortunately, there
-        * seems to be a limit of 32MB-28KB on X64 and 64MB-32KB on x86;
-        * bigger writes fail on Windows XP.
-        * So we cap to a nice 31MB here to avoid write failures over
-        * the net without changing the number of WriteFile() calls in
-        * the local case.
-        */
-       return write(fd, buf, min(count, 31 * 1024 * 1024));
-}
-
 static BOOL WINAPI ctrl_ignore(DWORD type)
 {
        return TRUE;
index 92cd728d3db2e3a21650d28f044ea4f59c2673e4..e033e720c900c430a0a74b0223522c396cb66629 100644 (file)
@@ -180,9 +180,6 @@ int mingw_rmdir(const char *path);
 int mingw_open (const char *filename, int oflags, ...);
 #define open mingw_open
 
-ssize_t mingw_write(int fd, const void *buf, size_t count);
-#define write mingw_write
-
 int mingw_fgetc(FILE *stream);
 #define fgetc mingw_fgetc
 
old mode 100644 (file)
new mode 100755 (executable)
old mode 100644 (file)
new mode 100755 (executable)
index e1d66a145b756c49c4e4902200c354499532a428..d969a5aefc2bca92938d3fd7f6a507e884ce2b7f 100644 (file)
--- a/config.c
+++ b/config.c
@@ -969,25 +969,25 @@ static int git_default_mailmap_config(const char *var, const char *value)
 
 int git_default_config(const char *var, const char *value, void *dummy)
 {
-       if (!prefixcmp(var, "core."))
+       if (starts_with(var, "core."))
                return git_default_core_config(var, value);
 
-       if (!prefixcmp(var, "user."))
+       if (starts_with(var, "user."))
                return git_ident_config(var, value, dummy);
 
-       if (!prefixcmp(var, "i18n."))
+       if (starts_with(var, "i18n."))
                return git_default_i18n_config(var, value);
 
-       if (!prefixcmp(var, "branch."))
+       if (starts_with(var, "branch."))
                return git_default_branch_config(var, value);
 
-       if (!prefixcmp(var, "push."))
+       if (starts_with(var, "push."))
                return git_default_push_config(var, value);
 
-       if (!prefixcmp(var, "mailmap."))
+       if (starts_with(var, "mailmap."))
                return git_default_mailmap_config(var, value);
 
-       if (!prefixcmp(var, "advice."))
+       if (starts_with(var, "advice."))
                return git_default_advice_config(var, value);
 
        if (!strcmp(var, "pager.color") || !strcmp(var, "color.pager")) {
@@ -1210,15 +1210,14 @@ int git_config(config_fn_t fn, void *data)
  * Find all the stuff for git_config_set() below.
  */
 
-#define MAX_MATCHES 512
-
 static struct {
        int baselen;
        char *key;
        int do_not_match;
        regex_t *value_regex;
        int multi_replace;
-       size_t offset[MAX_MATCHES];
+       size_t *offset;
+       unsigned int offset_alloc;
        enum { START, SECTION_SEEN, SECTION_END_SEEN, KEY_SEEN } state;
        int seen;
 } store;
@@ -1241,11 +1240,11 @@ static int store_aux(const char *key, const char *value, void *cb)
                if (matches(key, value)) {
                        if (store.seen == 1 && store.multi_replace == 0) {
                                warning("%s has multiple values", key);
-                       } else if (store.seen >= MAX_MATCHES) {
-                               error("too many matches for %s", key);
-                               return 1;
                        }
 
+                       ALLOC_GROW(store.offset, store.seen + 1,
+                                  store.offset_alloc);
+
                        store.offset[store.seen] = cf->do_ftell(cf);
                        store.seen++;
                }
@@ -1273,11 +1272,15 @@ static int store_aux(const char *key, const char *value, void *cb)
                 * Do not increment matches: this is no match, but we
                 * just made sure we are in the desired section.
                 */
+               ALLOC_GROW(store.offset, store.seen + 1,
+                          store.offset_alloc);
                store.offset[store.seen] = cf->do_ftell(cf);
                /* fallthru */
        case SECTION_END_SEEN:
        case START:
                if (matches(key, value)) {
+                       ALLOC_GROW(store.offset, store.seen + 1,
+                                  store.offset_alloc);
                        store.offset[store.seen] = cf->do_ftell(cf);
                        store.state = KEY_SEEN;
                        store.seen++;
@@ -1285,6 +1288,9 @@ static int store_aux(const char *key, const char *value, void *cb)
                        if (strrchr(key, '.') - key == store.baselen &&
                              !strncmp(key, store.key, store.baselen)) {
                                        store.state = SECTION_SEEN;
+                                       ALLOC_GROW(store.offset,
+                                                  store.seen + 1,
+                                                  store.offset_alloc);
                                        store.offset[store.seen] = cf->do_ftell(cf);
                        }
                }
@@ -1583,6 +1589,7 @@ int git_config_set_multivar_in_file(const char *config_filename,
                        }
                }
 
+               ALLOC_GROW(store.offset, 1, store.offset_alloc);
                store.offset[0] = 0;
                store.state = START;
                store.seen = 0;
@@ -1872,7 +1879,7 @@ int parse_config_key(const char *var,
        const char *dot;
 
        /* Does it start with "section." ? */
-       if (prefixcmp(var, section) || var[section_len] != '.')
+       if (!starts_with(var, section) || var[section_len] != '.')
                return -1;
 
        /*
index 82d549e48ba796469a27ef41ad0c9ad2a8d96cbc..7d31fad241761e41de655de6f42ddfb8c87ea4f2 100644 (file)
@@ -198,6 +198,13 @@ ifeq ($(uname_S),OpenBSD)
        BASIC_LDFLAGS += -L/usr/local/lib
        HAVE_PATHS_H = YesPlease
 endif
+ifeq ($(uname_S),MirBSD)
+       NO_STRCASESTR = YesPlease
+       NO_MEMMEM = YesPlease
+       USE_ST_TIMESPEC = YesPlease
+       NEEDS_LIBICONV = YesPlease
+       HAVE_PATHS_H = YesPlease
+endif
 ifeq ($(uname_S),NetBSD)
        ifeq ($(shell expr "$(uname_R)" : '[01]\.'),2)
                NEEDS_LIBICONV = YesPlease
index 06e88b0705f7fbd138e504920c20c70218121696..4150412e2c0ebf6129137baf41c9d268d50c60bd 100644 (file)
--- a/connect.c
+++ b/connect.c
@@ -8,6 +8,7 @@
 #include "connect.h"
 #include "url.h"
 #include "string-list.h"
+#include "sha1-array.h"
 
 static char *server_capabilities;
 static const char *parse_feature_value(const char *, const char *, int *);
@@ -45,13 +46,6 @@ int check_ref_type(const struct ref *ref, int flags)
        return check_ref(ref->name, strlen(ref->name), flags);
 }
 
-static void add_extra_have(struct extra_have_objects *extra, unsigned char *sha1)
-{
-       ALLOC_GROW(extra->array, extra->nr + 1, extra->alloc);
-       hashcpy(&(extra->array[extra->nr][0]), sha1);
-       extra->nr++;
-}
-
 static void die_initial_contact(int got_at_least_one_head)
 {
        if (got_at_least_one_head)
@@ -122,7 +116,8 @@ static void annotate_refs_with_symref_info(struct ref *ref)
  */
 struct ref **get_remote_heads(int in, char *src_buf, size_t src_len,
                              struct ref **list, unsigned int flags,
-                             struct extra_have_objects *extra_have)
+                             struct sha1_array *extra_have,
+                             struct sha1_array *shallow_points)
 {
        struct ref **orig_list = list;
        int got_at_least_one_head = 0;
@@ -145,9 +140,18 @@ struct ref **get_remote_heads(int in, char *src_buf, size_t src_len,
                if (!len)
                        break;
 
-               if (len > 4 && !prefixcmp(buffer, "ERR "))
+               if (len > 4 && starts_with(buffer, "ERR "))
                        die("remote error: %s", buffer + 4);
 
+               if (len == 48 && starts_with(buffer, "shallow ")) {
+                       if (get_sha1_hex(buffer + 8, old_sha1))
+                               die("protocol error: expected shallow sha-1, got '%s'", buffer + 8);
+                       if (!shallow_points)
+                               die("repository on the other end cannot be shallow");
+                       sha1_array_append(shallow_points, old_sha1);
+                       continue;
+               }
+
                if (len < 42 || get_sha1_hex(buffer, old_sha1) || buffer[40] != ' ')
                        die("protocol error: expected sha/ref, got '%s'", buffer);
                name = buffer + 41;
@@ -160,7 +164,7 @@ struct ref **get_remote_heads(int in, char *src_buf, size_t src_len,
 
                if (extra_have &&
                    name_len == 5 && !memcmp(".have", name, 5)) {
-                       add_extra_have(extra_have, old_sha1);
+                       sha1_array_append(extra_have, old_sha1);
                        continue;
                }
 
@@ -232,10 +236,34 @@ int server_supports(const char *feature)
 
 enum protocol {
        PROTO_LOCAL = 1,
+       PROTO_FILE,
        PROTO_SSH,
        PROTO_GIT
 };
 
+int url_is_local_not_ssh(const char *url)
+{
+       const char *colon = strchr(url, ':');
+       const char *slash = strchr(url, '/');
+       return !colon || (slash && slash < colon) ||
+               has_dos_drive_prefix(url);
+}
+
+static const char *prot_name(enum protocol protocol)
+{
+       switch (protocol) {
+               case PROTO_LOCAL:
+               case PROTO_FILE:
+                       return "file";
+               case PROTO_SSH:
+                       return "ssh";
+               case PROTO_GIT:
+                       return "git";
+               default:
+                       return "unkown protocol";
+       }
+}
+
 static enum protocol get_protocol(const char *name)
 {
        if (!strcmp(name, "ssh"))
@@ -247,7 +275,7 @@ static enum protocol get_protocol(const char *name)
        if (!strcmp(name, "ssh+git"))
                return PROTO_SSH;
        if (!strcmp(name, "file"))
-               return PROTO_LOCAL;
+               return PROTO_FILE;
        die("I don't handle protocol '%s'", name);
 }
 
@@ -527,55 +555,31 @@ static struct child_process *git_proxy_connect(int fd[2], char *host)
        return proxy;
 }
 
-#define MAX_CMD_LEN 1024
-
-static char *get_port(char *host)
+static const char *get_port_numeric(const char *p)
 {
        char *end;
-       char *p = strchr(host, ':');
-
        if (p) {
                long port = strtol(p + 1, &end, 10);
                if (end != p + 1 && *end == '\0' && 0 <= port && port < 65536) {
-                       *p = '\0';
-                       return p+1;
+                       return p;
                }
        }
 
        return NULL;
 }
 
-static struct child_process no_fork;
-
 /*
- * This returns a dummy child_process if the transport protocol does not
- * need fork(2), or a struct child_process object if it does.  Once done,
- * finish the connection with finish_connect() with the value returned from
- * this function (it is safe to call finish_connect() with NULL to support
- * the former case).
- *
- * If it returns, the connect is successful; it just dies on errors (this
- * will hopefully be changed in a libification effort, to return NULL when
- * the connection failed).
+ * Extract protocol and relevant parts from the specified connection URL.
+ * The caller must free() the returned strings.
  */
-struct child_process *git_connect(int fd[2], const char *url_orig,
-                                 const char *prog, int flags)
+static enum protocol parse_connect_url(const char *url_orig, char **ret_host,
+                                      char **ret_path)
 {
        char *url;
        char *host, *path;
        char *end;
-       int c;
-       struct child_process *conn = &no_fork;
+       int separator = '/';
        enum protocol protocol = PROTO_LOCAL;
-       int free_path = 0;
-       char *port = NULL;
-       const char **arg;
-       struct strbuf cmd;
-
-       /* Without this we cannot rely on waitpid() to tell
-        * what happened to our children.
-        */
-       signal(SIGCHLD, SIG_DFL);
 
        if (is_url(url_orig))
                url = url_decode(url_orig);
@@ -587,40 +591,33 @@ struct child_process *git_connect(int fd[2], const char *url_orig,
                *host = '\0';
                protocol = get_protocol(url);
                host += 3;
-               c = '/';
        } else {
                host = url;
-               c = ':';
+               if (!url_is_local_not_ssh(url)) {
+                       protocol = PROTO_SSH;
+                       separator = ':';
+               }
        }
 
        /*
-        * Don't do destructive transforms with git:// as that
-        * protocol code does '[]' unwrapping of its own.
+        * Don't do destructive transforms as protocol code does
+        * '[]' unwrapping in get_host_and_port()
         */
        if (host[0] == '[') {
                end = strchr(host + 1, ']');
                if (end) {
-                       if (protocol != PROTO_GIT) {
-                               *end = 0;
-                               host++;
-                       }
                        end++;
                } else
                        end = host;
        } else
                end = host;
 
-       path = strchr(end, c);
-       if (path && !has_dos_drive_prefix(end)) {
-               if (c == ':') {
-                       if (host != url || path < strchrnul(host, '/')) {
-                               protocol = PROTO_SSH;
-                               *path++ = '\0';
-                       } else /* '/' in the host part, assume local path */
-                               path = end;
-               }
-       } else
+       if (protocol == PROTO_LOCAL)
                path = end;
+       else if (protocol == PROTO_FILE && has_dos_drive_prefix(end))
+               path = end; /* "file://$(pwd)" may be "file://C:/projects/repo" */
+       else
+               path = strchr(end, separator);
 
        if (!path || !*path)
                die("No path specified. See 'man git-pull' for valid url syntax");
@@ -629,33 +626,67 @@ struct child_process *git_connect(int fd[2], const char *url_orig,
         * null-terminate hostname and point path to ~ for URL's like this:
         *    ssh://host.xz/~user/repo
         */
-       if (protocol != PROTO_LOCAL && host != url) {
-               char *ptr = path;
+
+       end = path; /* Need to \0 terminate host here */
+       if (separator == ':')
+               path++; /* path starts after ':' */
+       if (protocol == PROTO_GIT || protocol == PROTO_SSH) {
                if (path[1] == '~')
                        path++;
-               else {
-                       path = xstrdup(ptr);
-                       free_path = 1;
-               }
-
-               *ptr = '\0';
        }
 
-       /*
-        * Add support for ssh port: ssh://host.xy:<port>/...
+       path = xstrdup(path);
+       *end = '\0';
+
+       *ret_host = xstrdup(host);
+       *ret_path = path;
+       free(url);
+       return protocol;
+}
+
+static struct child_process no_fork;
+
+/*
+ * This returns a dummy child_process if the transport protocol does not
+ * need fork(2), or a struct child_process object if it does.  Once done,
+ * finish the connection with finish_connect() with the value returned from
+ * this function (it is safe to call finish_connect() with NULL to support
+ * the former case).
+ *
+ * If it returns, the connect is successful; it just dies on errors (this
+ * will hopefully be changed in a libification effort, to return NULL when
+ * the connection failed).
+ */
+struct child_process *git_connect(int fd[2], const char *url,
+                                 const char *prog, int flags)
+{
+       char *hostandport, *path;
+       struct child_process *conn = &no_fork;
+       enum protocol protocol;
+       const char **arg;
+       struct strbuf cmd = STRBUF_INIT;
+
+       /* Without this we cannot rely on waitpid() to tell
+        * what happened to our children.
         */
-       if (protocol == PROTO_SSH && host != url)
-               port = get_port(end);
+       signal(SIGCHLD, SIG_DFL);
 
-       if (protocol == PROTO_GIT) {
+       protocol = parse_connect_url(url, &hostandport, &path);
+       if (flags & CONNECT_DIAG_URL) {
+               printf("Diag: url=%s\n", url ? url : "NULL");
+               printf("Diag: protocol=%s\n", prot_name(protocol));
+               printf("Diag: hostandport=%s\n", hostandport ? hostandport : "NULL");
+               printf("Diag: path=%s\n", path ? path : "NULL");
+               conn = NULL;
+       } else if (protocol == PROTO_GIT) {
                /* These underlying connection commands die() if they
                 * cannot connect.
                 */
-               char *target_host = xstrdup(host);
-               if (git_use_proxy(host))
-                       conn = git_proxy_connect(fd, host);
+               char *target_host = xstrdup(hostandport);
+               if (git_use_proxy(hostandport))
+                       conn = git_proxy_connect(fd, hostandport);
                else
-                       git_tcp_connect(fd, host, flags);
+                       git_tcp_connect(fd, hostandport, flags);
                /*
                 * Separate original protocol components prog and path
                 * from extended host header with a NUL byte.
@@ -668,55 +699,51 @@ struct child_process *git_connect(int fd[2], const char *url_orig,
                             prog, path, 0,
                             target_host, 0);
                free(target_host);
-               free(url);
-               if (free_path)
-                       free(path);
-               return conn;
-       }
-
-       conn = xcalloc(1, sizeof(*conn));
-
-       strbuf_init(&cmd, MAX_CMD_LEN);
-       strbuf_addstr(&cmd, prog);
-       strbuf_addch(&cmd, ' ');
-       sq_quote_buf(&cmd, path);
-       if (cmd.len >= MAX_CMD_LEN)
-               die("command line too long");
-
-       conn->in = conn->out = -1;
-       conn->argv = arg = xcalloc(7, sizeof(*arg));
-       if (protocol == PROTO_SSH) {
-               const char *ssh = getenv("GIT_SSH");
-               int putty = ssh && strcasestr(ssh, "plink");
-               if (!ssh) ssh = "ssh";
-
-               *arg++ = ssh;
-               if (putty && !strcasestr(ssh, "tortoiseplink"))
-                       *arg++ = "-batch";
-               if (port) {
-                       /* P is for PuTTY, p is for OpenSSH */
-                       *arg++ = putty ? "-P" : "-p";
-                       *arg++ = port;
+       } else {
+               conn = xcalloc(1, sizeof(*conn));
+
+               strbuf_addstr(&cmd, prog);
+               strbuf_addch(&cmd, ' ');
+               sq_quote_buf(&cmd, path);
+
+               conn->in = conn->out = -1;
+               conn->argv = arg = xcalloc(7, sizeof(*arg));
+               if (protocol == PROTO_SSH) {
+                       const char *ssh = getenv("GIT_SSH");
+                       int putty = ssh && strcasestr(ssh, "plink");
+                       char *ssh_host = hostandport;
+                       const char *port = NULL;
+                       get_host_and_port(&ssh_host, &port);
+                       port = get_port_numeric(port);
+
+                       if (!ssh) ssh = "ssh";
+
+                       *arg++ = ssh;
+                       if (putty && !strcasestr(ssh, "tortoiseplink"))
+                               *arg++ = "-batch";
+                       if (port) {
+                               /* P is for PuTTY, p is for OpenSSH */
+                               *arg++ = putty ? "-P" : "-p";
+                               *arg++ = port;
+                       }
+                       *arg++ = ssh_host;
+               }       else {
+                       /* remove repo-local variables from the environment */
+                       conn->env = local_repo_env;
+                       conn->use_shell = 1;
                }
-               *arg++ = host;
-       }
-       else {
-               /* remove repo-local variables from the environment */
-               conn->env = local_repo_env;
-               conn->use_shell = 1;
-       }
-       *arg++ = cmd.buf;
-       *arg = NULL;
+               *arg++ = cmd.buf;
+               *arg = NULL;
 
-       if (start_command(conn))
-               die("unable to fork");
+               if (start_command(conn))
+                       die("unable to fork");
 
-       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);
+               fd[0] = conn->out; /* read from child's stdout */
+               fd[1] = conn->in;  /* write to child's stdin */
+               strbuf_release(&cmd);
+       }
+       free(hostandport);
+       free(path);
        return conn;
 }
 
index 64fb7dbbee8714c034c5e018b3b1b4f8c29f0ea7..c41a6850f1302e4d27af1066b53b20dd303f0d13 100644 (file)
--- a/connect.h
+++ b/connect.h
@@ -2,11 +2,13 @@
 #define CONNECT_H
 
 #define CONNECT_VERBOSE       (1u << 0)
+#define CONNECT_DIAG_URL      (1u << 1)
 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 git_connection_is_socket(struct child_process *conn);
 extern int server_supports(const char *feature);
 extern int parse_feature_request(const char *features, const char *feature);
 extern const char *server_feature_value(const char *feature, int *len_ret);
+extern int url_is_local_not_ssh(const char *url);
 
 #endif
index fae8d64c12e44cf6bcf4bab210abc4bc4b9c8ed9..be0253e21bec3617ad3a7f1b0afc7d2049efc4fd 100644 (file)
@@ -19,17 +19,17 @@ int check_everything_connected(sha1_iterate_fn fn, int quiet, void *cb_data)
  *
  * Returns 0 if everything is connected, non-zero otherwise.
  */
-int check_everything_connected_with_transport(sha1_iterate_fn fn,
-                                             int quiet,
-                                             void *cb_data,
-                                             struct transport *transport)
+static int check_everything_connected_real(sha1_iterate_fn fn,
+                                          int quiet,
+                                          void *cb_data,
+                                          struct transport *transport,
+                                          const char *shallow_file)
 {
        struct child_process rev_list;
-       const char *argv[] = {"rev-list", "--objects",
-                             "--stdin", "--not", "--all", NULL, NULL};
+       const char *argv[9];
        char commit[41];
        unsigned char sha1[20];
-       int err = 0;
+       int err = 0, ac = 0;
        struct packed_git *new_pack = NULL;
 
        if (fn(cb_data, sha1))
@@ -38,7 +38,7 @@ int check_everything_connected_with_transport(sha1_iterate_fn fn,
        if (transport && transport->smart_options &&
            transport->smart_options->self_contained_and_connected &&
            transport->pack_lockfile &&
-           !suffixcmp(transport->pack_lockfile, ".keep")) {
+           ends_with(transport->pack_lockfile, ".keep")) {
                struct strbuf idx_file = STRBUF_INIT;
                strbuf_addstr(&idx_file, transport->pack_lockfile);
                strbuf_setlen(&idx_file, idx_file.len - 5); /* ".keep" */
@@ -47,8 +47,18 @@ int check_everything_connected_with_transport(sha1_iterate_fn fn,
                strbuf_release(&idx_file);
        }
 
+       if (shallow_file) {
+               argv[ac++] = "--shallow-file";
+               argv[ac++] = shallow_file;
+       }
+       argv[ac++] = "rev-list";
+       argv[ac++] = "--objects";
+       argv[ac++] = "--stdin";
+       argv[ac++] = "--not";
+       argv[ac++] = "--all";
        if (quiet)
-               argv[5] = "--quiet";
+               argv[ac++] = "--quiet";
+       argv[ac] = NULL;
 
        memset(&rev_list, 0, sizeof(rev_list));
        rev_list.argv = argv;
@@ -92,3 +102,19 @@ int check_everything_connected_with_transport(sha1_iterate_fn fn,
        sigchain_pop(SIGPIPE);
        return finish_command(&rev_list) || err;
 }
+
+int check_everything_connected_with_transport(sha1_iterate_fn fn,
+                                             int quiet,
+                                             void *cb_data,
+                                             struct transport *transport)
+{
+       return check_everything_connected_real(fn, quiet, cb_data,
+                                              transport, NULL);
+}
+
+int check_shallow_connected(sha1_iterate_fn fn, int quiet, void *cb_data,
+                           const char *shallow_file)
+{
+       return check_everything_connected_real(fn, quiet, cb_data,
+                                              NULL, shallow_file);
+}
index 0b060b7429ff2035ac3db108f80308c4a8a8e785..071d408f387b2afdbb642920054a7735c982200b 100644 (file)
@@ -18,6 +18,8 @@ typedef int (*sha1_iterate_fn)(void *, unsigned char [20]);
  * Return 0 if Ok, non zero otherwise (i.e. some missing objects)
  */
 extern int check_everything_connected(sha1_iterate_fn, int quiet, void *cb_data);
+extern int check_shallow_connected(sha1_iterate_fn, int quiet, void *cb_data,
+                                  const char *shallow_file);
 extern int check_everything_connected_with_transport(sha1_iterate_fn, int quiet,
                                                     void *cb_data,
                                                     struct transport *transport);
old mode 100644 (file)
new mode 100755 (executable)
old mode 100644 (file)
new mode 100755 (executable)
old mode 100644 (file)
new mode 100755 (executable)
index dba3c15700fae1ae8a7a43fd7a6ad7a5b9cbd5dd..9525343fcd34ce5009fcfd9b2bf2c6356282e259 100644 (file)
@@ -1,5 +1,3 @@
-#!bash
-#
 # bash/zsh completion support for core Git.
 #
 # Copyright (C) 2006,2007 Shawn O. Pearce <spearce@spearce.org>
@@ -180,9 +178,9 @@ _get_comp_words_by_ref ()
 }
 fi
 
-__gitcompadd ()
+__gitcompappend ()
 {
-       local i=0
+       local i=${#COMPREPLY[@]}
        for x in $1; do
                if [[ "$x" == "$3"* ]]; then
                        COMPREPLY[i++]="$2$x$4"
@@ -190,6 +188,12 @@ __gitcompadd ()
        done
 }
 
+__gitcompadd ()
+{
+       COMPREPLY=()
+       __gitcompappend "$@"
+}
+
 # Generates completion reply, appending a space to possible completion words,
 # if necessary.
 # It accepts 1 to 4 arguments:
@@ -220,6 +224,14 @@ __gitcomp ()
        esac
 }
 
+# Variation of __gitcomp_nl () that appends to the existing list of
+# completion candidates, COMPREPLY.
+__gitcomp_nl_append ()
+{
+       local IFS=$'\n'
+       __gitcompappend "$1" "${2-}" "${3-$cur}" "${4- }"
+}
+
 # Generates completion reply from newline-separated possible completion words
 # by appending a space to all of them.
 # It accepts 1 to 4 arguments:
@@ -231,8 +243,8 @@ __gitcomp ()
 #    appended.
 __gitcomp_nl ()
 {
-       local IFS=$'\n'
-       __gitcompadd "$1" "${2-}" "${3-$cur}" "${4- }"
+       COMPREPLY=()
+       __gitcomp_nl_append "$@"
 }
 
 # Generates completion reply with compgen from newline-separated possible
@@ -673,7 +685,6 @@ __git_list_porcelain_commands ()
                index-pack)       : plumbing;;
                init-db)          : deprecated;;
                local-fetch)      : plumbing;;
-               lost-found)       : infrequent;;
                ls-files)         : plumbing;;
                ls-remote)        : plumbing;;
                ls-tree)          : plumbing;;
@@ -687,14 +698,12 @@ __git_list_porcelain_commands ()
                pack-refs)        : plumbing;;
                parse-remote)     : plumbing;;
                patch-id)         : plumbing;;
-               peek-remote)      : plumbing;;
                prune)            : plumbing;;
                prune-packed)     : plumbing;;
                quiltimport)      : import;;
                read-tree)        : plumbing;;
                receive-pack)     : plumbing;;
                remote-*)         : transport;;
-               repo-config)      : deprecated;;
                rerere)           : plumbing;;
                rev-list)         : plumbing;;
                rev-parse)        : plumbing;;
@@ -707,7 +716,6 @@ __git_list_porcelain_commands ()
                ssh-*)            : transport;;
                stripspace)       : plumbing;;
                symbolic-ref)     : plumbing;;
-               tar-tree)         : deprecated;;
                unpack-file)      : plumbing;;
                unpack-objects)   : plumbing;;
                update-index)     : plumbing;;
@@ -1491,6 +1499,12 @@ _git_mergetool ()
 
 _git_merge_base ()
 {
+       case "$cur" in
+       --*)
+               __gitcomp "--octopus --independent --is-ancestor --fork-point"
+               return
+               ;;
+       esac
        __gitcomp_nl "$(__git_refs)"
 }
 
@@ -1623,7 +1637,7 @@ _git_rebase ()
                        --preserve-merges --stat --no-stat
                        --committer-date-is-author-date --ignore-date
                        --ignore-whitespace --whitespace=
-                       --autosquash
+                       --autosquash --fork-point --no-fork-point
                        "
 
                return
@@ -1833,6 +1847,7 @@ _git_config ()
        branch.*)
                local pfx="${cur%.*}." cur_="${cur#*.}"
                __gitcomp_nl "$(__git_heads)" "$pfx" "$cur_" "."
+               __gitcomp_nl_append $'autosetupmerge\nautosetuprebase\n' "$pfx" "$cur_"
                return
                ;;
        guitool.*.*)
@@ -1875,6 +1890,7 @@ _git_config ()
        remote.*)
                local pfx="${cur%.*}." cur_="${cur#*.}"
                __gitcomp_nl "$(__git_remotes)" "$pfx" "$cur_" "."
+               __gitcomp_nl_append "pushdefault" "$pfx" "$cur_"
                return
                ;;
        url.*.*)
@@ -1997,6 +2013,7 @@ _git_config ()
                fetch.unpackLimit
                format.attach
                format.cc
+               format.coverLetter
                format.headers
                format.numbered
                format.pretty
index eaacaf0c3e5c63967163f863ec8cd37c01f3e996..6104a42a23c4f38848a34c5c212bd7b74de81ab4 100644 (file)
@@ -1,5 +1,3 @@
-#!tcsh
-#
 # tcsh completion support for core Git.
 #
 # Copyright (C) 2012 Marc Khouzam <marc.khouzam@gmail.com>
index fac5e711ebbbd074366d89848165e0f9dbc36c36..6b779685722916f8032dce9b3b46220eb6a19414 100644 (file)
@@ -30,10 +30,10 @@ if [ -z "$script" ]; then
        local -a locations
        local e
        locations=(
+               $(dirname ${funcsourcetrace[1]%:*})/git-completion.bash
                '/etc/bash_completion.d/git' # fedora, old debian
                '/usr/share/bash-completion/completions/git' # arch, ubuntu, new debian
                '/usr/share/bash-completion/git' # gentoo
-               $(dirname ${funcsourcetrace[1]%:*})/git-completion.bash
                )
        for e in $locations; do
                test -f $e && script="$e" && break
@@ -76,6 +76,14 @@ __gitcomp_nl ()
        compadd -Q -S "${4- }" -p "${2-}" -- ${=1} && _ret=0
 }
 
+__gitcomp_nl_append ()
+{
+       emulate -L zsh
+
+       local IFS=$'\n'
+       compadd -Q -S "${4- }" -p "${2-}" -- ${=1} && _ret=0
+}
+
 __gitcomp_file ()
 {
        emulate -L zsh
index 635c96bc563a21e98df181e3a179e50e0eaeadaa..2a317fca442a51dd6f47bfeeaa19bf32ed6e670f 100644 (file)
@@ -60,7 +60,7 @@
 #define gnome_keyring_memory_free gnome_keyring_free_password
 #define gnome_keyring_memory_strdup g_strdup
 
-static const chargnome_keyring_result_to_message(GnomeKeyringResult result)
+static const char *gnome_keyring_result_to_message(GnomeKeyringResult result)
 {
        switch (result) {
        case GNOME_KEYRING_RESULT_OK:
@@ -95,9 +95,9 @@ static const char* gnome_keyring_result_to_message(GnomeKeyringResult result)
 
 static void gnome_keyring_done_cb(GnomeKeyringResult result, gpointer user_data)
 {
-       gpointer *data = (gpointer*) user_data;
-       int *done = (int*) data[0];
-       GnomeKeyringResult *r = (GnomeKeyringResult*) data[1];
+       gpointer *data = (gpointer *)user_data;
+       int *done = (int *)data[0];
+       GnomeKeyringResult *r = (GnomeKeyringResult *)data[1];
 
        *r = result;
        *done = 1;
@@ -130,34 +130,30 @@ static GnomeKeyringResult gnome_keyring_item_delete_sync(const char *keyring, gu
 /*
  * This credential struct and API is simplified from git's credential.{h,c}
  */
-struct credential
-{
-       char          *protocol;
-       char          *host;
+struct credential {
+       char *protocol;
+       char *host;
        unsigned short port;
-       char          *path;
-       char          *username;
-       char          *password;
+       char *path;
+       char *username;
+       char *password;
 };
 
-#define CREDENTIAL_INIT \
-  { NULL,NULL,0,NULL,NULL,NULL }
+#define CREDENTIAL_INIT { NULL, NULL, 0, NULL, NULL, NULL }
 
-typedef int (*credential_op_cb)(struct credential*);
+typedef int (*credential_op_cb)(struct credential *);
 
-struct credential_operation
-{
-       char             *name;
+struct credential_operation {
+       char *name;
        credential_op_cb op;
 };
 
-#define CREDENTIAL_OP_END \
-  { NULL,NULL }
+#define CREDENTIAL_OP_END { NULL, NULL }
 
 /* ----------------- GNOME Keyring functions ----------------- */
 
 /* create a special keyring option string, if path is given */
-static charkeyring_object(struct credential *c)
+static char *keyring_object(struct credential *c)
 {
        if (!c->path)
                return NULL;
@@ -170,7 +166,7 @@ static char* keyring_object(struct credential *c)
 
 static int keyring_get(struct credential *c)
 {
-       charobject = NULL;
+       char *object = NULL;
        GList *entries;
        GnomeKeyringNetworkPasswordData *password_data;
        GnomeKeyringResult result;
@@ -204,7 +200,7 @@ static int keyring_get(struct credential *c)
        }
 
        /* pick the first one from the list */
-       password_data = (GnomeKeyringNetworkPasswordData *) entries->data;
+       password_data = (GnomeKeyringNetworkPasswordData *)entries->data;
 
        gnome_keyring_memory_free(c->password);
        c->password = gnome_keyring_memory_strdup(password_data->password);
@@ -221,7 +217,7 @@ static int keyring_get(struct credential *c)
 static int keyring_store(struct credential *c)
 {
        guint32 item_id;
-       char  *object = NULL;
+       char *object = NULL;
        GnomeKeyringResult result;
 
        /*
@@ -262,7 +258,7 @@ static int keyring_store(struct credential *c)
 
 static int keyring_erase(struct credential *c)
 {
-       char  *object = NULL;
+       char *object = NULL;
        GList *entries;
        GnomeKeyringNetworkPasswordData *password_data;
        GnomeKeyringResult result;
@@ -298,22 +294,20 @@ static int keyring_erase(struct credential *c)
        if (result == GNOME_KEYRING_RESULT_CANCELLED)
                return EXIT_SUCCESS;
 
-       if (result != GNOME_KEYRING_RESULT_OK)
-       {
+       if (result != GNOME_KEYRING_RESULT_OK) {
                g_critical("%s", gnome_keyring_result_to_message(result));
                return EXIT_FAILURE;
        }
 
        /* pick the first one from the list (delete all matches?) */
-       password_data = (GnomeKeyringNetworkPasswordData *) entries->data;
+       password_data = (GnomeKeyringNetworkPasswordData *)entries->data;
 
        result = gnome_keyring_item_delete_sync(
                password_data->keyring, password_data->item_id);
 
        gnome_keyring_network_password_list_free(entries);
 
-       if (result != GNOME_KEYRING_RESULT_OK)
-       {
+       if (result != GNOME_KEYRING_RESULT_OK) {
                g_critical("%s", gnome_keyring_result_to_message(result));
                return EXIT_FAILURE;
        }
@@ -325,9 +319,8 @@ static int keyring_erase(struct credential *c)
  * Table with helper operation callbacks, used by generic
  * credential helper main function.
  */
-static struct credential_operation const credential_helper_ops[] =
-{
-       { "get",   keyring_get   },
+static struct credential_operation const credential_helper_ops[] = {
+       { "get",   keyring_get },
        { "store", keyring_store },
        { "erase", keyring_erase },
        CREDENTIAL_OP_END
@@ -353,24 +346,23 @@ static void credential_clear(struct credential *c)
 
 static int credential_read(struct credential *c)
 {
-       char    *buf;
+       char *buf;
        size_t line_len;
-       char   *key;
-       char   *value;
+       char *key;
+       char *value;
 
        key = buf = gnome_keyring_memory_alloc(1024);
 
-       while (fgets(buf, 1024, stdin))
-       {
+       while (fgets(buf, 1024, stdin)) {
                line_len = strlen(buf);
 
                if (line_len && buf[line_len-1] == '\n')
-                       buf[--line_len]='\0';
+                       buf[--line_len] = '\0';
 
                if (!line_len)
                        break;
 
-               value = strchr(buf,'=');
+               value = strchr(buf, '=');
                if (!value) {
                        g_warning("invalid credential line: %s", key);
                        gnome_keyring_memory_free(buf);
@@ -384,7 +376,7 @@ static int credential_read(struct credential *c)
                } else if (!strcmp(key, "host")) {
                        g_free(c->host);
                        c->host = g_strdup(value);
-                       value = strrchr(c->host,':');
+                       value = strrchr(c->host, ':');
                        if (value) {
                                *value++ = '\0';
                                c->port = atoi(value);
@@ -398,7 +390,8 @@ static int credential_read(struct credential *c)
                } else if (!strcmp(key, "password")) {
                        gnome_keyring_memory_free(c->password);
                        c->password = gnome_keyring_memory_strdup(value);
-                       while (*value) *value++ = '\0';
+                       while (*value)
+                               *value++ = '\0';
                }
                /*
                 * Ignore other lines; we don't know what they mean, but
@@ -429,16 +422,16 @@ static void credential_write(const struct credential *c)
 static void usage(const char *name)
 {
        struct credential_operation const *try_op = credential_helper_ops;
-       const char *basename = strrchr(name,'/');
+       const char *basename = strrchr(name, '/');
 
        basename = (basename) ? basename + 1 : name;
        fprintf(stderr, "usage: %s <", basename);
        while (try_op->name) {
-               fprintf(stderr,"%s",(try_op++)->name);
+               fprintf(stderr, "%s", (try_op++)->name);
                if (try_op->name)
-                       fprintf(stderr,"%s","|");
+                       fprintf(stderr, "%s", "|");
        }
-       fprintf(stderr,"%s",">\n");
+       fprintf(stderr, "%s", ">\n");
 }
 
 int main(int argc, char *argv[])
@@ -446,7 +439,7 @@ int main(int argc, char *argv[])
        int ret = EXIT_SUCCESS;
 
        struct credential_operation const *try_op = credential_helper_ops;
-       struct credential                  cred   = CREDENTIAL_INIT;
+       struct credential cred = CREDENTIAL_INIT;
 
        if (!argv[1]) {
                usage(argv[0]);
index 1fb9feb34849f5030d0b9dc7f91ae65e22322786..2edbdc6d998806cbffb880e6d29d5f48b5905145 100755 (executable)
@@ -9,12 +9,12 @@ case "$0" in
 *whatchanged)
        count=
        test -z "$diff_tree_flags" &&
-               diff_tree_flags=$(git-repo-config --get whatchanged.difftree)
+               diff_tree_flags=$(git config --get whatchanged.difftree)
        diff_tree_default_flags='-c -M --abbrev' ;;
 *show)
        count=-n1
        test -z "$diff_tree_flags" &&
-               diff_tree_flags=$(git-repo-config --get show.difftree)
+               diff_tree_flags=$(git config --get show.difftree)
        diff_tree_default_flags='--cc --always' ;;
 esac
 test -z "$diff_tree_flags" &&
index 8ee410f8435c8794ca7f4fabd8ebdead439891c1..8747b84334f35fd378f51e6a9a433cfacc630960 100755 (executable)
@@ -22,7 +22,6 @@
 # For example, on debian the hook is stored in
 # /usr/share/git-core/contrib/hooks/post-receive-email:
 #
-#  chmod a+x post-receive-email
 #  cd /path/to/your/repository.git
 #  ln -sf /usr/share/git-core/contrib/hooks/post-receive-email hooks/post-receive
 #
old mode 100644 (file)
new mode 100755 (executable)
index 1f914c9..9d0c2d1
@@ -13,7 +13,6 @@
 # For example, if the hook is stored in
 # /usr/share/git-core/contrib/hooks/pre-auto-gc-battery:
 #
-# chmod a+x pre-auto-gc-battery
 # cd /path/to/your/repository.git
 # ln -sf /usr/share/git-core/contrib/hooks/pre-auto-gc-battery \
 #      hooks/pre-auto-gc
old mode 100644 (file)
new mode 100755 (executable)
old mode 100644 (file)
new mode 100755 (executable)
diff --git a/contrib/p4import/README b/contrib/p4import/README
deleted file mode 100644 (file)
index b9892b6..0000000
+++ /dev/null
@@ -1 +0,0 @@
-Please see contrib/fast-import/git-p4 for a better Perforce importer.
diff --git a/contrib/p4import/git-p4import.py b/contrib/p4import/git-p4import.py
deleted file mode 100644 (file)
index 593d6a0..0000000
+++ /dev/null
@@ -1,365 +0,0 @@
-#!/usr/bin/env python
-#
-# This tool is copyright (c) 2006, Sean Estabrooks.
-# It is released under the Gnu Public License, version 2.
-#
-# Import Perforce branches into Git repositories.
-# Checking out the files is done by calling the standard p4
-# client which you must have properly configured yourself
-#
-
-import marshal
-import os
-import sys
-import time
-import getopt
-
-if sys.hexversion < 0x02020000:
-   # The behavior of the marshal module changed significantly in 2.2
-   sys.stderr.write("git-p4import.py: requires Python 2.2 or later.\n")
-   sys.exit(1)
-
-from signal import signal, \
-   SIGPIPE, SIGINT, SIG_DFL, \
-   default_int_handler
-
-signal(SIGPIPE, SIG_DFL)
-s = signal(SIGINT, SIG_DFL)
-if s != default_int_handler:
-   signal(SIGINT, s)
-
-def die(msg, *args):
-    for a in args:
-        msg = "%s %s" % (msg, a)
-    print "git-p4import fatal error:", msg
-    sys.exit(1)
-
-def usage():
-    print "USAGE: git-p4import [-q|-v]  [--authors=<file>]  [-t <timezone>]  [//p4repo/path <branch>]"
-    sys.exit(1)
-
-verbosity = 1
-logfile = "/dev/null"
-ignore_warnings = False
-stitch = 0
-tagall = True
-
-def report(level, msg, *args):
-    global verbosity
-    global logfile
-    for a in args:
-        msg = "%s %s" % (msg, a)
-    fd = open(logfile, "a")
-    fd.writelines(msg)
-    fd.close()
-    if level <= verbosity:
-        print msg
-
-class p4_command:
-    def __init__(self, _repopath):
-        try:
-            global logfile
-            self.userlist = {}
-            if _repopath[-1] == '/':
-                self.repopath = _repopath[:-1]
-            else:
-                self.repopath = _repopath
-            if self.repopath[-4:] != "/...":
-                self.repopath= "%s/..." % self.repopath
-            f=os.popen('p4 -V 2>>%s'%logfile, 'rb')
-            a = f.readlines()
-            if f.close():
-                raise
-        except:
-                die("Could not find the \"p4\" command")
-
-    def p4(self, cmd, *args):
-        global logfile
-        cmd = "%s %s" % (cmd, ' '.join(args))
-        report(2, "P4:", cmd)
-        f=os.popen('p4 -G %s 2>>%s' % (cmd,logfile), 'rb')
-        list = []
-        while 1:
-           try:
-                list.append(marshal.load(f))
-           except EOFError:
-                break
-        self.ret = f.close()
-        return list
-
-    def sync(self, id, force=False, trick=False, test=False):
-        if force:
-            ret = self.p4("sync -f %s@%s"%(self.repopath, id))[0]
-        elif trick:
-            ret = self.p4("sync -k %s@%s"%(self.repopath, id))[0]
-        elif test:
-            ret = self.p4("sync -n %s@%s"%(self.repopath, id))[0]
-        else:
-            ret = self.p4("sync    %s@%s"%(self.repopath, id))[0]
-        if ret['code'] == "error":
-             data = ret['data'].upper()
-             if data.find('VIEW') > 0:
-                 die("Perforce reports %s is not in client view"% self.repopath)
-             elif data.find('UP-TO-DATE') < 0:
-                 die("Could not sync files from perforce", self.repopath)
-
-    def changes(self, since=0):
-        try:
-            list = []
-            for rec in self.p4("changes %s@%s,#head" % (self.repopath, since+1)):
-                list.append(rec['change'])
-            list.reverse()
-            return list
-        except:
-            return []
-
-    def authors(self, filename):
-        f=open(filename)
-        for l in f.readlines():
-            self.userlist[l[:l.find('=')].rstrip()] = \
-                    (l[l.find('=')+1:l.find('<')].rstrip(),l[l.find('<')+1:l.find('>')])
-        f.close()
-        for f,e in self.userlist.items():
-                report(2, f, ":", e[0], "  <", e[1], ">")
-
-    def _get_user(self, id):
-        if not self.userlist.has_key(id):
-            try:
-                user = self.p4("users", id)[0]
-                self.userlist[id] = (user['FullName'], user['Email'])
-            except:
-                self.userlist[id] = (id, "")
-        return self.userlist[id]
-
-    def _format_date(self, ticks):
-        symbol='+'
-        name = time.tzname[0]
-        offset = time.timezone
-        if ticks[8]:
-            name = time.tzname[1]
-            offset = time.altzone
-        if offset < 0:
-            offset *= -1
-            symbol = '-'
-        localo = "%s%02d%02d %s" % (symbol, offset / 3600, offset % 3600, name)
-        tickso = time.strftime("%a %b %d %H:%M:%S %Y", ticks)
-        return "%s %s" % (tickso, localo)
-
-    def where(self):
-        try:
-            return self.p4("where %s" % self.repopath)[-1]['path']
-        except:
-            return ""
-
-    def describe(self, num):
-        desc = self.p4("describe -s", num)[0]
-        self.msg = desc['desc']
-        self.author, self.email = self._get_user(desc['user'])
-        self.date = self._format_date(time.localtime(long(desc['time'])))
-        return self
-
-class git_command:
-    def __init__(self):
-        try:
-            self.version = self.git("--version")[0][12:].rstrip()
-        except:
-            die("Could not find the \"git\" command")
-        try:
-            self.gitdir = self.get_single("rev-parse --git-dir")
-            report(2, "gdir:", self.gitdir)
-        except:
-            die("Not a git repository... did you forget to \"git init\" ?")
-        try:
-            self.cdup = self.get_single("rev-parse --show-cdup")
-            if self.cdup != "":
-                os.chdir(self.cdup)
-            self.topdir = os.getcwd()
-            report(2, "topdir:", self.topdir)
-        except:
-            die("Could not find top git directory")
-
-    def git(self, cmd):
-        global logfile
-        report(2, "GIT:", cmd)
-        f=os.popen('git %s 2>>%s' % (cmd,logfile), 'rb')
-        r=f.readlines()
-        self.ret = f.close()
-        return r
-
-    def get_single(self, cmd):
-        return self.git(cmd)[0].rstrip()
-
-    def current_branch(self):
-        try:
-            testit = self.git("rev-parse --verify HEAD")[0]
-            return self.git("symbolic-ref HEAD")[0][11:].rstrip()
-        except:
-            return None
-
-    def get_config(self, variable):
-        try:
-            return self.git("config --get %s" % variable)[0].rstrip()
-        except:
-            return None
-
-    def set_config(self, variable, value):
-        try:
-            self.git("config %s %s"%(variable, value) )
-        except:
-            die("Could not set %s to " % variable, value)
-
-    def make_tag(self, name, head):
-        self.git("tag -f %s %s"%(name,head))
-
-    def top_change(self, branch):
-        try:
-            a=self.get_single("name-rev --tags refs/heads/%s" % branch)
-            loc = a.find(' tags/') + 6
-            if a[loc:loc+3] != "p4/":
-                raise
-            return int(a[loc+3:][:-2])
-        except:
-            return 0
-
-    def update_index(self):
-        self.git("ls-files -m -d -o -z | git update-index --add --remove -z --stdin")
-
-    def checkout(self, branch):
-        self.git("checkout %s" % branch)
-
-    def repoint_head(self, branch):
-        self.git("symbolic-ref HEAD refs/heads/%s" % branch)
-
-    def remove_files(self):
-        self.git("ls-files | xargs rm")
-
-    def clean_directories(self):
-        self.git("clean -d")
-
-    def fresh_branch(self, branch):
-        report(1, "Creating new branch", branch)
-        self.git("ls-files | xargs rm")
-        os.remove(".git/index")
-        self.repoint_head(branch)
-        self.git("clean -d")
-
-    def basedir(self):
-        return self.topdir
-
-    def commit(self, author, email, date, msg, id):
-        self.update_index()
-        fd=open(".msg", "w")
-        fd.writelines(msg)
-        fd.close()
-        try:
-                current = self.get_single("rev-parse --verify HEAD")
-                head = "-p HEAD"
-        except:
-                current = ""
-                head = ""
-        tree = self.get_single("write-tree")
-        for r,l in [('DATE',date),('NAME',author),('EMAIL',email)]:
-            os.environ['GIT_AUTHOR_%s'%r] = l
-            os.environ['GIT_COMMITTER_%s'%r] = l
-        commit = self.get_single("commit-tree %s %s < .msg" % (tree,head))
-        os.remove(".msg")
-        self.make_tag("p4/%s"%id, commit)
-        self.git("update-ref HEAD %s %s" % (commit, current) )
-
-try:
-    opts, args = getopt.getopt(sys.argv[1:], "qhvt:",
-            ["authors=","help","stitch=","timezone=","log=","ignore","notags"])
-except getopt.GetoptError:
-    usage()
-
-for o, a in opts:
-    if o == "-q":
-        verbosity = 0
-    if o == "-v":
-        verbosity += 1
-    if o in ("--log"):
-        logfile = a
-    if o in ("--notags"):
-        tagall = False
-    if o in ("-h", "--help"):
-        usage()
-    if o in ("--ignore"):
-        ignore_warnings = True
-
-git = git_command()
-branch=git.current_branch()
-
-for o, a in opts:
-    if o in ("-t", "--timezone"):
-        git.set_config("perforce.timezone", a)
-    if o in ("--stitch"):
-        git.set_config("perforce.%s.path" % branch, a)
-        stitch = 1
-
-if len(args) == 2:
-    branch = args[1]
-    git.checkout(branch)
-    if branch == git.current_branch():
-        die("Branch %s already exists!" % branch)
-    report(1, "Setting perforce to ", args[0])
-    git.set_config("perforce.%s.path" % branch, args[0])
-elif len(args) != 0:
-    die("You must specify the perforce //depot/path and git branch")
-
-p4path = git.get_config("perforce.%s.path" % branch)
-if p4path == None:
-    die("Do not know Perforce //depot/path for git branch", branch)
-
-p4 = p4_command(p4path)
-
-for o, a in opts:
-    if o in ("-a", "--authors"):
-        p4.authors(a)
-
-localdir = git.basedir()
-if p4.where()[:len(localdir)] != localdir:
-    report(1, "**WARNING** Appears p4 client is misconfigured")
-    report(1, "   for sync from %s to %s" % (p4.repopath, localdir))
-    if ignore_warnings != True:
-        die("Reconfigure or use \"--ignore\" on command line")
-
-if stitch == 0:
-    top = git.top_change(branch)
-else:
-    top = 0
-changes = p4.changes(top)
-count = len(changes)
-if count == 0:
-    report(1, "Already up to date...")
-    sys.exit(0)
-
-ptz = git.get_config("perforce.timezone")
-if ptz:
-    report(1, "Setting timezone to", ptz)
-    os.environ['TZ'] = ptz
-    time.tzset()
-
-if stitch == 1:
-    git.remove_files()
-    git.clean_directories()
-    p4.sync(changes[0], force=True)
-elif top == 0 and branch != git.current_branch():
-    p4.sync(changes[0], test=True)
-    report(1, "Creating new initial commit");
-    git.fresh_branch(branch)
-    p4.sync(changes[0], force=True)
-else:
-    p4.sync(changes[0], trick=True)
-
-report(1, "processing %s changes from p4 (%s) to git (%s)" % (count, p4.repopath, branch))
-for id in changes:
-    report(1, "Importing changeset", id)
-    change = p4.describe(id)
-    p4.sync(id)
-    if tagall :
-            git.commit(change.author, change.email, change.date, change.msg, id)
-    else:
-            git.commit(change.author, change.email, change.date, change.msg, "import")
-    if stitch == 1:
-        git.clean_directories()
-        stitch = 0
diff --git a/contrib/p4import/git-p4import.txt b/contrib/p4import/git-p4import.txt
deleted file mode 100644 (file)
index 9967587..0000000
+++ /dev/null
@@ -1,167 +0,0 @@
-git-p4import(1)
-===============
-
-NAME
-----
-git-p4import - Import a Perforce repository into git
-
-
-SYNOPSIS
---------
-[verse]
-`git-p4import` [-q|-v] [--notags] [--authors <file>] [-t <timezone>]
-               <//p4repo/path> <branch>
-`git-p4import` --stitch <//p4repo/path>
-`git-p4import`
-
-
-DESCRIPTION
------------
-Import a Perforce repository into an existing git repository.  When
-a <//p4repo/path> and <branch> are specified a new branch with the
-given name will be created and the initial import will begin.
-
-Once the initial import is complete you can do an incremental import
-of new commits from the Perforce repository.  You do this by checking
-out the appropriate git branch and then running `git-p4import` without
-any options.
-
-The standard p4 client is used to communicate with the Perforce
-repository; it must be configured correctly in order for `git-p4import`
-to operate (see below).
-
-
-OPTIONS
--------
--q::
-       Do not display any progress information.
-
--v::
-        Give extra progress information.
-
-\--authors::
-       Specify an authors file containing a mapping of Perforce user
-       ids to full names and email addresses (see Notes below).
-
-\--notags::
-       Do not create a tag for each imported commit.
-
-\--stitch::
-       Import the contents of the given perforce branch into the
-       currently checked out git branch.
-
-\--log::
-       Store debugging information in the specified file.
-
--t::
-       Specify that the remote repository is in the specified timezone.
-       Timezone must be in the format "US/Pacific" or "Europe/London"
-       etc.  You only need to specify this once, it will be saved in
-       the git config file for the repository.
-
-<//p4repo/path>::
-       The Perforce path that will be imported into the specified branch.
-
-<branch>::
-       The new branch that will be created to hold the Perforce imports.
-
-
-P4 Client
----------
-You must make the `p4` client command available in your $PATH and
-configure it to communicate with the target Perforce repository.
-Typically this means you must set the "$P4PORT" and "$P4CLIENT"
-environment variables.
-
-You must also configure a `p4` client "view" which maps the Perforce
-branch into the top level of your git repository, for example:
-
-------------
-Client: myhost
-
-Root:   /home/sean/import
-
-Options:   noallwrite clobber nocompress unlocked modtime rmdir
-
-View:
-        //public/jam/... //myhost/jam/...
-------------
-
-With the above `p4` client setup, you could import the "jam"
-perforce branch into a branch named "jammy", like so:
-
-------------
-$ mkdir -p /home/sean/import/jam
-$ cd /home/sean/import/jam
-$ git init
-$ git p4import //public/jam jammy
-------------
-
-
-Multiple Branches
------------------
-Note that by creating multiple "views" you can use `git-p4import`
-to import additional branches into the same git repository.
-However, the `p4` client has a limitation in that it silently
-ignores all but the last "view" that maps into the same local
-directory.  So the following will *not* work:
-
-------------
-View:
-        //public/jam/... //myhost/jam/...
-        //public/other/... //myhost/jam/...
-        //public/guest/... //myhost/jam/...
-------------
-
-If you want more than one Perforce branch to be imported into the
-same directory you must employ a workaround.  A simple option is
-to adjust your `p4` client before each import to only include a
-single view.
-
-Another option is to create multiple symlinks locally which all
-point to the same directory in your git repository and then use
-one per "view" instead of listing the actual directory.
-
-
-Tags
-----
-A git tag of the form p4/xx is created for every change imported from
-the Perforce repository where xx is the Perforce changeset number.
-Therefore after the import you can use git to access any commit by its
-Perforce number, e.g. git show p4/327.
-
-The tag associated with the HEAD commit is also how `git-p4import`
-determines if there are new changes to incrementally import from the
-Perforce repository.
-
-If you import from a repository with many thousands of changes
-you will have an equal number of p4/xxxx git tags.  Git tags can
-be expensive in terms of disk space and repository operations.
-If you don't need to perform further incremental imports, you
-may delete the tags.
-
-
-Notes
------
-You can interrupt the import (e.g. ctrl-c) at any time and restart it
-without worry.
-
-Author information is automatically determined by querying the
-Perforce "users" table using the id associated with each change.
-However, if you want to manually supply these mappings you can do
-so with the "--authors" option.  It accepts a file containing a list
-of mappings with each line containing one mapping in the format:
-
-------------
-    perforce_id = Full Name <email@address.com>
-------------
-
-
-Author
-------
-Written by Sean Estabrooks <seanlkml@sympatico.ca>
-
-
-GIT
----
-Part of the gitlink:git[7] suite
index 7e345320ada69f1cbb909130d406cf08ce9ae130..332aba784b8810120df8e1ddfafeb034a55337a3 100755 (executable)
@@ -883,6 +883,16 @@ def main(args):
     global branches, peers
     global transports
 
+    marks = None
+    is_tmp = False
+    gitdir = os.environ.get('GIT_DIR', None)
+
+    if len(args) < 3:
+        die('Not enough arguments.')
+
+    if not gitdir:
+        die('GIT_DIR not set')
+
     alias = args[1]
     url = args[2]
 
@@ -891,7 +901,6 @@ def main(args):
     blob_marks = {}
     parsed_refs = {}
     files_cache = {}
-    marks = None
     branches = {}
     peers = {}
     transports = []
@@ -899,11 +908,8 @@ def main(args):
     if alias[5:] == url:
         is_tmp = True
         alias = hashlib.sha1(alias).hexdigest()
-    else:
-        is_tmp = False
 
     prefix = 'refs/bzr/%s' % alias
-    gitdir = os.environ['GIT_DIR']
     dirname = os.path.join(gitdir, 'bzr', alias)
 
     if not is_tmp:
index 30402d55323488f7c2bc6599bbc4a471f2120b33..eb89ef67798e70fd77eff341e3dda5385d3cde48 100755 (executable)
@@ -415,6 +415,9 @@ def get_repo(url, alias):
         local_path = os.path.join(dirname, 'clone')
         if not os.path.exists(local_path):
             hg.share(myui, shared_path, local_path, update=False)
+        else:
+            # make sure the shared path is always up-to-date
+            util.writefile(os.path.join(local_path, '.hg', 'sharedpath'), hg_path)
 
         repo = hg.repository(myui, local_path)
         try:
@@ -537,7 +540,7 @@ def export_ref(repo, name, kind, head):
 
         print "commit %s" % ref
         print "mark :%d" % (note_mark)
-        print "committer remote-hg <> %s" % (ptime.strftime('%s %z'))
+        print "committer remote-hg <> %d %s" % (ptime.time(), gittz(ptime.timezone))
         desc = "Notes for %s\n" % (name)
         print "data %d" % (len(desc))
         print desc
@@ -1164,6 +1167,16 @@ def main(args):
     global dry_run
     global notes, alias
 
+    marks = None
+    is_tmp = False
+    gitdir = os.environ.get('GIT_DIR', None)
+
+    if len(args) < 3:
+        die('Not enough arguments.')
+
+    if not gitdir:
+        die('GIT_DIR not set')
+
     alias = args[1]
     url = args[2]
     peer = None
@@ -1184,16 +1197,12 @@ def main(args):
     if alias[4:] == url:
         is_tmp = True
         alias = hashlib.sha1(alias).hexdigest()
-    else:
-        is_tmp = False
 
-    gitdir = os.environ['GIT_DIR']
     dirname = os.path.join(gitdir, 'hg', alias)
     branches = {}
     bmarks = {}
     blob_marks = {}
     parsed_refs = {}
-    marks = None
     parsed_tags = {}
     filenodes = {}
     fake_bmark = None
index 347e812923a20652b964db6b820f8f08d6c0795f..5d128a5da9a1afd3bf441f58e75d4f02a64509d5 100755 (executable)
@@ -337,6 +337,17 @@ test_expect_success 'remote cloning' '
        check gitrepo HEAD zero
 '
 
+test_expect_success 'moving remote clone' '
+       test_when_finished "rm -rf gitrepo*" &&
+
+       (
+       git clone "hg::hgrepo" gitrepo &&
+       mv gitrepo gitrepo2 &&
+       cd gitrepo2 &&
+       git fetch
+       )
+'
+
 test_expect_success 'remote update bookmark' '
        test_when_finished "rm -rf gitrepo*" &&
 
@@ -444,6 +455,74 @@ test_expect_success 'remote new bookmark multiple branch head' '
 # cleanup previous stuff
 rm -rf hgrepo
 
+test_expect_success 'fetch special filenames' '
+       test_when_finished "rm -rf hgrepo gitrepo && LC_ALL=C" &&
+
+       LC_ALL=en_US.UTF-8
+       export LC_ALL
+
+       (
+       hg init hgrepo &&
+       cd hgrepo &&
+
+       echo test >> "æ rø" &&
+       hg add "æ rø" &&
+       echo test >> "ø~?" &&
+       hg add "ø~?" &&
+       hg commit -m add-utf-8 &&
+       echo test >> "æ rø" &&
+       hg commit -m test-utf-8 &&
+       hg rm "ø~?" &&
+       hg mv "æ rø" "ø~?" &&
+       hg commit -m hg-mv-utf-8
+       ) &&
+
+       (
+       git clone "hg::hgrepo" gitrepo &&
+       cd gitrepo &&
+       git -c core.quotepath=false ls-files > ../actual
+       ) &&
+       echo "ø~?" > expected &&
+       test_cmp expected actual
+'
+
+test_expect_success 'push special filenames' '
+       test_when_finished "rm -rf hgrepo gitrepo && LC_ALL=C" &&
+
+       mkdir -p tmp && cd tmp &&
+
+       LC_ALL=en_US.UTF-8
+       export LC_ALL
+
+       (
+       hg init hgrepo &&
+       cd hgrepo &&
+
+       echo one >> content &&
+       hg add content &&
+       hg commit -m one
+       ) &&
+
+       (
+       git clone "hg::hgrepo" gitrepo &&
+       cd gitrepo &&
+
+       echo test >> "æ rø" &&
+       git add "æ rø" &&
+       git commit -m utf-8 &&
+
+       git push
+       ) &&
+
+       (cd hgrepo &&
+       hg update &&
+       hg manifest > ../actual
+       ) &&
+
+       printf "content\næ rø\n" > expected &&
+       test_cmp expected actual
+'
+
 setup_big_push () {
        (
        hg init hgrepo &&
index 7d7af03274ee0759c9e915a6ea7b52ecce65cf55..dc59a91031ea6815456e5c86f5494bf56858c0d1 100755 (executable)
@@ -9,10 +9,10 @@ if [ $# -eq 0 ]; then
 fi
 OPTS_SPEC="\
 git subtree add   --prefix=<prefix> <commit>
-git subtree add   --prefix=<prefix> <repository> <commit>
+git subtree add   --prefix=<prefix> <repository> <ref>
 git subtree merge --prefix=<prefix> <commit>
-git subtree pull  --prefix=<prefix> <repository> <refspec...>
-git subtree push  --prefix=<prefix> <repository> <refspec...>
+git subtree pull  --prefix=<prefix> <repository> <ref>
+git subtree push  --prefix=<prefix> <repository> <ref>
 git subtree split --prefix=<prefix> <commit...>
 --
 h,help        show the help
@@ -489,6 +489,12 @@ ensure_clean()
        fi
 }
 
+ensure_valid_ref_format()
+{
+       git check-ref-format "refs/heads/$1" ||
+           die "'$1' does not look like a ref"
+}
+
 cmd_add()
 {
        if [ -e "$dir" ]; then
@@ -508,8 +514,7 @@ cmd_add()
            # specified directory.  Allowing a refspec might be
            # misleading because we won't do anything with any other
            # branches fetched via the refspec.
-           git rev-parse -q --verify "$2^{commit}" >/dev/null ||
-           die "'$2' does not refer to a commit"
+           ensure_valid_ref_format "$2"
 
            "cmd_add_repository" "$@"
        else
@@ -699,7 +704,11 @@ cmd_merge()
 
 cmd_pull()
 {
+       if [ $# -ne 2 ]; then
+           die "You must provide <repository> <ref>"
+       fi
        ensure_clean
+       ensure_valid_ref_format "$2"
        git fetch "$@" || exit $?
        revs=FETCH_HEAD
        set -- $revs
@@ -709,8 +718,9 @@ cmd_pull()
 cmd_push()
 {
        if [ $# -ne 2 ]; then
-           die "You must provide <repository> <refspec>"
+           die "You must provide <repository> <ref>"
        fi
+       ensure_valid_ref_format "$2"
        if [ -e "$dir" ]; then
            repository=$1
            refspec=$2
index e0957eee55d7fb4a530717d1ced39bcabb2057b2..02669b15344c5641c613961104293e22c311aeb1 100644 (file)
@@ -9,10 +9,10 @@ git-subtree - Merge subtrees together and split repository into subtrees
 SYNOPSIS
 --------
 [verse]
-'git subtree' add   -P <prefix> <refspec>
-'git subtree' add   -P <prefix> <repository> <refspec>
-'git subtree' pull  -P <prefix> <repository> <refspec...>
-'git subtree' push  -P <prefix> <repository> <refspec...>
+'git subtree' add   -P <prefix> <commit>
+'git subtree' add   -P <prefix> <repository> <ref>
+'git subtree' pull  -P <prefix> <repository> <ref>
+'git subtree' push  -P <prefix> <repository> <ref>
 'git subtree' merge -P <prefix> <commit>
 'git subtree' split -P <prefix> [OPTIONS] [<commit>]
 
@@ -68,7 +68,7 @@ COMMANDS
 --------
 add::
        Create the <prefix> subtree by importing its contents
-       from the given <refspec> or <repository> and remote <refspec>.
+       from the given <commit> or <repository> and remote <ref>.
        A new commit is created automatically, joining the imported
        project's history with your own.  With '--squash', imports
        only a single commit from the subproject, rather than its
@@ -90,13 +90,13 @@ merge::
        
 pull::
        Exactly like 'merge', but parallels 'git pull' in that
-       it fetches the given commit from the specified remote
+       it fetches the given ref from the specified remote
        repository.
        
 push::
        Does a 'split' (see below) using the <prefix> supplied
        and then does a 'git push' to push the result to the 
-       repository and refspec. This can be used to push your
+       repository and ref. This can be used to push your
        subtree to different branches of the remote repository.
 
 split::
index 11a95fc8b5b5eea6de2014b9209e16f9a3422186..ab80b723574ff88b6c9c4a7c98c8821600f3e6bc 100644 (file)
--- a/convert.c
+++ b/convert.c
@@ -1121,7 +1121,7 @@ static int is_foreign_ident(const char *str)
 {
        int i;
 
-       if (prefixcmp(str, "$Id: "))
+       if (!starts_with(str, "$Id: "))
                return 0;
        for (i = 5; str[i]; i++) {
                if (isspace(str[i]) && str[i+1] != '$')
index 53f5375b6ca3368de6647cf5edcd7fb4dec79657..b00b215031174a0f8ee3cd3d51c662d3cacc6b00 100644 (file)
@@ -11,7 +11,7 @@
 #include "progress.h"
 #include "csum-file.h"
 
-static void flush(struct sha1file *f, void *buf, unsigned int count)
+static void flush(struct sha1file *f, const void *buf, unsigned int count)
 {
        if (0 <= f->check_fd && count)  {
                unsigned char check_buffer[8192];
@@ -86,13 +86,13 @@ int sha1close(struct sha1file *f, unsigned char *result, unsigned int flags)
        return fd;
 }
 
-int sha1write(struct sha1file *f, void *buf, unsigned int count)
+void sha1write(struct sha1file *f, const void *buf, unsigned int count)
 {
        while (count) {
                unsigned offset = f->offset;
                unsigned left = sizeof(f->buffer) - offset;
                unsigned nr = count > left ? left : count;
-               void *data;
+               const void *data;
 
                if (f->do_crc)
                        f->crc32 = crc32(f->crc32, buf, nr);
@@ -116,7 +116,6 @@ int sha1write(struct sha1file *f, void *buf, unsigned int count)
                }
                f->offset = offset;
        }
-       return 0;
 }
 
 struct sha1file *sha1fd(int fd, const char *name)
index 3b540bdc21d2edf8d3d6fc2b5e52cc840aa19395..bb543d52f108cf1e976ee644e337f5ccec983790 100644 (file)
@@ -34,7 +34,7 @@ extern struct sha1file *sha1fd(int fd, const char *name);
 extern struct sha1file *sha1fd_check(const char *name);
 extern struct sha1file *sha1fd_throughput(int fd, const char *name, struct progress *tp);
 extern int sha1close(struct sha1file *, unsigned char *, unsigned int);
-extern int sha1write(struct sha1file *, void *, unsigned int);
+extern void sha1write(struct sha1file *, const void *, unsigned int);
 extern void sha1flush(struct sha1file *f);
 extern void crc32_begin(struct sha1file *);
 extern uint32_t crc32_end(struct sha1file *);
index 34916c5e105812e36d659dfeeb8341e003e746bd..503e03906c5c7f921e4d67a53835f771ea695d0c 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -235,7 +235,7 @@ static int service_enabled;
 
 static int git_daemon_config(const char *var, const char *value, void *cb)
 {
-       if (!prefixcmp(var, "daemon.") &&
+       if (starts_with(var, "daemon.") &&
            !strcmp(var + 7, service_looking_at->config_name)) {
                service_enabled = git_config_bool(var, value);
                return 0;
@@ -633,7 +633,7 @@ static int execute(void)
        for (i = 0; i < ARRAY_SIZE(daemon_service); i++) {
                struct daemon_service *s = &(daemon_service[i]);
                int namelen = strlen(s->name);
-               if (!prefixcmp(line, "git-") &&
+               if (starts_with(line, "git-") &&
                    !strncmp(s->name, line + 4, namelen) &&
                    line[namelen + 4] == ' ') {
                        /*
@@ -1165,11 +1165,11 @@ int main(int argc, char **argv)
        for (i = 1; i < argc; i++) {
                char *arg = argv[i];
 
-               if (!prefixcmp(arg, "--listen=")) {
+               if (starts_with(arg, "--listen=")) {
                        string_list_append(&listen_addr, xstrdup_tolower(arg + 9));
                        continue;
                }
-               if (!prefixcmp(arg, "--port=")) {
+               if (starts_with(arg, "--port=")) {
                        char *end;
                        unsigned long n;
                        n = strtoul(arg+7, &end, 0);
@@ -1199,19 +1199,19 @@ int main(int argc, char **argv)
                        export_all_trees = 1;
                        continue;
                }
-               if (!prefixcmp(arg, "--access-hook=")) {
+               if (starts_with(arg, "--access-hook=")) {
                        access_hook = arg + 14;
                        continue;
                }
-               if (!prefixcmp(arg, "--timeout=")) {
+               if (starts_with(arg, "--timeout=")) {
                        timeout = atoi(arg+10);
                        continue;
                }
-               if (!prefixcmp(arg, "--init-timeout=")) {
+               if (starts_with(arg, "--init-timeout=")) {
                        init_timeout = atoi(arg+15);
                        continue;
                }
-               if (!prefixcmp(arg, "--max-connections=")) {
+               if (starts_with(arg, "--max-connections=")) {
                        max_connections = atoi(arg+18);
                        if (max_connections < 0)
                                max_connections = 0;            /* unlimited */
@@ -1221,7 +1221,7 @@ int main(int argc, char **argv)
                        strict_paths = 1;
                        continue;
                }
-               if (!prefixcmp(arg, "--base-path=")) {
+               if (starts_with(arg, "--base-path=")) {
                        base_path = arg+12;
                        continue;
                }
@@ -1229,7 +1229,7 @@ int main(int argc, char **argv)
                        base_path_relaxed = 1;
                        continue;
                }
-               if (!prefixcmp(arg, "--interpolated-path=")) {
+               if (starts_with(arg, "--interpolated-path=")) {
                        interpolated_path = arg+20;
                        continue;
                }
@@ -1241,11 +1241,11 @@ int main(int argc, char **argv)
                        user_path = "";
                        continue;
                }
-               if (!prefixcmp(arg, "--user-path=")) {
+               if (starts_with(arg, "--user-path=")) {
                        user_path = arg + 12;
                        continue;
                }
-               if (!prefixcmp(arg, "--pid-file=")) {
+               if (starts_with(arg, "--pid-file=")) {
                        pid_file = arg + 11;
                        continue;
                }
@@ -1254,35 +1254,35 @@ int main(int argc, char **argv)
                        log_syslog = 1;
                        continue;
                }
-               if (!prefixcmp(arg, "--user=")) {
+               if (starts_with(arg, "--user=")) {
                        user_name = arg + 7;
                        continue;
                }
-               if (!prefixcmp(arg, "--group=")) {
+               if (starts_with(arg, "--group=")) {
                        group_name = arg + 8;
                        continue;
                }
-               if (!prefixcmp(arg, "--enable=")) {
+               if (starts_with(arg, "--enable=")) {
                        enable_service(arg + 9, 1);
                        continue;
                }
-               if (!prefixcmp(arg, "--disable=")) {
+               if (starts_with(arg, "--disable=")) {
                        enable_service(arg + 10, 0);
                        continue;
                }
-               if (!prefixcmp(arg, "--allow-override=")) {
+               if (starts_with(arg, "--allow-override=")) {
                        make_service_overridable(arg + 17, 1);
                        continue;
                }
-               if (!prefixcmp(arg, "--forbid-override=")) {
+               if (starts_with(arg, "--forbid-override=")) {
                        make_service_overridable(arg + 18, 0);
                        continue;
                }
-               if (!prefixcmp(arg, "--informative-errors")) {
+               if (!strcmp(arg, "--informative-errors")) {
                        informative_errors = 1;
                        continue;
                }
-               if (!prefixcmp(arg, "--no-informative-errors")) {
+               if (!strcmp(arg, "--no-informative-errors")) {
                        informative_errors = 0;
                        continue;
                }
index 00a8eefde9aeb96592f5ed6455dbf5d3b16747c4..33e5982a1c61a5726580f40df0a07dadc7e2aaf1 100644 (file)
@@ -183,54 +183,12 @@ static int queue_diff(struct diff_options *o,
 
 void diff_no_index(struct rev_info *revs,
                   int argc, const char **argv,
-                  int nongit, const char *prefix)
+                  const char *prefix)
 {
        int i, prefixlen;
-       int no_index = 0;
        unsigned deprecated_show_diff_q_option_used = 0;
        const char *paths[2];
 
-       /* Were we asked to do --no-index explicitly? */
-       for (i = 1; i < argc; i++) {
-               if (!strcmp(argv[i], "--")) {
-                       i++;
-                       break;
-               }
-               if (!strcmp(argv[i], "--no-index"))
-                       no_index = 1;
-               if (argv[i][0] != '-')
-                       break;
-       }
-
-       if (!no_index && !nongit) {
-               /*
-                * Inside a git repository, without --no-index.  Only
-                * when a path outside the repository is given,
-                * e.g. "git diff /var/tmp/[12]", or "git diff
-                * Makefile /var/tmp/Makefile", allow it to be used as
-                * a colourful "diff" replacement.
-                */
-               if ((argc != i + 2) ||
-                   (path_inside_repo(prefix, argv[i]) &&
-                    path_inside_repo(prefix, argv[i+1])))
-                       return;
-       }
-       if (argc != i + 2) {
-               if (!no_index) {
-                       /*
-                        * There was no --no-index and there were not two
-                        * paths. It is possible that the user intended
-                        * to do an inside-repository operation.
-                        */
-                       fprintf(stderr, "Not a git repository\n");
-                       fprintf(stderr,
-                               "To compare two paths outside a working tree:\n");
-               }
-               /* Give the usage message for non-repository usage and exit. */
-               usagef("git diff %s <path> <path>",
-                      no_index ? "--no-index" : "[--no-index]");
-       }
-
        diff_setup(&revs->diffopt);
        for (i = 1; i < argc - 2; ) {
                int j;
diff --git a/diff.c b/diff.c
index 3950e01910674c15d8af2c0d5d114402f43b1a9c..8e4a6a910519b2bb6d6a0337682ab402f2650997 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -30,6 +30,7 @@ static int diff_use_color_default = -1;
 static int diff_context_default = 3;
 static const char *diff_word_regex_cfg;
 static const char *external_diff_cmd_cfg;
+static const char *diff_order_file_cfg;
 int diff_auto_refresh_index = 1;
 static int diff_mnemonic_prefix;
 static int diff_no_prefix;
@@ -201,6 +202,8 @@ int git_diff_ui_config(const char *var, const char *value, void *cb)
                return git_config_string(&external_diff_cmd_cfg, var, value);
        if (!strcmp(var, "diff.wordregex"))
                return git_config_string(&diff_word_regex_cfg, var, value);
+       if (!strcmp(var, "diff.orderfile"))
+               return git_config_pathname(&diff_order_file_cfg, var, value);
 
        if (!strcmp(var, "diff.ignoresubmodules"))
                handle_ignore_submodules_arg(&default_diff_options, value);
@@ -235,7 +238,7 @@ int git_diff_basic_config(const char *var, const char *value, void *cb)
        if (userdiff_config(var, value) < 0)
                return -1;
 
-       if (!prefixcmp(var, "diff.color.") || !prefixcmp(var, "color.diff.")) {
+       if (starts_with(var, "diff.color.") || starts_with(var, "color.diff.")) {
                int slot = parse_diff_color_slot(var, 11);
                if (slot < 0)
                        return 0;
@@ -264,7 +267,7 @@ int git_diff_basic_config(const char *var, const char *value, void *cb)
                return 0;
        }
 
-       if (!prefixcmp(var, "submodule."))
+       if (starts_with(var, "submodule."))
                return parse_submodule_config_option(var, value);
 
        return git_default_config(var, value, cb);
@@ -1215,7 +1218,7 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
                        diff_words_append(line, len,
                                          &ecbdata->diff_words->plus);
                        return;
-               } else if (!prefixcmp(line, "\\ ")) {
+               } else if (starts_with(line, "\\ ")) {
                        /*
                         * Eat the "no newline at eof" marker as if we
                         * saw a "+" or "-" line with nothing on it,
@@ -2387,9 +2390,9 @@ static void builtin_diff(const char *name_a,
                        xdiff_set_find_func(&xecfg, pe->pattern, pe->cflags);
                if (!diffopts)
                        ;
-               else if (!prefixcmp(diffopts, "--unified="))
+               else if (starts_with(diffopts, "--unified="))
                        xecfg.ctxlen = strtoul(diffopts + 10, NULL, 10);
-               else if (!prefixcmp(diffopts, "-u"))
+               else if (starts_with(diffopts, "-u"))
                        xecfg.ctxlen = strtoul(diffopts + 2, NULL, 10);
                if (o->word_diff)
                        init_diff_words_data(&ecbdata, o, one, two);
@@ -2899,11 +2902,16 @@ static void run_external_diff(const char *pgm,
                              struct diff_filespec *one,
                              struct diff_filespec *two,
                              const char *xfrm_msg,
-                             int complete_rewrite)
+                             int complete_rewrite,
+                             struct diff_options *o)
 {
        const char *spawn_arg[10];
        int retval;
        const char **arg = &spawn_arg[0];
+       struct diff_queue_struct *q = &diff_queued_diff;
+       const char *env[3] = { NULL };
+       char env_counter[50];
+       char env_total[50];
 
        if (one && two) {
                struct diff_tempfile *temp_one, *temp_two;
@@ -2928,7 +2936,14 @@ static void run_external_diff(const char *pgm,
        }
        *arg = NULL;
        fflush(NULL);
-       retval = run_command_v_opt(spawn_arg, RUN_USING_SHELL);
+
+       env[0] = env_counter;
+       snprintf(env_counter, sizeof(env_counter), "GIT_DIFF_PATH_COUNTER=%d",
+                ++o->diff_path_counter);
+       env[1] = env_total;
+       snprintf(env_total, sizeof(env_total), "GIT_DIFF_PATH_TOTAL=%d", q->nr);
+
+       retval = run_command_v_opt_cd_env(spawn_arg, RUN_USING_SHELL, NULL, env);
        remove_tempfile();
        if (retval) {
                fprintf(stderr, "external diff died, stopping at %s.\n", name);
@@ -3042,7 +3057,7 @@ static void run_diff_cmd(const char *pgm,
 
        if (pgm) {
                run_external_diff(pgm, name, other, one, two, xfrm_msg,
-                                 complete_rewrite);
+                                 complete_rewrite, o);
                return;
        }
        if (one && two)
@@ -3207,6 +3222,8 @@ void diff_setup(struct diff_options *options)
        options->detect_rename = diff_detect_rename_default;
        options->xdl_opts |= diff_algorithm;
 
+       options->orderfile = diff_order_file_cfg;
+
        if (diff_no_prefix) {
                options->a_prefix = options->b_prefix = "";
        } else if (!diff_mnemonic_prefix) {
@@ -3317,6 +3334,8 @@ void diff_setup_done(struct diff_options *options)
                options->output_format = DIFF_FORMAT_NO_OUTPUT;
                DIFF_OPT_SET(options, EXIT_WITH_STATUS);
        }
+
+       options->diff_path_counter = 0;
 }
 
 static int opt_arg(const char *arg, int arg_short, const char *arg_long, int *val)
@@ -3391,7 +3410,7 @@ int parse_long_opt(const char *opt, const char **argv,
        if (arg[0] != '-' || arg[1] != '-')
                return 0;
        arg += strlen("--");
-       if (prefixcmp(arg, opt))
+       if (!starts_with(arg, opt))
                return 0;
        arg += strlen(opt);
        if (*arg == '=') { /* stuck form: --option=value */
@@ -3422,7 +3441,7 @@ static int stat_opt(struct diff_options *options, const char **av)
 
        switch (*arg) {
        case '-':
-               if (!prefixcmp(arg, "-width")) {
+               if (starts_with(arg, "-width")) {
                        arg += strlen("-width");
                        if (*arg == '=')
                                width = strtoul(arg + 1, &end, 10);
@@ -3432,7 +3451,7 @@ static int stat_opt(struct diff_options *options, const char **av)
                                width = strtoul(av[1], &end, 10);
                                argcount = 2;
                        }
-               } else if (!prefixcmp(arg, "-name-width")) {
+               } else if (starts_with(arg, "-name-width")) {
                        arg += strlen("-name-width");
                        if (*arg == '=')
                                name_width = strtoul(arg + 1, &end, 10);
@@ -3442,7 +3461,7 @@ static int stat_opt(struct diff_options *options, const char **av)
                                name_width = strtoul(av[1], &end, 10);
                                argcount = 2;
                        }
-               } else if (!prefixcmp(arg, "-graph-width")) {
+               } else if (starts_with(arg, "-graph-width")) {
                        arg += strlen("-graph-width");
                        if (*arg == '=')
                                graph_width = strtoul(arg + 1, &end, 10);
@@ -3452,7 +3471,7 @@ static int stat_opt(struct diff_options *options, const char **av)
                                graph_width = strtoul(av[1], &end, 10);
                                argcount = 2;
                        }
-               } else if (!prefixcmp(arg, "-count")) {
+               } else if (starts_with(arg, "-count")) {
                        arg += strlen("-count");
                        if (*arg == '=')
                                count = strtoul(arg + 1, &end, 10);
@@ -3614,15 +3633,15 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
                options->output_format |= DIFF_FORMAT_SHORTSTAT;
        else if (!strcmp(arg, "-X") || !strcmp(arg, "--dirstat"))
                return parse_dirstat_opt(options, "");
-       else if (!prefixcmp(arg, "-X"))
+       else if (starts_with(arg, "-X"))
                return parse_dirstat_opt(options, arg + 2);
-       else if (!prefixcmp(arg, "--dirstat="))
+       else if (starts_with(arg, "--dirstat="))
                return parse_dirstat_opt(options, arg + 10);
        else if (!strcmp(arg, "--cumulative"))
                return parse_dirstat_opt(options, "cumulative");
        else if (!strcmp(arg, "--dirstat-by-file"))
                return parse_dirstat_opt(options, "files");
-       else if (!prefixcmp(arg, "--dirstat-by-file=")) {
+       else if (starts_with(arg, "--dirstat-by-file=")) {
                parse_dirstat_opt(options, "files");
                return parse_dirstat_opt(options, arg + 18);
        }
@@ -3639,17 +3658,17 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
                options->output_format |= DIFF_FORMAT_NAME_STATUS;
        else if (!strcmp(arg, "-s") || !strcmp(arg, "--no-patch"))
                options->output_format |= DIFF_FORMAT_NO_OUTPUT;
-       else if (!prefixcmp(arg, "--stat"))
+       else if (starts_with(arg, "--stat"))
                /* --stat, --stat-width, --stat-name-width, or --stat-count */
                return stat_opt(options, av);
 
        /* renames options */
-       else if (!prefixcmp(arg, "-B") || !prefixcmp(arg, "--break-rewrites=") ||
+       else if (starts_with(arg, "-B") || starts_with(arg, "--break-rewrites=") ||
                 !strcmp(arg, "--break-rewrites")) {
                if ((options->break_opt = diff_scoreopt_parse(arg)) == -1)
                        return error("invalid argument to -B: %s", arg+2);
        }
-       else if (!prefixcmp(arg, "-M") || !prefixcmp(arg, "--find-renames=") ||
+       else if (starts_with(arg, "-M") || starts_with(arg, "--find-renames=") ||
                 !strcmp(arg, "--find-renames")) {
                if ((options->rename_score = diff_scoreopt_parse(arg)) == -1)
                        return error("invalid argument to -M: %s", arg+2);
@@ -3658,7 +3677,7 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
        else if (!strcmp(arg, "-D") || !strcmp(arg, "--irreversible-delete")) {
                options->irreversible_delete = 1;
        }
-       else if (!prefixcmp(arg, "-C") || !prefixcmp(arg, "--find-copies=") ||
+       else if (starts_with(arg, "-C") || starts_with(arg, "--find-copies=") ||
                 !strcmp(arg, "--find-copies")) {
                if (options->detect_rename == DIFF_DETECT_COPY)
                        DIFF_OPT_SET(options, FIND_COPIES_HARDER);
@@ -3674,7 +3693,7 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
                DIFF_OPT_CLR(options, RENAME_EMPTY);
        else if (!strcmp(arg, "--relative"))
                DIFF_OPT_SET(options, RELATIVE_NAME);
-       else if (!prefixcmp(arg, "--relative=")) {
+       else if (starts_with(arg, "--relative=")) {
                DIFF_OPT_SET(options, RELATIVE_NAME);
                options->prefix = arg + 11;
        }
@@ -3727,7 +3746,7 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
                DIFF_OPT_CLR(options, FOLLOW_RENAMES);
        else if (!strcmp(arg, "--color"))
                options->use_color = 1;
-       else if (!prefixcmp(arg, "--color=")) {
+       else if (starts_with(arg, "--color=")) {
                int value = git_config_colorbool(NULL, arg+8);
                if (value < 0)
                        return error("option `color' expects \"always\", \"auto\", or \"never\"");
@@ -3739,7 +3758,7 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
                options->use_color = 1;
                options->word_diff = DIFF_WORDS_COLOR;
        }
-       else if (!prefixcmp(arg, "--color-words=")) {
+       else if (starts_with(arg, "--color-words=")) {
                options->use_color = 1;
                options->word_diff = DIFF_WORDS_COLOR;
                options->word_regex = arg + 14;
@@ -3748,7 +3767,7 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
                if (options->word_diff == DIFF_WORDS_NONE)
                        options->word_diff = DIFF_WORDS_PLAIN;
        }
-       else if (!prefixcmp(arg, "--word-diff=")) {
+       else if (starts_with(arg, "--word-diff=")) {
                const char *type = arg + 12;
                if (!strcmp(type, "plain"))
                        options->word_diff = DIFF_WORDS_PLAIN;
@@ -3784,12 +3803,12 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
        else if (!strcmp(arg, "--ignore-submodules")) {
                DIFF_OPT_SET(options, OVERRIDE_SUBMODULE_CONFIG);
                handle_ignore_submodules_arg(options, "all");
-       } else if (!prefixcmp(arg, "--ignore-submodules=")) {
+       } else if (starts_with(arg, "--ignore-submodules=")) {
                DIFF_OPT_SET(options, OVERRIDE_SUBMODULE_CONFIG);
                handle_ignore_submodules_arg(options, arg + 20);
        } else if (!strcmp(arg, "--submodule"))
                DIFF_OPT_SET(options, SUBMODULE_LOG);
-       else if (!prefixcmp(arg, "--submodule="))
+       else if (starts_with(arg, "--submodule="))
                return parse_submodule_opt(options, arg + 12);
 
        /* misc options */
@@ -3825,7 +3844,7 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
        }
        else if (!strcmp(arg, "--abbrev"))
                options->abbrev = DEFAULT_ABBREV;
-       else if (!prefixcmp(arg, "--abbrev=")) {
+       else if (starts_with(arg, "--abbrev=")) {
                options->abbrev = strtoul(arg + 9, NULL, 10);
                if (options->abbrev < MINIMUM_ABBREV)
                        options->abbrev = MINIMUM_ABBREV;
@@ -3907,15 +3926,15 @@ static int diff_scoreopt_parse(const char *opt)
        cmd = *opt++;
        if (cmd == '-') {
                /* convert the long-form arguments into short-form versions */
-               if (!prefixcmp(opt, "break-rewrites")) {
+               if (starts_with(opt, "break-rewrites")) {
                        opt += strlen("break-rewrites");
                        if (*opt == 0 || *opt++ == '=')
                                cmd = 'B';
-               } else if (!prefixcmp(opt, "find-copies")) {
+               } else if (starts_with(opt, "find-copies")) {
                        opt += strlen("find-copies");
                        if (*opt == 0 || *opt++ == '=')
                                cmd = 'C';
-               } else if (!prefixcmp(opt, "find-renames")) {
+               } else if (starts_with(opt, "find-renames")) {
                        opt += strlen("find-renames");
                        if (*opt == 0 || *opt++ == '=')
                                cmd = 'M';
@@ -4120,9 +4139,9 @@ void diff_debug_filespec(struct diff_filespec *s, int x, const char *one)
                DIFF_FILE_VALID(s) ? "valid" : "invalid",
                s->mode,
                s->sha1_valid ? sha1_to_hex(s->sha1) : "");
-       fprintf(stderr, "queue[%d] %s size %lu flags %d\n",
+       fprintf(stderr, "queue[%d] %s size %lu\n",
                x, one ? one : "",
-               s->size, s->xfrm_flags);
+               s->size);
 }
 
 void diff_debug_filepair(const struct diff_filepair *p, int i)
@@ -4325,7 +4344,7 @@ static void patch_id_consume(void *priv, char *line, unsigned long len)
        int new_len;
 
        /* Ignore line numbers when computing the SHA1 of the patch */
-       if (!prefixcmp(line, "@@ -"))
+       if (starts_with(line, "@@ -"))
                return;
 
        new_len = remove_space(line, len);
diff --git a/diff.h b/diff.h
index 1c05b3a6bec6ce90a97efe366614d23133026b00..ce123fa06fee6fb6e5e76d6a92ea8284a5bbbcf1 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -164,6 +164,8 @@ struct diff_options {
        diff_prefix_fn_t output_prefix;
        int output_prefix_length;
        void *output_prefix_data;
+
+       int diff_path_counter;
 };
 
 enum color_diff {
@@ -330,7 +332,7 @@ extern int diff_flush_patch_id(struct diff_options *, unsigned char *);
 
 extern int diff_result_code(struct diff_options *, int);
 
-extern void diff_no_index(struct rev_info *, int, const char **, int, const char *);
+extern void diff_no_index(struct rev_info *, int, const char **, const char *);
 
 extern int index_differs_from(const char *def, int diff_flags);
 
index 23e93852d8c701760d56e7e728dba7c08367fbe8..fe7f1f4647a16a5882cc4c1e63738c7faa4bc677 100644 (file)
@@ -10,28 +10,21 @@ static int order_cnt;
 
 static void prepare_order(const char *orderfile)
 {
-       int fd, cnt, pass;
+       int cnt, pass;
+       struct strbuf sb = STRBUF_INIT;
        void *map;
        char *cp, *endp;
-       struct stat st;
-       size_t sz;
+       ssize_t sz;
 
        if (order)
                return;
 
-       fd = open(orderfile, O_RDONLY);
-       if (fd < 0)
-               return;
-       if (fstat(fd, &st)) {
-               close(fd);
-               return;
-       }
-       sz = xsize_t(st.st_size);
-       map = mmap(NULL, sz, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
-       close(fd);
-       if (map == MAP_FAILED)
-               return;
+       sz = strbuf_read_file(&sb, orderfile, 0);
+       if (sz < 0)
+               die_errno(_("failed to read orderfile '%s'"), orderfile);
+       map = strbuf_detach(&sb, NULL);
        endp = (char *) map + sz;
+
        for (pass = 0; pass < 2; pass++) {
                cnt = 0;
                cp = map;
@@ -73,15 +66,16 @@ struct pair_order {
 static int match_order(const char *path)
 {
        int i;
-       char p[PATH_MAX];
+       static struct strbuf p = STRBUF_INIT;
 
        for (i = 0; i < order_cnt; i++) {
-               strcpy(p, path);
-               while (p[0]) {
+               strbuf_reset(&p);
+               strbuf_addstr(&p, path);
+               while (p.buf[0]) {
                        char *cp;
-                       if (!fnmatch(order[i], p, 0))
+                       if (!fnmatch(order[i], p.buf, 0))
                                return i;
-                       cp = strrchr(p, '/');
+                       cp = strrchr(p.buf, '/');
                        if (!cp)
                                break;
                        *cp = 0;
index 1c16c8595b21c2712259041c01d4b58d76a60222..79de8cf28dc6919f069ae44f7f036a46264452e9 100644 (file)
@@ -29,10 +29,8 @@ struct diff_filespec {
        char *path;
        void *data;
        void *cnt_data;
-       const char *funcname_pattern_ident;
        unsigned long size;
        int count;               /* Reference count */
-       int xfrm_flags;          /* for use by the xfrm */
        int rename_used;         /* Count of rename users */
        unsigned short mode;     /* file mode */
        unsigned sha1_valid : 1; /* if true, use sha1 and trust mode;
@@ -43,13 +41,13 @@ struct diff_filespec {
        unsigned should_free : 1; /* data should be free()'ed */
        unsigned should_munmap : 1; /* data should be munmap()'ed */
        unsigned dirty_submodule : 2;  /* For submodules: its work tree is dirty */
-       unsigned is_stdin : 1;
 #define DIRTY_SUBMODULE_UNTRACKED 1
 #define DIRTY_SUBMODULE_MODIFIED  2
+       unsigned is_stdin : 1;
        unsigned has_more_entries : 1; /* only appear in combined diff */
-       struct userdiff_driver *driver;
        /* data should be considered "binary"; -1 means "don't know yet" */
-       int is_binary;
+       int is_binary : 2;
+       struct userdiff_driver *driver;
 };
 
 extern struct diff_filespec *alloc_filespec(const char *);
diff --git a/dir.c b/dir.c
index d10a63f731020aab99f9b25a2b96a029d772410d..b35b6330f850f610b582b189d7e4d6a9ba4495db 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -1511,8 +1511,13 @@ static int remove_dir_recurse(struct strbuf *path, int flag, int *kept_up)
        flag &= ~REMOVE_DIR_KEEP_TOPLEVEL;
        dir = opendir(path->buf);
        if (!dir) {
-               /* an empty dir could be removed even if it is unreadble */
-               if (!keep_toplevel)
+               if (errno == ENOENT)
+                       return keep_toplevel ? -1 : 0;
+               else if (errno == EACCES && !keep_toplevel)
+                       /*
+                        * An empty dir could be removable even if it
+                        * is unreadable:
+                        */
                        return rmdir(path->buf);
                else
                        return -1;
@@ -1528,13 +1533,21 @@ static int remove_dir_recurse(struct strbuf *path, int flag, int *kept_up)
 
                strbuf_setlen(path, len);
                strbuf_addstr(path, e->d_name);
-               if (lstat(path->buf, &st))
-                       ; /* fall thru */
-               else if (S_ISDIR(st.st_mode)) {
+               if (lstat(path->buf, &st)) {
+                       if (errno == ENOENT)
+                               /*
+                                * file disappeared, which is what we
+                                * wanted anyway
+                                */
+                               continue;
+                       /* fall thru */
+               } else if (S_ISDIR(st.st_mode)) {
                        if (!remove_dir_recurse(path, flag, &kept_down))
                                continue; /* happy */
-               } else if (!only_empty && !unlink(path->buf))
+               } else if (!only_empty &&
+                          (!unlink(path->buf) || errno == ENOENT)) {
                        continue; /* happy, too */
+               }
 
                /* path too long, stat fails, or non-directory still exists */
                ret = -1;
@@ -1544,7 +1557,7 @@ static int remove_dir_recurse(struct strbuf *path, int flag, int *kept_up)
 
        strbuf_setlen(path, original_len);
        if (!ret && !keep_toplevel && !kept_down)
-               ret = rmdir(path->buf);
+               ret = (!rmdir(path->buf) || errno == ENOENT) ? 0 : -1;
        else if (kept_up)
                /*
                 * report the uplevel that it is not an error that we
index 0a15349cfe38ab76b1d1cc94878cec2a8d754bbd..4a3437d8a61256029655f25c76256f27a6189248 100644 (file)
@@ -10,6 +10,7 @@
 #include "cache.h"
 #include "refs.h"
 #include "fmt-merge-msg.h"
+#include "commit.h"
 
 int trust_executable_bit = 1;
 int trust_ctime = 1;
@@ -97,6 +98,7 @@ const char * const local_repo_env[] = {
        INDEX_ENVIRONMENT,
        NO_REPLACE_OBJECTS_ENVIRONMENT,
        GIT_PREFIX_ENVIRONMENT,
+       GIT_SHALLOW_FILE_ENVIRONMENT,
        NULL
 };
 
@@ -124,6 +126,7 @@ static char *expand_namespace(const char *raw_namespace)
 static void setup_git_env(void)
 {
        const char *gitfile;
+       const char *shallow_file;
 
        git_dir = getenv(GIT_DIR_ENVIRONMENT);
        if (!git_dir)
@@ -147,6 +150,9 @@ static void setup_git_env(void)
                read_replace_refs = 0;
        namespace = expand_namespace(getenv(GIT_NAMESPACE_ENVIRONMENT));
        namespace_len = strlen(namespace);
+       shallow_file = getenv(GIT_SHALLOW_FILE_ENVIRONMENT);
+       if (shallow_file)
+               set_alternate_shallow_file(shallow_file, 0);
 }
 
 int is_bare_repository(void)
@@ -171,7 +177,7 @@ const char *get_git_namespace(void)
 
 const char *strip_namespace(const char *namespaced_ref)
 {
-       if (prefixcmp(namespaced_ref, get_git_namespace()) != 0)
+       if (!starts_with(namespaced_ref, get_git_namespace()))
                return NULL;
        return namespaced_ref + namespace_len;
 }
index f4d9969e5c81f9c1415f0908f090bf62b156e179..4fd18a3574a6e72bb7a299b26b3f97fa20b8052d 100644 (file)
@@ -1877,8 +1877,8 @@ static int read_next_command(void)
                                return EOF;
 
                        if (!seen_data_command
-                               && prefixcmp(command_buf.buf, "feature ")
-                               && prefixcmp(command_buf.buf, "option ")) {
+                               && !starts_with(command_buf.buf, "feature ")
+                               && !starts_with(command_buf.buf, "option ")) {
                                parse_argv();
                        }
 
@@ -1898,7 +1898,7 @@ static int read_next_command(void)
                        rc->prev->next = rc;
                        cmd_tail = rc;
                }
-               if (!prefixcmp(command_buf.buf, "cat-blob ")) {
+               if (starts_with(command_buf.buf, "cat-blob ")) {
                        parse_cat_blob();
                        continue;
                }
@@ -1917,7 +1917,7 @@ static void skip_optional_lf(void)
 
 static void parse_mark(void)
 {
-       if (!prefixcmp(command_buf.buf, "mark :")) {
+       if (starts_with(command_buf.buf, "mark :")) {
                next_mark = strtoumax(command_buf.buf + 6, NULL, 10);
                read_next_command();
        }
@@ -1929,10 +1929,10 @@ static int parse_data(struct strbuf *sb, uintmax_t limit, uintmax_t *len_res)
 {
        strbuf_reset(sb);
 
-       if (prefixcmp(command_buf.buf, "data "))
+       if (!starts_with(command_buf.buf, "data "))
                die("Expected 'data n' command, found: %s", command_buf.buf);
 
-       if (!prefixcmp(command_buf.buf + 5, "<<")) {
+       if (starts_with(command_buf.buf + 5, "<<")) {
                char *term = xstrdup(command_buf.buf + 5 + 2);
                size_t term_len = command_buf.len - 5 - 2;
 
@@ -2306,7 +2306,7 @@ static void file_change_m(struct branch *b)
        if (*p == ':') {
                oe = find_mark(parse_mark_ref_space(&p));
                hashcpy(sha1, oe->idx.sha1);
-       } else if (!prefixcmp(p, "inline ")) {
+       } else if (starts_with(p, "inline ")) {
                inline_data = 1;
                oe = NULL; /* not used with inline_data, but makes gcc happy */
                p += strlen("inline");  /* advance to space */
@@ -2479,7 +2479,7 @@ static void note_change_n(struct branch *b, unsigned char *old_fanout)
        if (*p == ':') {
                oe = find_mark(parse_mark_ref_space(&p));
                hashcpy(sha1, oe->idx.sha1);
-       } else if (!prefixcmp(p, "inline ")) {
+       } else if (starts_with(p, "inline ")) {
                inline_data = 1;
                oe = NULL; /* not used with inline_data, but makes gcc happy */
                p += strlen("inline");  /* advance to space */
@@ -2590,7 +2590,7 @@ static int parse_from(struct branch *b)
        const char *from;
        struct branch *s;
 
-       if (prefixcmp(command_buf.buf, "from "))
+       if (!starts_with(command_buf.buf, "from "))
                return 0;
 
        if (b->branch_tree.tree) {
@@ -2636,7 +2636,7 @@ static struct hash_list *parse_merge(unsigned int *count)
        struct branch *s;
 
        *count = 0;
-       while (!prefixcmp(command_buf.buf, "merge ")) {
+       while (starts_with(command_buf.buf, "merge ")) {
                from = strchr(command_buf.buf, ' ') + 1;
                n = xmalloc(sizeof(*n));
                s = lookup_branch(from);
@@ -2687,11 +2687,11 @@ static void parse_new_commit(void)
 
        read_next_command();
        parse_mark();
-       if (!prefixcmp(command_buf.buf, "author ")) {
+       if (starts_with(command_buf.buf, "author ")) {
                author = parse_ident(command_buf.buf + 7);
                read_next_command();
        }
-       if (!prefixcmp(command_buf.buf, "committer ")) {
+       if (starts_with(command_buf.buf, "committer ")) {
                committer = parse_ident(command_buf.buf + 10);
                read_next_command();
        }
@@ -2712,19 +2712,19 @@ static void parse_new_commit(void)
 
        /* file_change* */
        while (command_buf.len > 0) {
-               if (!prefixcmp(command_buf.buf, "M "))
+               if (starts_with(command_buf.buf, "M "))
                        file_change_m(b);
-               else if (!prefixcmp(command_buf.buf, "D "))
+               else if (starts_with(command_buf.buf, "D "))
                        file_change_d(b);
-               else if (!prefixcmp(command_buf.buf, "R "))
+               else if (starts_with(command_buf.buf, "R "))
                        file_change_cr(b, 1);
-               else if (!prefixcmp(command_buf.buf, "C "))
+               else if (starts_with(command_buf.buf, "C "))
                        file_change_cr(b, 0);
-               else if (!prefixcmp(command_buf.buf, "N "))
+               else if (starts_with(command_buf.buf, "N "))
                        note_change_n(b, &prev_fanout);
                else if (!strcmp("deleteall", command_buf.buf))
                        file_change_deleteall(b);
-               else if (!prefixcmp(command_buf.buf, "ls "))
+               else if (starts_with(command_buf.buf, "ls "))
                        parse_ls(b);
                else {
                        unread_command_buf = 1;
@@ -2793,7 +2793,7 @@ static void parse_new_tag(void)
        read_next_command();
 
        /* from ... */
-       if (prefixcmp(command_buf.buf, "from "))
+       if (!starts_with(command_buf.buf, "from "))
                die("Expected from command, got %s", command_buf.buf);
        from = strchr(command_buf.buf, ' ') + 1;
        s = lookup_branch(from);
@@ -2821,7 +2821,7 @@ static void parse_new_tag(void)
        read_next_command();
 
        /* tagger ... */
-       if (!prefixcmp(command_buf.buf, "tagger ")) {
+       if (starts_with(command_buf.buf, "tagger ")) {
                tagger = parse_ident(command_buf.buf + 7);
                read_next_command();
        } else
@@ -3209,7 +3209,7 @@ static void option_export_pack_edges(const char *edges)
 
 static int parse_one_option(const char *option)
 {
-       if (!prefixcmp(option, "max-pack-size=")) {
+       if (starts_with(option, "max-pack-size=")) {
                unsigned long v;
                if (!git_parse_ulong(option + 14, &v))
                        return 0;
@@ -3221,20 +3221,20 @@ static int parse_one_option(const char *option)
                        v = 1024 * 1024;
                }
                max_packsize = v;
-       } else if (!prefixcmp(option, "big-file-threshold=")) {
+       } else if (starts_with(option, "big-file-threshold=")) {
                unsigned long v;
                if (!git_parse_ulong(option + 19, &v))
                        return 0;
                big_file_threshold = v;
-       } else if (!prefixcmp(option, "depth=")) {
+       } else if (starts_with(option, "depth=")) {
                option_depth(option + 6);
-       } else if (!prefixcmp(option, "active-branches=")) {
+       } else if (starts_with(option, "active-branches=")) {
                option_active_branches(option + 16);
-       } else if (!prefixcmp(option, "export-pack-edges=")) {
+       } else if (starts_with(option, "export-pack-edges=")) {
                option_export_pack_edges(option + 18);
-       } else if (!prefixcmp(option, "quiet")) {
+       } else if (starts_with(option, "quiet")) {
                show_stats = 0;
-       } else if (!prefixcmp(option, "stats")) {
+       } else if (starts_with(option, "stats")) {
                show_stats = 1;
        } else {
                return 0;
@@ -3245,14 +3245,14 @@ static int parse_one_option(const char *option)
 
 static int parse_one_feature(const char *feature, int from_stream)
 {
-       if (!prefixcmp(feature, "date-format=")) {
+       if (starts_with(feature, "date-format=")) {
                option_date_format(feature + 12);
-       } else if (!prefixcmp(feature, "import-marks=")) {
+       } else if (starts_with(feature, "import-marks=")) {
                option_import_marks(feature + 13, from_stream, 0);
-       } else if (!prefixcmp(feature, "import-marks-if-exists=")) {
+       } else if (starts_with(feature, "import-marks-if-exists=")) {
                option_import_marks(feature + strlen("import-marks-if-exists="),
                                        from_stream, 1);
-       } else if (!prefixcmp(feature, "export-marks=")) {
+       } else if (starts_with(feature, "export-marks=")) {
                option_export_marks(feature + 13);
        } else if (!strcmp(feature, "cat-blob")) {
                ; /* Don't die - this feature is supported */
@@ -3350,7 +3350,7 @@ static void parse_argv(void)
                if (parse_one_feature(a + 2, 0))
                        continue;
 
-               if (!prefixcmp(a + 2, "cat-blob-fd=")) {
+               if (starts_with(a + 2, "cat-blob-fd=")) {
                        option_cat_blob_fd(a + 2 + strlen("cat-blob-fd="));
                        continue;
                }
@@ -3404,25 +3404,25 @@ int main(int argc, char **argv)
        while (read_next_command() != EOF) {
                if (!strcmp("blob", command_buf.buf))
                        parse_new_blob();
-               else if (!prefixcmp(command_buf.buf, "ls "))
+               else if (starts_with(command_buf.buf, "ls "))
                        parse_ls(NULL);
-               else if (!prefixcmp(command_buf.buf, "commit "))
+               else if (starts_with(command_buf.buf, "commit "))
                        parse_new_commit();
-               else if (!prefixcmp(command_buf.buf, "tag "))
+               else if (starts_with(command_buf.buf, "tag "))
                        parse_new_tag();
-               else if (!prefixcmp(command_buf.buf, "reset "))
+               else if (starts_with(command_buf.buf, "reset "))
                        parse_reset_branch();
                else if (!strcmp("checkpoint", command_buf.buf))
                        parse_checkpoint();
                else if (!strcmp("done", command_buf.buf))
                        break;
-               else if (!prefixcmp(command_buf.buf, "progress "))
+               else if (starts_with(command_buf.buf, "progress "))
                        parse_progress();
-               else if (!prefixcmp(command_buf.buf, "feature "))
+               else if (starts_with(command_buf.buf, "feature "))
                        parse_feature();
-               else if (!prefixcmp(command_buf.buf, "option git "))
+               else if (starts_with(command_buf.buf, "option git "))
                        parse_option();
-               else if (!prefixcmp(command_buf.buf, "option "))
+               else if (starts_with(command_buf.buf, "option "))
                        /* ignore non-git options*/;
                else
                        die("Unsupported command: %s", command_buf.buf);
index 5a1200f1a06f0ba3a9366eb9221d982d423d8da0..90fdd49821a1d6d3e104124444e37ac4b9b03fa2 100644 (file)
@@ -13,6 +13,7 @@
 #include "transport.h"
 #include "version.h"
 #include "prio-queue.h"
+#include "sha1-array.h"
 
 static int transfer_unpack_limit = -1;
 static int fetch_unpack_limit = -1;
@@ -174,9 +175,9 @@ static void consume_shallow_list(struct fetch_pack_args *args, int fd)
                 */
                char *line;
                while ((line = packet_read_line(fd, NULL))) {
-                       if (!prefixcmp(line, "shallow "))
+                       if (starts_with(line, "shallow "))
                                continue;
-                       if (!prefixcmp(line, "unshallow "))
+                       if (starts_with(line, "unshallow "))
                                continue;
                        die("git fetch-pack: expected shallow list");
                }
@@ -192,7 +193,7 @@ static enum ack_type get_ack(int fd, unsigned char *result_sha1)
                die("git fetch-pack: expected ACK/NAK, got EOF");
        if (!strcmp(line, "NAK"))
                return NAK;
-       if (!prefixcmp(line, "ACK ")) {
+       if (starts_with(line, "ACK ")) {
                if (!get_sha1_hex(line+4, result_sha1)) {
                        if (len < 45)
                                return ACK;
@@ -309,7 +310,7 @@ static int find_common(struct fetch_pack_args *args,
        }
 
        if (is_repository_shallow())
-               write_shallow_commits(&req_buf, 1);
+               write_shallow_commits(&req_buf, 1, NULL);
        if (args->depth > 0)
                packet_buf_write(&req_buf, "deepen %d", args->depth);
        packet_buf_flush(&req_buf);
@@ -321,13 +322,13 @@ static int find_common(struct fetch_pack_args *args,
 
                send_request(args, fd[1], &req_buf);
                while ((line = packet_read_line(fd[0], NULL))) {
-                       if (!prefixcmp(line, "shallow ")) {
+                       if (starts_with(line, "shallow ")) {
                                if (get_sha1_hex(line + 8, sha1))
                                        die("invalid shallow line: %s", line);
                                register_shallow(sha1);
                                continue;
                        }
-                       if (!prefixcmp(line, "unshallow ")) {
+                       if (starts_with(line, "unshallow ")) {
                                if (get_sha1_hex(line + 10, sha1))
                                        die("invalid unshallow line: %s", line);
                                if (!lookup_object(sha1))
@@ -505,7 +506,7 @@ static void filter_refs(struct fetch_pack_args *args,
                next = ref->next;
 
                if (!memcmp(ref->name, "refs/", 5) &&
-                   check_refname_format(ref->name + 5, 0))
+                   check_refname_format(ref->name, 0))
                        ; /* trash */
                else {
                        while (i < nr_sought) {
@@ -521,7 +522,7 @@ static void filter_refs(struct fetch_pack_args *args,
                }
 
                if (!keep && args->fetch_all &&
-                   (!args->depth || prefixcmp(ref->name, "refs/tags/")))
+                   (!args->depth || !starts_with(ref->name, "refs/tags/")))
                        keep = 1;
 
                if (keep) {
@@ -772,6 +773,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
                                 int fd[2],
                                 const struct ref *orig_ref,
                                 struct ref **sought, int nr_sought,
+                                struct shallow_info *si,
                                 char **pack_lockfile)
 {
        struct ref *ref = copy_ref_list(orig_ref);
@@ -848,7 +850,10 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
        if (args->stateless_rpc)
                packet_flush(fd[1]);
        if (args->depth > 0)
-               setup_alternate_shallow(&shallow_lock, &alternate_shallow_file);
+               setup_alternate_shallow(&shallow_lock, &alternate_shallow_file,
+                                       NULL);
+       else if (si->nr_ours || si->nr_theirs)
+               alternate_shallow_file = setup_temporary_shallow(si->shallow);
        else
                alternate_shallow_file = NULL;
        if (get_pack(args, fd, pack_lockfile))
@@ -922,14 +927,121 @@ static int remove_duplicates_in_refs(struct ref **ref, int nr)
        return dst;
 }
 
+static void update_shallow(struct fetch_pack_args *args,
+                          struct ref **sought, int nr_sought,
+                          struct shallow_info *si)
+{
+       struct sha1_array ref = SHA1_ARRAY_INIT;
+       int *status;
+       int i;
+
+       if (args->depth > 0 && alternate_shallow_file) {
+               if (*alternate_shallow_file == '\0') { /* --unshallow */
+                       unlink_or_warn(git_path("shallow"));
+                       rollback_lock_file(&shallow_lock);
+               } else
+                       commit_lock_file(&shallow_lock);
+               return;
+       }
+
+       if (!si->shallow || !si->shallow->nr)
+               return;
+
+       if (alternate_shallow_file) {
+               /*
+                * The temporary shallow file is only useful for
+                * index-pack and unpack-objects because it may
+                * contain more roots than we want. Delete it.
+                */
+               if (*alternate_shallow_file)
+                       unlink(alternate_shallow_file);
+               free((char *)alternate_shallow_file);
+       }
+
+       if (args->cloning) {
+               /*
+                * remote is shallow, but this is a clone, there are
+                * no objects in repo to worry about. Accept any
+                * shallow points that exist in the pack (iow in repo
+                * after get_pack() and reprepare_packed_git())
+                */
+               struct sha1_array extra = SHA1_ARRAY_INIT;
+               unsigned char (*sha1)[20] = si->shallow->sha1;
+               for (i = 0; i < si->shallow->nr; i++)
+                       if (has_sha1_file(sha1[i]))
+                               sha1_array_append(&extra, sha1[i]);
+               if (extra.nr) {
+                       setup_alternate_shallow(&shallow_lock,
+                                               &alternate_shallow_file,
+                                               &extra);
+                       commit_lock_file(&shallow_lock);
+               }
+               sha1_array_clear(&extra);
+               return;
+       }
+
+       if (!si->nr_ours && !si->nr_theirs)
+               return;
+
+       remove_nonexistent_theirs_shallow(si);
+       if (!si->nr_ours && !si->nr_theirs)
+               return;
+       for (i = 0; i < nr_sought; i++)
+               sha1_array_append(&ref, sought[i]->old_sha1);
+       si->ref = &ref;
+
+       if (args->update_shallow) {
+               /*
+                * remote is also shallow, .git/shallow may be updated
+                * so all refs can be accepted. Make sure we only add
+                * shallow roots that are actually reachable from new
+                * refs.
+                */
+               struct sha1_array extra = SHA1_ARRAY_INIT;
+               unsigned char (*sha1)[20] = si->shallow->sha1;
+               assign_shallow_commits_to_refs(si, NULL, NULL);
+               if (!si->nr_ours && !si->nr_theirs) {
+                       sha1_array_clear(&ref);
+                       return;
+               }
+               for (i = 0; i < si->nr_ours; i++)
+                       sha1_array_append(&extra, sha1[si->ours[i]]);
+               for (i = 0; i < si->nr_theirs; i++)
+                       sha1_array_append(&extra, sha1[si->theirs[i]]);
+               setup_alternate_shallow(&shallow_lock,
+                                       &alternate_shallow_file,
+                                       &extra);
+               commit_lock_file(&shallow_lock);
+               sha1_array_clear(&extra);
+               sha1_array_clear(&ref);
+               return;
+       }
+
+       /*
+        * remote is also shallow, check what ref is safe to update
+        * without updating .git/shallow
+        */
+       status = xcalloc(nr_sought, sizeof(*status));
+       assign_shallow_commits_to_refs(si, NULL, status);
+       if (si->nr_ours || si->nr_theirs) {
+               for (i = 0; i < nr_sought; i++)
+                       if (status[i])
+                               sought[i]->status = REF_STATUS_REJECT_SHALLOW;
+       }
+       free(status);
+       sha1_array_clear(&ref);
+}
+
 struct ref *fetch_pack(struct fetch_pack_args *args,
                       int fd[], struct child_process *conn,
                       const struct ref *ref,
                       const char *dest,
                       struct ref **sought, int nr_sought,
+                      struct sha1_array *shallow,
                       char **pack_lockfile)
 {
        struct ref *ref_cpy;
+       struct shallow_info si;
 
        fetch_pack_setup();
        if (nr_sought)
@@ -939,16 +1051,11 @@ struct ref *fetch_pack(struct fetch_pack_args *args,
                packet_flush(fd[1]);
                die("no matching remote head");
        }
-       ref_cpy = do_fetch_pack(args, fd, ref, sought, nr_sought, pack_lockfile);
-
-       if (args->depth > 0 && alternate_shallow_file) {
-               if (*alternate_shallow_file == '\0') { /* --unshallow */
-                       unlink_or_warn(git_path("shallow"));
-                       rollback_lock_file(&shallow_lock);
-               } else
-                       commit_lock_file(&shallow_lock);
-       }
-
+       prepare_shallow_info(&si, shallow);
+       ref_cpy = do_fetch_pack(args, fd, ref, sought, nr_sought,
+                               &si, pack_lockfile);
        reprepare_packed_git();
+       update_shallow(args, sought, nr_sought, &si);
+       clear_shallow_info(&si);
        return ref_cpy;
 }
index 461cbf39b2affa71e27dd1a95c24d6c65a938d62..bb7fd76e5939d812f1f89b787324ae6ec519ca15 100644 (file)
@@ -4,22 +4,27 @@
 #include "string-list.h"
 #include "run-command.h"
 
+struct sha1_array;
+
 struct fetch_pack_args {
        const char *uploadpack;
        int unpacklimit;
        int depth;
-       unsigned quiet:1,
-               keep_pack:1,
-               lock_pack:1,
-               use_thin_pack:1,
-               fetch_all:1,
-               stdin_refs:1,
-               verbose:1,
-               no_progress:1,
-               include_tag:1,
-               stateless_rpc:1,
-               check_self_contained_and_connected:1,
-               self_contained_and_connected:1;
+       unsigned quiet:1;
+       unsigned keep_pack:1;
+       unsigned lock_pack:1;
+       unsigned use_thin_pack:1;
+       unsigned fetch_all:1;
+       unsigned stdin_refs:1;
+       unsigned diag_url:1;
+       unsigned verbose:1;
+       unsigned no_progress:1;
+       unsigned include_tag:1;
+       unsigned stateless_rpc:1;
+       unsigned check_self_contained_and_connected:1;
+       unsigned self_contained_and_connected:1;
+       unsigned cloning:1;
+       unsigned update_shallow:1;
 };
 
 /*
@@ -33,6 +38,7 @@ struct ref *fetch_pack(struct fetch_pack_args *args,
                       const char *dest,
                       struct ref **sought,
                       int nr_sought,
+                      struct sha1_array *shallow,
                       char **pack_lockfile);
 
 #endif
index 71e954563d7068500bd41269dd57f01b097f7c3d..8b2da4641f8b20ff3cc70c63636de878eec72470 100644 (file)
--- a/gettext.c
+++ b/gettext.c
@@ -29,6 +29,17 @@ int use_gettext_poison(void)
 #endif
 
 #ifndef NO_GETTEXT
+static int test_vsnprintf(const char *fmt, ...)
+{
+       char buf[26];
+       int ret;
+       va_list ap;
+       va_start(ap, fmt);
+       ret = vsnprintf(buf, sizeof(buf), fmt, ap);
+       va_end(ap);
+       return ret;
+}
+
 static const char *charset;
 static void init_gettext_charset(const char *domain)
 {
@@ -99,9 +110,7 @@ static void init_gettext_charset(const char *domain)
           $ LANGUAGE= LANG=de_DE.utf8 ./test
           test: Kein passendes Ger?t gefunden
 
-          In the long term we should probably see about getting that
-          vsnprintf bug in glibc fixed, and audit our code so it won't
-          fall apart under a non-C locale.
+          The vsnprintf bug has been fixed since glibc 2.17.
 
           Then we could simply set LC_CTYPE from the environment, which would
           make things like the external perror(3) messages work.
@@ -115,7 +124,9 @@ static void init_gettext_charset(const char *domain)
        setlocale(LC_CTYPE, "");
        charset = locale_charset();
        bind_textdomain_codeset(domain, charset);
-       setlocale(LC_CTYPE, "C");
+       /* the string is taken from v0.99.6~1 */
+       if (test_vsnprintf("%.*s", 13, "David_K\345gedal") < 0)
+               setlocale(LC_CTYPE, "C");
 }
 
 void git_setup_gettext(void)
index 7776f126d3bd4facfe987bd844a339c8a17b102a..cbd86c37f51c00679498107d1a05131c19afa36e 100644 (file)
@@ -75,7 +75,7 @@
 # endif
 #elif !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__USLC__) && \
       !defined(_M_UNIX) && !defined(__sgi) && !defined(__DragonFly__) && \
-      !defined(__TANDEM) && !defined(__QNX__)
+      !defined(__TANDEM) && !defined(__QNX__) && !defined(__MirBSD__)
 #define _XOPEN_SOURCE 600 /* glibc2 and AIX 5.3L need 500, OpenBSD needs 600 for S_ISLNK() */
 #define _XOPEN_SOURCE_EXTENDED 1 /* AIX 5.3L needs this */
 #endif
@@ -350,7 +350,9 @@ extern void set_die_routine(NORETURN_PTR void (*routine)(const char *err, va_lis
 extern void set_error_routine(void (*routine)(const char *err, va_list params));
 extern void set_die_is_recursing_routine(int (*routine)(void));
 
+extern int starts_with(const char *str, const char *prefix);
 extern int prefixcmp(const char *str, const char *prefix);
+extern int ends_with(const char *str, const char *suffix);
 extern int suffixcmp(const char *str, const char *suffix);
 
 static inline const char *skip_prefix(const char *str, const char *prefix)
index b00ed95dba9c035ee812d3a2d6db2d13a1944a33..7ef36b9482ccc4111f2c888bf5ccbaa2421a5875 100755 (executable)
@@ -40,7 +40,8 @@ launch_merge_tool () {
        # the user with the real $MERGED name before launching $merge_tool.
        if should_prompt
        then
-               printf "\nViewing: '%s'\n" "$MERGED"
+               printf "\nViewing (%s/%s): '%s'\n" "$GIT_DIFF_PATH_COUNTER" \
+                       "$GIT_DIFF_PATH_TOTAL" "$MERGED"
                if use_ext_cmd
                then
                        printf "Launch '%s' [Y/n]: " \
index 0a1f8d4f8a9187a0d5decf20f9c926dbd570a8db..a9ea21826a4ce016cfa270ac5945f29d10fb897d 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=0.18.GITGUI
+DEF_VER=0.19.GITGUI
 
 LF='
 '
index e9c2bc347dc77fb08bc6515a604a744fc4ee5795..cde8b2ea31969192136d5c541458cc43c358d4dc 100644 (file)
@@ -4,7 +4,7 @@ all::
 #
 # 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.
+# TCL_PATH must be valid for this to work.
 #
 
 GIT-VERSION-FILE: FORCE
index 153f85da068294917f5e60afca4fb6715f3f32ab..cf2209b4f201b990cffab6d454599b9fa6932fc0 100755 (executable)
@@ -894,6 +894,7 @@ set default_config(gui.textconv) true
 set default_config(gui.pruneduringfetch) false
 set default_config(gui.trustmtime) false
 set default_config(gui.fastcopyblame) false
+set default_config(gui.maxrecentrepo) 10
 set default_config(gui.copyblamethreshold) 40
 set default_config(gui.blamehistoryctx) 7
 set default_config(gui.diffcontext) 5
@@ -912,6 +913,7 @@ set font_descs {
        {fontdiff font_diff {mc "Diff/Console Font"}}
 }
 set default_config(gui.stageuntracked) ask
+set default_config(gui.displayuntracked) true
 
 ######################################################################
 ##
@@ -1282,7 +1284,11 @@ apply_config
 
 # v1.7.0 introduced --show-toplevel to return the canonical work-tree
 if {[package vsatisfies $_git_version 1.7.0]} {
-       set _gitworktree [git rev-parse --show-toplevel]
+       if { [is_Cygwin] } {
+               catch {set _gitworktree [exec cygpath --windows [git rev-parse --show-toplevel]]}
+       } else {
+               set _gitworktree [git rev-parse --show-toplevel]
+       }
 } else {
        # try to set work tree from environment, core.worktree or use
        # cdup to obtain a relative path to the top of the worktree. If
@@ -1550,18 +1556,23 @@ proc rescan_stage2 {fd after} {
        set buf_rdf {}
        set buf_rlo {}
 
-       set rescan_active 3
+       set rescan_active 2
        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]
 
        fconfigure $fd_di -blocking 0 -translation binary -encoding binary
        fconfigure $fd_df -blocking 0 -translation binary -encoding binary
-       fconfigure $fd_lo -blocking 0 -translation binary -encoding binary
+
        fileevent $fd_di readable [list read_diff_index $fd_di $after]
        fileevent $fd_df readable [list read_diff_files $fd_df $after]
-       fileevent $fd_lo readable [list read_ls_others $fd_lo $after]
+
+       if {[is_config_true gui.displayuntracked]} {
+               set fd_lo [eval git_read ls-files --others -z $ls_others]
+               fconfigure $fd_lo -blocking 0 -translation binary -encoding binary
+               fileevent $fd_lo readable [list read_ls_others $fd_lo $after]
+               incr rescan_active
+       }
 }
 
 proc load_message {file {encoding {}}} {
@@ -2654,6 +2665,16 @@ if {![is_bare]} {
        .mbar.repository add command \
                -label [mc "Explore Working Copy"] \
                -command {do_explore}
+}
+
+if {[is_Windows]} {
+       .mbar.repository add command \
+               -label [mc "Git Bash"] \
+               -command {eval exec [auto_execok start] \
+                                         [list "Git Bash" bash --login -l &]}
+}
+
+if {[is_Windows] || ![is_bare]} {
        .mbar.repository add separator
 }
 
@@ -3203,13 +3224,29 @@ unset i
 
 # -- Diff and Commit Area
 #
-${NS}::frame .vpane.lower -height 300 -width 400
-${NS}::frame .vpane.lower.commarea
-${NS}::frame .vpane.lower.diff -relief sunken -borderwidth 1
-pack .vpane.lower.diff -fill both -expand 1
-pack .vpane.lower.commarea -side bottom -fill x
-.vpane add .vpane.lower
-if {!$use_ttk} {.vpane paneconfigure .vpane.lower -sticky nsew}
+if {$have_tk85} {
+       ${NS}::panedwindow .vpane.lower -orient vertical
+       ${NS}::frame .vpane.lower.commarea
+       ${NS}::frame .vpane.lower.diff -relief sunken -borderwidth 1 -height 500
+       .vpane.lower add .vpane.lower.diff
+       .vpane.lower add .vpane.lower.commarea
+       .vpane add .vpane.lower
+       if {$use_ttk} {
+               .vpane.lower pane .vpane.lower.diff -weight 1
+               .vpane.lower pane .vpane.lower.commarea -weight 0
+       } else {
+               .vpane.lower paneconfigure .vpane.lower.diff -stretch always
+               .vpane.lower paneconfigure .vpane.lower.commarea -stretch never
+       }
+} else {
+       frame .vpane.lower -height 300 -width 400
+       frame .vpane.lower.commarea
+       frame .vpane.lower.diff -relief sunken -borderwidth 1
+       pack .vpane.lower.diff -fill both -expand 1
+       pack .vpane.lower.commarea -side bottom -fill x
+       .vpane add .vpane.lower
+       .vpane paneconfigure .vpane.lower -sticky nsew
+}
 
 # -- Commit Area Buttons
 #
index 324f7744c495dc2eb9a1d4acde09352d7948efe8..b1d15f46218e793866bee9300684663e2c04fd29 100644 (file)
@@ -5,7 +5,7 @@ class blame {
 
 image create photo ::blame::img_back_arrow -data {R0lGODlhGAAYAIUAAPwCBEzKXFTSZIz+nGzmhGzqfGTidIT+nEzGXHTqhGzmfGzifFzadETCVES+VARWDFzWbHzyjAReDGTadFTOZDSyRDyyTCymPARaFGTedFzSbDy2TCyqRCyqPARaDAyCHES6VDy6VCyiPAR6HCSeNByWLARyFARiDARqFGTifARiFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAAALAAAAAAYABgAAAajQIBwSCwaj8ikcsk0BppJwRPqHEypQwHBis0WDAdEFyBIKBaMAKLBdjQeSkFBYTBAIvgEoS6JmhUTEwIUDQ4VFhcMGEhyCgoZExoUaxsWHB0THkgfAXUGAhoBDSAVFR0XBnCbDRmgog0hpSIiDJpJIyEQhBUcJCIlwA22SSYVogknEg8eD82qSigdDSknY0IqJQXPYxIl1dZCGNvWw+Dm510GQQAh/mhDcmVhdGVkIGJ5IEJNUFRvR0lGIFBybyB2ZXJzaW9uIDIuNQ0KqSBEZXZlbENvciAxOTk3LDE5OTguIEFsbCByaWdodHMgcmVzZXJ2ZWQuDQpodHRwOi8vd3d3LmRldmVsY29yLmNvbQA7}
 
-# Persistant data (survives loads)
+# Persistent data (survives loads)
 #
 field history {}; # viewer history: {commit path}
 field header    ; # array commit,key -> header field
index ee58981f539bfd27dbba99db47d6f85102b0e7a6..3c10bc656d207553c8c093abcca275dd253a57fb 100644 (file)
@@ -24,6 +24,10 @@ field sorted_recent       ; # recent repositories (sorted)
 constructor pick {} {
        global M1T M1B use_ttk NS
 
+       if {[set maxrecent [get_config gui.maxrecentrepo]] eq {}} {
+               set maxrecent 10
+       }
+
        make_dialog top w
        wm title $top [mc "Git Gui"]
 
@@ -148,7 +152,7 @@ constructor pick {} {
                        -background [get_bg_color $w_body.recentlabel] \
                        -wrap none \
                        -width 50 \
-                       -height 10
+                       -height $maxrecent
                $w_recentlist tag conf link \
                        -foreground blue \
                        -underline 1
@@ -264,7 +268,11 @@ proc _append_recentrepos {path} {
        git config --global --add gui.recentrepo $path
        load_config 1
 
-       while {[llength $recent] > 10} {
+       if {[set maxrecent [get_config gui.maxrecentrepo]] eq {}} {
+               set maxrecent 10
+       }
+
+       while {[llength $recent] > $maxrecent} {
                _unset_recentrepo [lindex $recent 0]
                set recent [lrange $recent 1 end]
        }
index 8efbbdde21123dd65412cd5fe6dbb506966b0af9..74a81a7b42779c006fb0c0060724359fca2b33d9 100644 (file)
@@ -414,7 +414,7 @@ proc revert_helper {txt paths} {
        # 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).
+       # rest of string (in different 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,
index 0cf1da1d7ebe75fe91322d9b404dbf4bd8a3b00f..23c9ae72a43c3f2b6ac0409ded488869f3b2de0d 100644 (file)
@@ -150,6 +150,7 @@ proc do_options {} {
                {b gui.matchtrackingbranch {mc "Match Tracking Branches"}}
                {b gui.textconv {mc "Use Textconv For Diffs and Blames"}}
                {b gui.fastcopyblame {mc "Blame Copy Only On Changed Files"}}
+               {i-0..100 gui.maxrecentrepo {mc "Maximum Length of Recent Repositories List"}}
                {i-20..200 gui.copyblamethreshold {mc "Minimum Letters To Blame Copy On"}}
                {i-0..300 gui.blamehistoryctx {mc "Blame History Context Radius (days)"}}
                {i-1..99 gui.diffcontext {mc "Number of Diff Context Lines"}}
@@ -159,6 +160,7 @@ proc do_options {} {
                {c gui.encoding {mc "Default File Contents Encoding"}}
                {b gui.warndetachedcommit {mc "Warn before committing to a detached head"}}
                {s gui.stageuntracked {mc "Staging of untracked files"} {list "yes" "no" "ask"}}
+               {b gui.displayuntracked {mc "Show untracked files"}}
                } {
                set type [lindex $option 0]
                set name [lindex $option 1]
index e6120303e940ee6c849bf706415c1b450e68b57f..538d61c792defa7a8e19736039fa5a9af630125c 100644 (file)
@@ -14,7 +14,7 @@ field w_menu      ; # context menu for the widget
 field s_menuidx 0 ; # last index of insertion into $w_menu
 
 field s_i           {} ; # timer registration for _run callbacks
-field s_clear        0 ; # did we erase mispelled tags yet?
+field s_clear        0 ; # did we erase misspelled tags yet?
 field s_seen    [list] ; # lines last seen from $w_text in _run
 field s_checked [list] ; # lines already checked
 field s_pending [list] ; # [$line $data] sent to ispell/aspell
@@ -259,7 +259,7 @@ method _run {} {
                if {$n == $cur_line
                 && ![regexp {^\W$} [$w_text get $cur_pos insert]]} {
 
-                       # If the current word is mispelled remove the tag
+                       # If the current word is misspelled remove the tag
                        # but force a spellcheck later.
                        #
                        set tags [$w_text tag names $cur_pos]
index b3bf15fa1c1a41265460664417caf47265553a4f..1ade121c4cc532d565bdfc9f03a7e766963c34a8 100644 (file)
@@ -24,5 +24,7 @@
        <string>GITg</string>
        <key>CFBundleVersion</key>
        <string>@@GITGUI_VERSION@@</string>
+       <key>NSHighResolutionCapable</key>
+       <true/>
 </dict>
 </plist>
diff --git a/git-gui/po/bg.po b/git-gui/po/bg.po
new file mode 100644 (file)
index 0000000..89b3a4e
--- /dev/null
@@ -0,0 +1,2694 @@
+# Bulgarian translation of git-gui po-file.
+# Copyright (C) 2012, 2013, 2014 Alexander Shopov <ash@kambanaria.org>.
+# This file is distributed under the same license as the git package.
+# Alexander Shopov <ash@kambanaria.org>, 2012, 2013, 2014.
+#
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: git-gui master\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2014-01-13 20:45+0200\n"
+"PO-Revision-Date: 2014-01-13 21:31+0200\n"
+"Last-Translator: Alexander Shopov <ash@kambanaria.org>\n"
+"Language-Team: Bulgarian <dict@fsa-bg.org>\n"
+"Language: bg\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:859
+#, tcl-format
+msgid "Invalid font specified in %s:"
+msgstr "Указан е неправилен шрифт в %s:"
+
+#: git-gui.sh:912
+msgid "Main Font"
+msgstr "Основен шрифт"
+
+#: git-gui.sh:913
+msgid "Diff/Console Font"
+msgstr "Шрифт за разликите/конзолата"
+
+#: git-gui.sh:928 git-gui.sh:942 git-gui.sh:955 git-gui.sh:1045
+#: git-gui.sh:1064 git-gui.sh:3115
+msgid "git-gui: fatal error"
+msgstr "git-gui: фатална грешка"
+
+#: git-gui.sh:929
+msgid "Cannot find git in PATH."
+msgstr "Командата git липсва в пътя (PATH)."
+
+#: git-gui.sh:956
+msgid "Cannot parse Git version string:"
+msgstr "Низът с версията на Git не може да бъде интерпретиран:"
+
+#: git-gui.sh:981
+#, 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 изисква Git, версия поне 1.5.0.\n"
+"\n"
+"Да се приеме ли, че „%s“ е версия 1.5.0?\n"
+
+#: git-gui.sh:1278
+msgid "Git directory not found:"
+msgstr "Директорията на Git не е открита:"
+
+#: git-gui.sh:1312
+msgid "Cannot move to top of working directory:"
+msgstr "Не може да се премине към родителската  директория."
+
+#: git-gui.sh:1320
+msgid "Cannot use bare repository:"
+msgstr "Голо хранилище не може да се използва:"
+
+#: git-gui.sh:1328
+msgid "No working directory"
+msgstr "Работната директория липсва"
+
+#: git-gui.sh:1500 lib/checkout_op.tcl:306
+msgid "Refreshing file status..."
+msgstr "Обновяване на състоянието на файла…"
+
+#: git-gui.sh:1560
+msgid "Scanning for modified files ..."
+msgstr "Проверка за променени файлове…"
+
+#: git-gui.sh:1632
+msgid "Calling prepare-commit-msg hook..."
+msgstr "Куката prepare-commit-msg се изпълнява в момента…"
+
+#: git-gui.sh:1649
+msgid "Commit declined by prepare-commit-msg hook."
+msgstr "Подаването е отхвърлено от куката prepare-commit-msg."
+
+#: git-gui.sh:1807 lib/browser.tcl:252
+msgid "Ready."
+msgstr "Готово."
+
+#: git-gui.sh:1965
+#, tcl-format
+msgid "Displaying only %s of %s files."
+msgstr "Показване на само %s от %s файла."
+
+#: git-gui.sh:2091
+msgid "Unmodified"
+msgstr "Непроменен"
+
+#: git-gui.sh:2093
+msgid "Modified, not staged"
+msgstr "Променен, но не е в индекса"
+
+#: git-gui.sh:2094 git-gui.sh:2106
+msgid "Staged for commit"
+msgstr "В индекса за подаване"
+
+#: git-gui.sh:2095 git-gui.sh:2107
+msgid "Portions staged for commit"
+msgstr "Части са в индекса за подаване"
+
+#: git-gui.sh:2096 git-gui.sh:2108
+msgid "Staged for commit, missing"
+msgstr "В индекса за подаване, но липсва"
+
+#: git-gui.sh:2098
+msgid "File type changed, not staged"
+msgstr "Видът на файла е сменен, но не е в индекса"
+
+#: git-gui.sh:2099 git-gui.sh:2100
+msgid "File type changed, old type staged for commit"
+msgstr "Видът на файла е сменен, в индекса е старият"
+
+#: git-gui.sh:2101
+msgid "File type changed, staged"
+msgstr "Видът на файла е сменен и е в индекса"
+
+#: git-gui.sh:2102
+msgid "File type change staged, modification not staged"
+msgstr "Видът на файла е сменен, промяната не е в индекса"
+
+#: git-gui.sh:2103
+msgid "File type change staged, file missing"
+msgstr "Видът на файла е сменен, файлът липсва"
+
+#: git-gui.sh:2105
+msgid "Untracked, not staged"
+msgstr "Неследен"
+
+#: git-gui.sh:2110
+msgid "Missing"
+msgstr "Липсващ"
+
+#: git-gui.sh:2111
+msgid "Staged for removal"
+msgstr "В индекса за изтриване"
+
+#: git-gui.sh:2112
+msgid "Staged for removal, still present"
+msgstr "В индекса за изтриване, но още го има"
+
+#: git-gui.sh:2114 git-gui.sh:2115 git-gui.sh:2116 git-gui.sh:2117
+#: git-gui.sh:2118 git-gui.sh:2119
+msgid "Requires merge resolution"
+msgstr "Изисква коригиране при сливане"
+
+#: git-gui.sh:2154
+msgid "Starting gitk... please wait..."
+msgstr "Стартиране на gitk…, изчакайте…"
+
+#: git-gui.sh:2166
+msgid "Couldn't find gitk in PATH"
+msgstr "В пътищата, определени от променливата PATH, липсва изпълним gitk"
+
+#: git-gui.sh:2225
+msgid "Couldn't find git gui in PATH"
+msgstr "В пътищата, определени от променливата PATH, липсва изпълним git gui"
+
+#: git-gui.sh:2644 lib/choose_repository.tcl:40
+msgid "Repository"
+msgstr "Хранилище"
+
+#: git-gui.sh:2645
+msgid "Edit"
+msgstr "Редактиране"
+
+#: git-gui.sh:2647 lib/choose_rev.tcl:567
+msgid "Branch"
+msgstr "Клон"
+
+#: git-gui.sh:2650 lib/choose_rev.tcl:554
+msgid "Commit@@noun"
+msgstr "Подаване"
+
+#: git-gui.sh:2653 lib/merge.tcl:123 lib/merge.tcl:152 lib/merge.tcl:170
+msgid "Merge"
+msgstr "Сливане"
+
+#: git-gui.sh:2654 lib/choose_rev.tcl:563
+msgid "Remote"
+msgstr "Отдалечено хранилище"
+
+#: git-gui.sh:2657
+msgid "Tools"
+msgstr "Команди"
+
+#: git-gui.sh:2666
+msgid "Explore Working Copy"
+msgstr "Разглеждане на работното копие"
+
+#: git-gui.sh:2672
+msgid "Git Bash"
+msgstr "Bash на Git"
+
+#: git-gui.sh:2682
+msgid "Browse Current Branch's Files"
+msgstr "Разглеждане на файловете в текущия клон"
+
+#: git-gui.sh:2686
+msgid "Browse Branch Files..."
+msgstr "Разглеждане на текущия клон"
+
+#: git-gui.sh:2691
+msgid "Visualize Current Branch's History"
+msgstr "Визуализация на историята на текущия клон"
+
+#: git-gui.sh:2695
+msgid "Visualize All Branch History"
+msgstr "Визуализация на историята на всички клонове"
+
+#: git-gui.sh:2702
+#, tcl-format
+msgid "Browse %s's Files"
+msgstr "Разглеждане на файловете в %s"
+
+#: git-gui.sh:2704
+#, tcl-format
+msgid "Visualize %s's History"
+msgstr "Визуализация на историята на %s"
+
+#: git-gui.sh:2709 lib/database.tcl:40 lib/database.tcl:66
+msgid "Database Statistics"
+msgstr "Статистика на базата от данни"
+
+#: git-gui.sh:2712 lib/database.tcl:33
+msgid "Compress Database"
+msgstr "Компресиране на базата от данни"
+
+#: git-gui.sh:2715
+msgid "Verify Database"
+msgstr "Проверка на базата от данни"
+
+#: git-gui.sh:2722 git-gui.sh:2726 git-gui.sh:2730 lib/shortcut.tcl:8
+#: lib/shortcut.tcl:40 lib/shortcut.tcl:72
+msgid "Create Desktop Icon"
+msgstr "Добавяне на икона на работния плот"
+
+#: git-gui.sh:2738 lib/choose_repository.tcl:192 lib/choose_repository.tcl:200
+msgid "Quit"
+msgstr "Спиране на програмата"
+
+#: git-gui.sh:2746
+msgid "Undo"
+msgstr "Отмяна"
+
+#: git-gui.sh:2749
+msgid "Redo"
+msgstr "Повторение"
+
+#: git-gui.sh:2753 git-gui.sh:3348
+msgid "Cut"
+msgstr "Отрязване"
+
+#: git-gui.sh:2756 git-gui.sh:3351 git-gui.sh:3425 git-gui.sh:3510
+#: lib/console.tcl:69
+msgid "Copy"
+msgstr "Копиране"
+
+#: git-gui.sh:2759 git-gui.sh:3354
+msgid "Paste"
+msgstr "Поставяне"
+
+#: git-gui.sh:2762 git-gui.sh:3357 lib/remote_branch_delete.tcl:39
+#: lib/branch_delete.tcl:28
+msgid "Delete"
+msgstr "Изтриване"
+
+#: git-gui.sh:2766 git-gui.sh:3361 git-gui.sh:3514 lib/console.tcl:71
+msgid "Select All"
+msgstr "Избиране на всичко"
+
+#: git-gui.sh:2775
+msgid "Create..."
+msgstr "Създаване…"
+
+#: git-gui.sh:2781
+msgid "Checkout..."
+msgstr "Изтегляне…"
+
+#: git-gui.sh:2787
+msgid "Rename..."
+msgstr "Преименуване…"
+
+#: git-gui.sh:2792
+msgid "Delete..."
+msgstr "Изтриване…"
+
+#: git-gui.sh:2797
+msgid "Reset..."
+msgstr "Отмяна на промените…"
+
+#: git-gui.sh:2807
+msgid "Done"
+msgstr "Готово"
+
+#: git-gui.sh:2809
+msgid "Commit@@verb"
+msgstr "Подаване"
+
+#: git-gui.sh:2818 git-gui.sh:3289
+msgid "New Commit"
+msgstr "Подаване"
+
+#: git-gui.sh:2826 git-gui.sh:3296
+msgid "Amend Last Commit"
+msgstr "Поправяне на последното подаване"
+
+#: git-gui.sh:2836 git-gui.sh:3250 lib/remote_branch_delete.tcl:101
+msgid "Rescan"
+msgstr "Обновяване"
+
+#: git-gui.sh:2842
+msgid "Stage To Commit"
+msgstr "Към индекса за подаване"
+
+#: git-gui.sh:2848
+msgid "Stage Changed Files To Commit"
+msgstr "Всички променени файлове към индекса за подаване"
+
+#: git-gui.sh:2854
+msgid "Unstage From Commit"
+msgstr "Изваждане от индекса за подаване"
+
+#: git-gui.sh:2860 lib/index.tcl:442
+msgid "Revert Changes"
+msgstr "Връщане на оригинала"
+
+#: git-gui.sh:2868 git-gui.sh:3561 git-gui.sh:3592
+msgid "Show Less Context"
+msgstr "По-малко контекст"
+
+#: git-gui.sh:2872 git-gui.sh:3565 git-gui.sh:3596
+msgid "Show More Context"
+msgstr "Повече контекст"
+
+#: git-gui.sh:2879 git-gui.sh:3263 git-gui.sh:3372
+msgid "Sign Off"
+msgstr "Подписване"
+
+#: git-gui.sh:2895
+msgid "Local Merge..."
+msgstr "Локално сливане…"
+
+#: git-gui.sh:2900
+msgid "Abort Merge..."
+msgstr "Преустановяване на сливане…"
+
+#: git-gui.sh:2912 git-gui.sh:2940
+msgid "Add..."
+msgstr "Добавяне…"
+
+#: git-gui.sh:2916
+msgid "Push..."
+msgstr "Избутване…"
+
+#: git-gui.sh:2920
+msgid "Delete Branch..."
+msgstr "Изтриване на клон…"
+
+#: git-gui.sh:2930 git-gui.sh:3543
+msgid "Options..."
+msgstr "Опции…"
+
+#: git-gui.sh:2941
+msgid "Remove..."
+msgstr "Премахване…"
+
+#: git-gui.sh:2950 lib/choose_repository.tcl:54
+msgid "Help"
+msgstr "Помощ"
+
+#: git-gui.sh:2954 git-gui.sh:2958 lib/choose_repository.tcl:48
+#: lib/choose_repository.tcl:57 lib/about.tcl:14
+#, tcl-format
+msgid "About %s"
+msgstr "Относно %s"
+
+#: git-gui.sh:2982
+msgid "Online Documentation"
+msgstr "Документация в Интернет"
+
+#: git-gui.sh:2985 lib/choose_repository.tcl:51 lib/choose_repository.tcl:60
+msgid "Show SSH Key"
+msgstr "Показване на ключа за SSH"
+
+#: git-gui.sh:3004 git-gui.sh:3136
+msgid "Usage"
+msgstr "Употреба"
+
+#: git-gui.sh:3085 lib/blame.tcl:573
+msgid "Error"
+msgstr "Грешка"
+
+#: git-gui.sh:3116
+#, tcl-format
+msgid "fatal: cannot stat path %s: No such file or directory"
+msgstr ""
+"фатално: пътят %s не може да бъде открит: такъв файл или директория няма"
+
+#: git-gui.sh:3149
+msgid "Current Branch:"
+msgstr "Текущ клон:"
+
+#: git-gui.sh:3175
+msgid "Staged Changes (Will Commit)"
+msgstr "Промени в индекса (за подаване)"
+
+#: git-gui.sh:3195
+msgid "Unstaged Changes"
+msgstr "Промени извън индекса"
+
+#: git-gui.sh:3256
+msgid "Stage Changed"
+msgstr "Индексът е променен"
+
+#: git-gui.sh:3275 lib/transport.tcl:137 lib/transport.tcl:229
+msgid "Push"
+msgstr "Изтласкване"
+
+#: git-gui.sh:3310
+msgid "Initial Commit Message:"
+msgstr "Първоначално съобщение при подаване:"
+
+#: git-gui.sh:3311
+msgid "Amended Commit Message:"
+msgstr "Поправено съобщение при подаване:"
+
+#: git-gui.sh:3312
+msgid "Amended Initial Commit Message:"
+msgstr "Поправено първоначално съобщение при подаване:"
+
+#: git-gui.sh:3313
+msgid "Amended Merge Commit Message:"
+msgstr "Поправено съобщение при подаване със сливане:"
+
+#: git-gui.sh:3314
+msgid "Merge Commit Message:"
+msgstr "Съобщение при подаване със сливане:"
+
+#: git-gui.sh:3315
+msgid "Commit Message:"
+msgstr "Съобщение при подаване:"
+
+#: git-gui.sh:3364 git-gui.sh:3518 lib/console.tcl:73
+msgid "Copy All"
+msgstr "Копиране на всичко"
+
+#: git-gui.sh:3388 lib/blame.tcl:105
+msgid "File:"
+msgstr "Файл:"
+
+#: git-gui.sh:3506
+msgid "Refresh"
+msgstr "Обновяване"
+
+#: git-gui.sh:3527
+msgid "Decrease Font Size"
+msgstr "По-едър шрифт"
+
+#: git-gui.sh:3531
+msgid "Increase Font Size"
+msgstr "По-дребен шрифт"
+
+#: git-gui.sh:3539 lib/blame.tcl:294
+msgid "Encoding"
+msgstr "Кодиране"
+
+#: git-gui.sh:3550
+msgid "Apply/Reverse Hunk"
+msgstr "Прилагане/връщане на парче"
+
+#: git-gui.sh:3555
+msgid "Apply/Reverse Line"
+msgstr "Прилагане/връщане на ред"
+
+#: git-gui.sh:3574
+msgid "Run Merge Tool"
+msgstr "Изпълнение на програмата за сливане"
+
+#: git-gui.sh:3579
+msgid "Use Remote Version"
+msgstr "Версия от отдалеченото хранилище"
+
+#: git-gui.sh:3583
+msgid "Use Local Version"
+msgstr "Локална версия"
+
+#: git-gui.sh:3587
+msgid "Revert To Base"
+msgstr "Връщане към родителската версия"
+
+#: git-gui.sh:3605
+msgid "Visualize These Changes In The Submodule"
+msgstr "Визуализиране на промените в подмодула"
+
+#: git-gui.sh:3609
+msgid "Visualize Current Branch History In The Submodule"
+msgstr "Визуализация на историята на текущия клон в историята за подмодула"
+
+#: git-gui.sh:3613
+msgid "Visualize All Branch History In The Submodule"
+msgstr "Визуализация на историята на всички клони в историята за подмодула"
+
+#: git-gui.sh:3618
+msgid "Start git gui In The Submodule"
+msgstr "Стартиране на „git gui“ за подмодула"
+
+#: git-gui.sh:3653
+msgid "Unstage Hunk From Commit"
+msgstr "Изваждане на парчето от подаването"
+
+#: git-gui.sh:3655
+msgid "Unstage Lines From Commit"
+msgstr "Изваждане на редовете от подаването"
+
+#: git-gui.sh:3657
+msgid "Unstage Line From Commit"
+msgstr "Изваждане на реда от подаването"
+
+#: git-gui.sh:3660
+msgid "Stage Hunk For Commit"
+msgstr "Добавяне на парчето за подаване"
+
+#: git-gui.sh:3662
+msgid "Stage Lines For Commit"
+msgstr "Добавяне на редовете за подаване"
+
+#: git-gui.sh:3664
+msgid "Stage Line For Commit"
+msgstr "Добавяне на реда за подаване"
+
+#: git-gui.sh:3689
+msgid "Initializing..."
+msgstr "Инициализиране…"
+
+#: git-gui.sh:3832
+#, 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:3861
+msgid ""
+"\n"
+"This is due to a known issue with the\n"
+"Tcl binary distributed by Cygwin."
+msgstr ""
+"\n"
+"Това е познат проблем и се дължи на\n"
+"версията на Tcl включена в Cygwin."
+
+#: git-gui.sh:3866
+#, 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 в личния си файл ~/.gitconfig.\n"
+
+#: lib/spellcheck.tcl:57
+msgid "Unsupported spell checker"
+msgstr "Тази програма за проверка на правописа не се поддържа"
+
+#: lib/spellcheck.tcl:65
+msgid "Spell checking is unavailable"
+msgstr "Липсва програма за проверка на правописа"
+
+#: lib/spellcheck.tcl:68
+msgid "Invalid spell checking configuration"
+msgstr "Неправилни настройки на проверката на правописа"
+
+#: lib/spellcheck.tcl:70
+#, tcl-format
+msgid "Reverting dictionary to %s."
+msgstr "Ползване на речник за език „%s“."
+
+#: lib/spellcheck.tcl:73
+msgid "Spell checker silently failed on startup"
+msgstr "Програмата за правопис даже не стартира успешно."
+
+#: lib/spellcheck.tcl:80
+msgid "Unrecognized spell checker"
+msgstr "Непозната програма за проверка на правописа"
+
+#: lib/spellcheck.tcl:186
+msgid "No Suggestions"
+msgstr "Няма предложения"
+
+#: lib/spellcheck.tcl:388
+msgid "Unexpected EOF from spell checker"
+msgstr "Неочакван край на файл от програмата за проверка на правописа"
+
+#: lib/spellcheck.tcl:392
+msgid "Spell Checker Failed"
+msgstr "Грешка в програмата за проверка на правописа"
+
+#: lib/remote_add.tcl:20
+msgid "Add Remote"
+msgstr "Добавяне на отдалечено хранилище"
+
+#: lib/remote_add.tcl:25
+msgid "Add New Remote"
+msgstr "Добавяне на отдалечено хранилище"
+
+#: lib/remote_add.tcl:30 lib/tools_dlg.tcl:37
+msgid "Add"
+msgstr "Добавяне"
+
+#: lib/remote_add.tcl:34 lib/browser.tcl:292 lib/branch_checkout.tcl:30
+#: lib/transport.tcl:141 lib/branch_rename.tcl:32 lib/choose_font.tcl:45
+#: lib/option.tcl:127 lib/tools_dlg.tcl:41 lib/tools_dlg.tcl:202
+#: lib/tools_dlg.tcl:345 lib/remote_branch_delete.tcl:43
+#: lib/checkout_op.tcl:579 lib/branch_create.tcl:37 lib/branch_delete.tcl:34
+#: lib/merge.tcl:174
+msgid "Cancel"
+msgstr "Отказване"
+
+#: lib/remote_add.tcl:39
+msgid "Remote Details"
+msgstr "Данни за отдалеченото хранилище"
+
+#: lib/remote_add.tcl:41 lib/tools_dlg.tcl:51 lib/branch_create.tcl:44
+msgid "Name:"
+msgstr "Име:"
+
+#: lib/remote_add.tcl:50
+msgid "Location:"
+msgstr "Местоположение:"
+
+#: lib/remote_add.tcl:60
+msgid "Further Action"
+msgstr "Следващо действие"
+
+#: lib/remote_add.tcl:63
+msgid "Fetch Immediately"
+msgstr "Незабавно доставяне"
+
+#: lib/remote_add.tcl:69
+msgid "Initialize Remote Repository and Push"
+msgstr "Инициализиране на отдалеченото хранилище и изтласкване на промените"
+
+#: lib/remote_add.tcl:75
+msgid "Do Nothing Else Now"
+msgstr "Да не се прави нищо"
+
+#: lib/remote_add.tcl:100
+msgid "Please supply a remote name."
+msgstr "Задайте име за отдалеченото хранилище."
+
+#: lib/remote_add.tcl:113
+#, tcl-format
+msgid "'%s' is not an acceptable remote name."
+msgstr "Отдалечено хранилище не може да се казва „%s“."
+
+#: lib/remote_add.tcl:124
+#, tcl-format
+msgid "Failed to add remote '%s' of location '%s'."
+msgstr "Неуспешно добавяне на отдалеченото хранилище „%s“ от адрес „%s“."
+
+#: lib/remote_add.tcl:132 lib/transport.tcl:6
+#, tcl-format
+msgid "fetch %s"
+msgstr "доставяне на „%s“"
+
+#: lib/remote_add.tcl:133
+#, tcl-format
+msgid "Fetching the %s"
+msgstr "Доставяне на „%s“"
+
+#: lib/remote_add.tcl:156
+#, tcl-format
+msgid "Do not know how to initialize repository at location '%s'."
+msgstr "Хранилището с местоположение „%s“ не може да бъде инициализирано."
+
+#: lib/remote_add.tcl:162 lib/transport.tcl:54 lib/transport.tcl:92
+#: lib/transport.tcl:110
+#, tcl-format
+msgid "push %s"
+msgstr "изтласкване на „%s“"
+
+#: lib/remote_add.tcl:163
+#, tcl-format
+msgid "Setting up the %s (at %s)"
+msgstr "Добавяне на хранилище „%s“ (с адрес „%s“)"
+
+#: lib/browser.tcl:17
+msgid "Starting..."
+msgstr "Стартиране…"
+
+#: lib/browser.tcl:27
+msgid "File Browser"
+msgstr "Файлов браузър"
+
+#: lib/browser.tcl:132 lib/browser.tcl:149
+#, tcl-format
+msgid "Loading %s..."
+msgstr "Зареждане на „%s“…"
+
+#: lib/browser.tcl:193
+msgid "[Up To Parent]"
+msgstr "[Към родителя]"
+
+#: lib/browser.tcl:275 lib/browser.tcl:282
+msgid "Browse Branch Files"
+msgstr "Разглеждане на файловете в клона"
+
+#: lib/browser.tcl:288 lib/choose_repository.tcl:406
+#: lib/choose_repository.tcl:493 lib/choose_repository.tcl:502
+#: lib/choose_repository.tcl:1029
+msgid "Browse"
+msgstr "Разглеждане"
+
+#: lib/browser.tcl:297 lib/branch_checkout.tcl:35 lib/tools_dlg.tcl:321
+msgid "Revision"
+msgstr "Версия"
+
+#: lib/tools.tcl:75
+#, tcl-format
+msgid "Running %s requires a selected file."
+msgstr "За изпълнението на „%s“ трябва да изберете файл."
+
+#: lib/tools.tcl:91
+#, tcl-format
+msgid "Are you sure you want to run %1$s on file \"%2$s\"?"
+msgstr "Сигурни ли сте, че искате да стартирате „%1$s“ върху „%2$s“?"
+
+#: lib/tools.tcl:95
+#, tcl-format
+msgid "Are you sure you want to run %s?"
+msgstr "Сигурни ли сте, че искате да стартирате „%s“?"
+
+#: lib/tools.tcl:116
+#, tcl-format
+msgid "Tool: %s"
+msgstr "Команда: %s"
+
+#: lib/tools.tcl:117
+#, tcl-format
+msgid "Running: %s"
+msgstr "Изпълнение: %s"
+
+#: lib/tools.tcl:155
+#, tcl-format
+msgid "Tool completed successfully: %s"
+msgstr "Командата завърши успешно: %s"
+
+#: lib/tools.tcl:157
+#, tcl-format
+msgid "Tool failed: %s"
+msgstr "Командата върна грешка: %s"
+
+#: lib/branch_checkout.tcl:16 lib/branch_checkout.tcl:21
+msgid "Checkout Branch"
+msgstr "Клон за изтегляне"
+
+#: lib/branch_checkout.tcl:26
+msgid "Checkout"
+msgstr "Изтегляне"
+
+#: lib/branch_checkout.tcl:39 lib/option.tcl:309 lib/branch_create.tcl:69
+msgid "Options"
+msgstr "Опции"
+
+#: lib/branch_checkout.tcl:42 lib/branch_create.tcl:92
+msgid "Fetch Tracking Branch"
+msgstr "Изтегляне на промените от следения клон"
+
+#: lib/branch_checkout.tcl:47
+msgid "Detach From Local Branch"
+msgstr "Изтриване от локалния клон"
+
+#: 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
+msgid "fetch all remotes"
+msgstr "доставяне на всички хранилища"
+
+#: lib/transport.tcl:26
+msgid "Fetching new changes from all remotes"
+msgstr "Доставяне на промените от всички отдалечени хранилища"
+
+#: lib/transport.tcl:40
+msgid "remote prune all remotes"
+msgstr "окастряне на всички клони следящи изтрити клони"
+
+#: lib/transport.tcl:41
+msgid "Pruning tracking branches deleted from all remotes"
+msgstr ""
+"Окастряне на следящите клони на изтритите клони от всички отдалечени "
+"хранилища"
+
+#: lib/transport.tcl:55
+#, tcl-format
+msgid "Pushing changes to %s"
+msgstr "Изтласкване на промените към %s"
+
+#: lib/transport.tcl:93
+#, tcl-format
+msgid "Mirroring to %s"
+msgstr "Изтласкване на всичко към „%s“"
+
+#: lib/transport.tcl:111
+#, tcl-format
+msgid "Pushing %s %s to %s"
+msgstr "Изтласкване на %s %s към %s"
+
+#: lib/transport.tcl:132
+msgid "Push Branches"
+msgstr "Клони за изтласкване"
+
+#: lib/transport.tcl:147
+msgid "Source Branches"
+msgstr "Клони-източници"
+
+#: lib/transport.tcl:162
+msgid "Destination Repository"
+msgstr "Целево хранилище"
+
+#: lib/transport.tcl:165 lib/remote_branch_delete.tcl:51
+msgid "Remote:"
+msgstr "Отдалечено хранилище:"
+
+#: lib/transport.tcl:187 lib/remote_branch_delete.tcl:72
+msgid "Arbitrary Location:"
+msgstr "Произволно местоположение:"
+
+#: lib/transport.tcl:205
+msgid "Transfer Options"
+msgstr "Настройки при пренасянето"
+
+#: lib/transport.tcl:207
+msgid "Force overwrite existing branch (may discard changes)"
+msgstr ""
+"Изрично презаписване на съществуващ клон (някои промени може да бъдат "
+"загубени)"
+
+#: lib/transport.tcl:211
+msgid "Use thin pack (for slow network connections)"
+msgstr "Максимална компресия (за бавни мрежови връзки)"
+
+#: lib/transport.tcl:215
+msgid "Include tags"
+msgstr "Включване на етикетите"
+
+#: lib/status_bar.tcl:87
+#, tcl-format
+msgid "%s ... %*i of %*i %s (%3i%%)"
+msgstr "%s… %*i от общо %*i %s (%3i%%)"
+
+#: lib/remote.tcl:200
+msgid "Push to"
+msgstr "Изтласкване към"
+
+#: lib/remote.tcl:218
+msgid "Remove Remote"
+msgstr "Премахване на отдалечено хранилище"
+
+#: lib/remote.tcl:223
+msgid "Prune from"
+msgstr "Окастряне от"
+
+#: lib/remote.tcl:228
+msgid "Fetch from"
+msgstr "Доставяне от"
+
+#: lib/sshkey.tcl:31
+msgid "No keys found."
+msgstr "Не са открити ключове."
+
+#: lib/sshkey.tcl:34
+#, tcl-format
+msgid "Found a public key in: %s"
+msgstr "Открит е публичен ключ в „%s“"
+
+#: lib/sshkey.tcl:40
+msgid "Generate Key"
+msgstr "Генериране на ключ"
+
+#: lib/sshkey.tcl:55 lib/checkout_op.tcl:146 lib/console.tcl:81
+#: lib/database.tcl:30
+msgid "Close"
+msgstr "Затваряне"
+
+#: lib/sshkey.tcl:58
+msgid "Copy To Clipboard"
+msgstr "Копиране към системния буфер"
+
+#: lib/sshkey.tcl:72
+msgid "Your OpenSSH Public Key"
+msgstr "Публичният ви ключ за OpenSSH"
+
+#: lib/sshkey.tcl:80
+msgid "Generating..."
+msgstr "Генериране…"
+
+#: lib/sshkey.tcl:86
+#, tcl-format
+msgid ""
+"Could not start ssh-keygen:\n"
+"\n"
+"%s"
+msgstr ""
+"Програмата „ssh-keygen“ не може да бъде стартирана:\n"
+"\n"
+"%s"
+
+#: lib/sshkey.tcl:113
+msgid "Generation failed."
+msgstr "Неуспешно генериране."
+
+#: lib/sshkey.tcl:120
+msgid "Generation succeeded, but no keys found."
+msgstr "Генерирането завърши успешно, а не са намерени ключове."
+
+#: lib/sshkey.tcl:123
+#, tcl-format
+msgid "Your key is in: %s"
+msgstr "Ключът ви е в „%s“"
+
+#: lib/branch_rename.tcl:15 lib/branch_rename.tcl:23
+msgid "Rename Branch"
+msgstr "Преименуване на клон"
+
+#: lib/branch_rename.tcl:28
+msgid "Rename"
+msgstr "Преименуване"
+
+#: lib/branch_rename.tcl:38
+msgid "Branch:"
+msgstr "Клон:"
+
+#: lib/branch_rename.tcl:46
+msgid "New Name:"
+msgstr "Ново име:"
+
+#: lib/branch_rename.tcl:81
+msgid "Please select a branch to rename."
+msgstr "Изберете клон за преименуване."
+
+#: lib/branch_rename.tcl:92 lib/branch_create.tcl:154
+msgid "Please supply a branch name."
+msgstr "Дайте име на клона."
+
+#: lib/branch_rename.tcl:102 lib/checkout_op.tcl:202
+#, tcl-format
+msgid "Branch '%s' already exists."
+msgstr "Клонът „%s“ вече съществува."
+
+#: lib/branch_rename.tcl:112 lib/branch_create.tcl:165
+#, tcl-format
+msgid "'%s' is not an acceptable branch name."
+msgstr "„%s“ не може да се използва за име на клон."
+
+#: lib/branch_rename.tcl:123
+#, tcl-format
+msgid "Failed to rename '%s'."
+msgstr "Неуспешно преименуване на „%s“."
+
+#: lib/choose_font.tcl:41
+msgid "Select"
+msgstr "Избор"
+
+#: lib/choose_font.tcl:55
+msgid "Font Family"
+msgstr "Шрифт"
+
+#: lib/choose_font.tcl:76
+msgid "Font Size"
+msgstr "Размер"
+
+#: lib/choose_font.tcl:93
+msgid "Font Example"
+msgstr "Мостра"
+
+#: lib/choose_font.tcl:105
+msgid ""
+"This is example text.\n"
+"If you like this text, it can be your font."
+msgstr ""
+"Това е примерен текст.\n"
+"Ако ви харесва как изглежда, изберете този шрифт."
+
+#: lib/option.tcl:11
+#, tcl-format
+msgid "Invalid global encoding '%s'"
+msgstr "Неправилно глобално кодиране „%s“"
+
+#: lib/option.tcl:19
+#, tcl-format
+msgid "Invalid repo encoding '%s'"
+msgstr "Неправилно кодиране „%s“ на хранилището"
+
+#: lib/option.tcl:119
+msgid "Restore Defaults"
+msgstr "Стандартни настройки"
+
+#: lib/option.tcl:123
+msgid "Save"
+msgstr "Запазване"
+
+#: lib/option.tcl:133
+#, tcl-format
+msgid "%s Repository"
+msgstr "Хранилище „%s“"
+
+#: lib/option.tcl:134
+msgid "Global (All Repositories)"
+msgstr "Глобално (за всички хранилища)"
+
+#: lib/option.tcl:140
+msgid "User Name"
+msgstr "Потребителско име"
+
+#: lib/option.tcl:141
+msgid "Email Address"
+msgstr "Адрес на е-поща"
+
+#: lib/option.tcl:143
+msgid "Summarize Merge Commits"
+msgstr "Обобщаване на подаванията при сливане"
+
+#: lib/option.tcl:144
+msgid "Merge Verbosity"
+msgstr "Подробности при сливанията"
+
+#: lib/option.tcl:145
+msgid "Show Diffstat After Merge"
+msgstr "Извеждане на статистика след сливанията"
+
+#: lib/option.tcl:146
+msgid "Use Merge Tool"
+msgstr "Използване на програма за сливане"
+
+#: lib/option.tcl:148
+msgid "Trust File Modification Timestamps"
+msgstr "Доверие във времето на промяна на файловете"
+
+#: lib/option.tcl:149
+msgid "Prune Tracking Branches During Fetch"
+msgstr "Окастряне на следящите клонове при доставяне"
+
+#: lib/option.tcl:150
+msgid "Match Tracking Branches"
+msgstr "Напасване на следящите клонове"
+
+#: lib/option.tcl:151
+msgid "Use Textconv For Diffs and Blames"
+msgstr "Използване на „textconv“ за разликите и анотациите"
+
+#: lib/option.tcl:152
+msgid "Blame Copy Only On Changed Files"
+msgstr "Анотиране на копието само по променените файлове"
+
+#: lib/option.tcl:153
+msgid "Maximum Length of Recent Repositories List"
+msgstr "Максимален брой скоро ползвани хранилища"
+
+#: lib/option.tcl:154
+msgid "Minimum Letters To Blame Copy On"
+msgstr "Минимален брой знаци за анотиране на копието"
+
+#: lib/option.tcl:155
+msgid "Blame History Context Radius (days)"
+msgstr "Исторически обхват за анотиране в дни"
+
+#: lib/option.tcl:156
+msgid "Number of Diff Context Lines"
+msgstr "Брой редове за контекста при извеждане на разликите"
+
+#: lib/option.tcl:157
+msgid "Additional Diff Parameters"
+msgstr "Допълнителни параметри за разликите"
+
+#: lib/option.tcl:158
+msgid "Commit Message Text Width"
+msgstr "Широчина на текста на съобщението при подаване"
+
+#: lib/option.tcl:159
+msgid "New Branch Name Template"
+msgstr "Шаблон за името на новите клони"
+
+#: lib/option.tcl:160
+msgid "Default File Contents Encoding"
+msgstr "Стандартно кодиране на файловете"
+
+#: lib/option.tcl:161
+msgid "Warn before committing to a detached head"
+msgstr "Предупреждение при подаване към несвързан връх"
+
+#: lib/option.tcl:162
+msgid "Staging of untracked files"
+msgstr "Добавяне на неследените файлове към индекса"
+
+#: lib/option.tcl:163
+msgid "Show untracked files"
+msgstr "Показване на неследените файлове"
+
+#: lib/option.tcl:209
+msgid "Change"
+msgstr "Смяна"
+
+#: lib/option.tcl:253
+msgid "Spelling Dictionary:"
+msgstr "Правописен речник:"
+
+#: lib/option.tcl:283
+msgid "Change Font"
+msgstr "Смяна на шрифта"
+
+#: lib/option.tcl:287
+#, tcl-format
+msgid "Choose %s"
+msgstr "Избор на „%s“"
+
+#: lib/option.tcl:293
+msgid "pt."
+msgstr "тчк."
+
+#: lib/option.tcl:307
+msgid "Preferences"
+msgstr "Настройки"
+
+#: lib/option.tcl:344
+msgid "Failed to completely save options:"
+msgstr "Неуспешно запазване на настройките:"
+
+#: lib/mergetool.tcl:8
+msgid "Force resolution to the base version?"
+msgstr "Да се използва базовата версия"
+
+#: lib/mergetool.tcl:9
+msgid "Force resolution to this branch?"
+msgstr "Да се използва версията от този клон"
+
+#: lib/mergetool.tcl:10
+msgid "Force resolution to the other branch?"
+msgstr "Да се използва версията от другия клон"
+
+#: lib/mergetool.tcl:14
+#, tcl-format
+msgid ""
+"Note that the diff shows only conflicting changes.\n"
+"\n"
+"%s will be overwritten.\n"
+"\n"
+"This operation can be undone only by restarting the merge."
+msgstr ""
+"Разликата показва само разликите с конфликт.\n"
+"\n"
+"Файлът „%s“ ще бъде презаписан.\n"
+"\n"
+"Тази операция може да бъде отменена само чрез започване на сливането наново."
+
+#: lib/mergetool.tcl:45
+#, tcl-format
+msgid "File %s seems to have unresolved conflicts, still stage?"
+msgstr ""
+"Изглежда, че във файла „%s“ все още има некоригирани конфликти. Да се добави "
+"ли файлът към индекса?"
+
+#: lib/mergetool.tcl:60
+#, tcl-format
+msgid "Adding resolution for %s"
+msgstr "Добавяне на корекция на конфликтите в „%s“"
+
+#: lib/mergetool.tcl:141
+msgid "Cannot resolve deletion or link conflicts using a tool"
+msgstr ""
+"Конфликтите при символни връзки или изтриване не могат да бъдат коригирани с "
+"външна програма."
+
+#: lib/mergetool.tcl:146
+msgid "Conflict file does not exist"
+msgstr "Файлът, в който е конфликтът, не съществува"
+
+#: lib/mergetool.tcl:246
+#, tcl-format
+msgid "Not a GUI merge tool: '%s'"
+msgstr "Това не е графична програма за сливане: „%s“"
+
+#: lib/mergetool.tcl:275
+#, tcl-format
+msgid "Unsupported merge tool '%s'"
+msgstr "Неподдържана програма за сливане: „%s“"
+
+#: lib/mergetool.tcl:310
+msgid "Merge tool is already running, terminate it?"
+msgstr "Програмата за сливане вече е стартирана. Да бъде ли изключена?"
+
+#: lib/mergetool.tcl:330
+#, tcl-format
+msgid ""
+"Error retrieving versions:\n"
+"%s"
+msgstr ""
+"Грешка при изтеглянето на версии:\n"
+"%s"
+
+#: lib/mergetool.tcl:350
+#, tcl-format
+msgid ""
+"Could not start the merge tool:\n"
+"\n"
+"%s"
+msgstr ""
+"Програмата за сливане не може да бъде стартирана:\n"
+"\n"
+"%s"
+
+#: lib/mergetool.tcl:354
+msgid "Running merge tool..."
+msgstr "Стартиране на програмата за сливане…"
+
+#: lib/mergetool.tcl:382 lib/mergetool.tcl:390
+msgid "Merge tool failed."
+msgstr "Грешка в програмата за сливане."
+
+#: lib/tools_dlg.tcl:22
+msgid "Add Tool"
+msgstr "Добавяне на команда"
+
+#: lib/tools_dlg.tcl:28
+msgid "Add New Tool Command"
+msgstr "Добавяне на команда"
+
+#: lib/tools_dlg.tcl:34
+msgid "Add globally"
+msgstr "Глобално добавяне"
+
+#: lib/tools_dlg.tcl:46
+msgid "Tool Details"
+msgstr "Подробности за командата"
+
+#: lib/tools_dlg.tcl:49
+msgid "Use '/' separators to create a submenu tree:"
+msgstr "За създаване на подменюта използвайте за разделител знака „/“:"
+
+#: lib/tools_dlg.tcl:60
+msgid "Command:"
+msgstr "Команда:"
+
+#: lib/tools_dlg.tcl:71
+msgid "Show a dialog before running"
+msgstr "Преди изпълнение да се извежда диалогов прозорец"
+
+#: lib/tools_dlg.tcl:77
+msgid "Ask the user to select a revision (sets $REVISION)"
+msgstr "Потребителят да укаже версия (задаване на променливата $REVISION)"
+
+#: lib/tools_dlg.tcl:82
+msgid "Ask the user for additional arguments (sets $ARGS)"
+msgstr ""
+"Потребителят да укаже допълнителни аргументи (задаване на променливата $ARGS)"
+
+#: lib/tools_dlg.tcl:89
+msgid "Don't show the command output window"
+msgstr "Без показване на прозорец с изхода от командата"
+
+#: lib/tools_dlg.tcl:94
+msgid "Run only if a diff is selected ($FILENAME not empty)"
+msgstr ""
+"Стартиране само след избор на разлика (променливата $FILENAME не е празна)"
+
+#: lib/tools_dlg.tcl:118
+msgid "Please supply a name for the tool."
+msgstr "Задайте име за командата."
+
+#: lib/tools_dlg.tcl:126
+#, tcl-format
+msgid "Tool '%s' already exists."
+msgstr "Командата „%s“ вече съществува."
+
+#: lib/tools_dlg.tcl:148
+#, tcl-format
+msgid ""
+"Could not add tool:\n"
+"%s"
+msgstr ""
+"Командата не може да бъде добавена:\n"
+"%s"
+
+#: lib/tools_dlg.tcl:187
+msgid "Remove Tool"
+msgstr "Премахване на команда"
+
+#: lib/tools_dlg.tcl:193
+msgid "Remove Tool Commands"
+msgstr "Премахване на команди"
+
+#: lib/tools_dlg.tcl:198
+msgid "Remove"
+msgstr "Премахване"
+
+#: lib/tools_dlg.tcl:231
+msgid "(Blue denotes repository-local tools)"
+msgstr "(команди към локалното хранилище са обозначени в синьо)"
+
+#: lib/tools_dlg.tcl:292
+#, tcl-format
+msgid "Run Command: %s"
+msgstr "Изпълнение на командата „%s“"
+
+#: lib/tools_dlg.tcl:306
+msgid "Arguments"
+msgstr "Аргументи"
+
+#: lib/tools_dlg.tcl:336 lib/checkout_op.tcl:567 lib/merge.tcl:166
+msgid "Visualize"
+msgstr "Визуализация"
+
+#: lib/tools_dlg.tcl:341
+msgid "OK"
+msgstr "Добре"
+
+#: lib/search.tcl:48
+msgid "Find:"
+msgstr "Търсене:"
+
+#: lib/search.tcl:50
+msgid "Next"
+msgstr "Следваща поява"
+
+#: lib/search.tcl:51
+msgid "Prev"
+msgstr "Предишна поява"
+
+#: lib/search.tcl:52
+msgid "RegExp"
+msgstr "Рег. изр."
+
+#: lib/search.tcl:54
+msgid "Case"
+msgstr "Регистър"
+
+#: lib/shortcut.tcl:21 lib/shortcut.tcl:62
+msgid "Cannot write shortcut:"
+msgstr "Клавишната комбинация не може да бъде запазена:"
+
+#: lib/shortcut.tcl:137
+msgid "Cannot write icon:"
+msgstr "Иконата не може да бъде запазена:"
+
+#: lib/diff.tcl:64
+#, 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:104
+#, tcl-format
+msgid "Loading diff of %s..."
+msgstr "Зареждане на разликите в „%s“…"
+
+#: lib/diff.tcl:125
+msgid ""
+"LOCAL: deleted\n"
+"REMOTE:\n"
+msgstr ""
+"ЛОКАЛНО: изтрит\n"
+"ОТДАЛЕЧЕНО:\n"
+
+#: lib/diff.tcl:130
+msgid ""
+"REMOTE: deleted\n"
+"LOCAL:\n"
+msgstr ""
+"ОТДАЛЕЧЕНО: изтрит\n"
+"ЛОКАЛНО:\n"
+
+#: lib/diff.tcl:137
+msgid "LOCAL:\n"
+msgstr "ЛОКАЛНО:\n"
+
+#: lib/diff.tcl:140
+msgid "REMOTE:\n"
+msgstr "ОТДАЛЕЧЕНО:\n"
+
+#: lib/diff.tcl:202 lib/diff.tcl:337
+#, tcl-format
+msgid "Unable to display %s"
+msgstr "Не може да бъде показан файлът „%s“"
+
+#: lib/diff.tcl:203
+msgid "Error loading file:"
+msgstr "Грешка при зареждане на файл:"
+
+#: lib/diff.tcl:210
+msgid "Git Repository (subproject)"
+msgstr "Хранилище на Git (подпроект)"
+
+#: lib/diff.tcl:222
+msgid "* Binary file (not showing content)."
+msgstr "⁃ Двоичен файл (съдържанието не се показва)."
+
+#: lib/diff.tcl:227
+#, tcl-format
+msgid ""
+"* Untracked file is %d bytes.\n"
+"* Showing only first %d bytes.\n"
+msgstr ""
+"⁃ Неследеният файл е %d байта.\n"
+"⁃ Показват се само първите %d байта.\n"
+
+#: lib/diff.tcl:233
+#, tcl-format
+msgid ""
+"\n"
+"* Untracked file clipped here by %s.\n"
+"* To see the entire file, use an external editor.\n"
+msgstr ""
+"\n"
+"⁃ Неследеният файл е отрязан дотук %s.\n"
+"⁃ Използвайте външен редактор, за да видите целия файл.\n"
+
+#: lib/diff.tcl:338 lib/blame.tcl:1128
+msgid "Error loading diff:"
+msgstr "Грешка при зареждане на разлика:"
+
+#: lib/diff.tcl:557
+msgid "Failed to unstage selected hunk."
+msgstr "Избраното парче не може да бъде извадено от индекса."
+
+#: lib/diff.tcl:564
+msgid "Failed to stage selected hunk."
+msgstr "Избраното парче не може да бъде добавен към индекса."
+
+#: lib/diff.tcl:643
+msgid "Failed to unstage selected line."
+msgstr "Избраният ред не може да бъде изваден от индекса."
+
+#: lib/diff.tcl:651
+msgid "Failed to stage selected line."
+msgstr "Избраният ред не може да бъде добавен към индекса."
+
+#: lib/remote_branch_delete.tcl:29 lib/remote_branch_delete.tcl:34
+msgid "Delete Branch Remotely"
+msgstr "Изтриване на отдалечения клон"
+
+#: lib/remote_branch_delete.tcl:48
+msgid "From Repository"
+msgstr "От хранилище"
+
+#: lib/remote_branch_delete.tcl:88
+msgid "Branches"
+msgstr "Клонове"
+
+#: lib/remote_branch_delete.tcl:110
+msgid "Delete Only If"
+msgstr "Изтриване, само ако"
+
+#: lib/remote_branch_delete.tcl:112
+msgid "Merged Into:"
+msgstr "Слят в:"
+
+#: lib/remote_branch_delete.tcl:120 lib/branch_delete.tcl:53
+msgid "Always (Do not perform merge checks)"
+msgstr "Винаги (без проверка за сливане)"
+
+#: lib/remote_branch_delete.tcl:153
+msgid "A branch is required for 'Merged Into'."
+msgstr "За данните „Слят в“ е необходимо да зададете клон."
+
+#: lib/remote_branch_delete.tcl:185
+#, 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:190
+#, 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:208
+msgid "Please select one or more branches to delete."
+msgstr "Изберете поне един клон за изтриване."
+
+#: lib/remote_branch_delete.tcl:218 lib/branch_delete.tcl:115
+msgid ""
+"Recovering deleted branches is difficult.\n"
+"\n"
+"Delete the selected branches?"
+msgstr ""
+"Възстановяването на изтрити клони може да е трудно.\n"
+"\n"
+"Сигурни ли сте, че искате да триете?"
+
+#: lib/remote_branch_delete.tcl:227
+#, tcl-format
+msgid "Deleting branches from %s"
+msgstr "Изтриване на клони от „%s“"
+
+#: lib/remote_branch_delete.tcl:300
+msgid "No repository selected."
+msgstr "Не е избрано хранилище."
+
+#: lib/remote_branch_delete.tcl:305
+#, tcl-format
+msgid "Scanning %s..."
+msgstr "Претърсване на „%s“…"
+
+#: lib/choose_repository.tcl:32
+msgid "Git Gui"
+msgstr "ГПИ на Git"
+
+#: lib/choose_repository.tcl:91 lib/choose_repository.tcl:396
+msgid "Create New Repository"
+msgstr "Създаване на ново хранилище"
+
+#: lib/choose_repository.tcl:97
+msgid "New..."
+msgstr "Ново…"
+
+#: lib/choose_repository.tcl:104 lib/choose_repository.tcl:480
+msgid "Clone Existing Repository"
+msgstr "Клониране на съществуващо хранилище"
+
+#: lib/choose_repository.tcl:115
+msgid "Clone..."
+msgstr "Клониране…"
+
+#: lib/choose_repository.tcl:122 lib/choose_repository.tcl:1019
+msgid "Open Existing Repository"
+msgstr "Отваряне на съществуващо хранилище"
+
+#: lib/choose_repository.tcl:128
+msgid "Open..."
+msgstr "Отваряне…"
+
+#: lib/choose_repository.tcl:141
+msgid "Recent Repositories"
+msgstr "Скоро ползвани"
+
+#: lib/choose_repository.tcl:147
+msgid "Open Recent Repository:"
+msgstr "Отваряне на хранилище ползвано наскоро:"
+
+#: lib/choose_repository.tcl:315 lib/choose_repository.tcl:322
+#: lib/choose_repository.tcl:329
+#, tcl-format
+msgid "Failed to create repository %s:"
+msgstr "Неуспешно създаване на хранилището „%s“:"
+
+#: lib/choose_repository.tcl:391 lib/branch_create.tcl:33
+msgid "Create"
+msgstr "Създаване"
+
+#: lib/choose_repository.tcl:401
+msgid "Directory:"
+msgstr "Директория:"
+
+#: lib/choose_repository.tcl:431 lib/choose_repository.tcl:552
+#: lib/choose_repository.tcl:1053
+msgid "Git Repository"
+msgstr "Хранилище на Git"
+
+#: lib/choose_repository.tcl:456
+#, tcl-format
+msgid "Directory %s already exists."
+msgstr "Вече съществува директория „%s“."
+
+#: lib/choose_repository.tcl:460
+#, tcl-format
+msgid "File %s already exists."
+msgstr "Вече съществува файл „%s“."
+
+#: lib/choose_repository.tcl:475
+msgid "Clone"
+msgstr "Клониране"
+
+#: lib/choose_repository.tcl:488
+msgid "Source Location:"
+msgstr "Адрес на източника:"
+
+#: lib/choose_repository.tcl:497
+msgid "Target Directory:"
+msgstr "Целева директория:"
+
+#: lib/choose_repository.tcl:507
+msgid "Clone Type:"
+msgstr "Вид клониране:"
+
+#: lib/choose_repository.tcl:512
+msgid "Standard (Fast, Semi-Redundant, Hardlinks)"
+msgstr "Стандартно (бързо, частично споделяне на файлове, твърди връзки)"
+
+#: lib/choose_repository.tcl:517
+msgid "Full Copy (Slower, Redundant Backup)"
+msgstr "Пълно (бавно, пълноценно резервно копие)"
+
+#: lib/choose_repository.tcl:522
+msgid "Shared (Fastest, Not Recommended, No Backup)"
+msgstr "Споделено (най-бързо, не се препоръчва, не прави резервно копие)"
+
+#: lib/choose_repository.tcl:558 lib/choose_repository.tcl:605
+#: lib/choose_repository.tcl:751 lib/choose_repository.tcl:821
+#: lib/choose_repository.tcl:1059 lib/choose_repository.tcl:1067
+#, tcl-format
+msgid "Not a Git repository: %s"
+msgstr "Това не е хранилище на Git: %s"
+
+#: lib/choose_repository.tcl:594
+msgid "Standard only available for local repository."
+msgstr "Само локални хранилища могат да се клонират стандартно"
+
+#: lib/choose_repository.tcl:598
+msgid "Shared only available for local repository."
+msgstr "Само локални хранилища могат да се клонират споделено"
+
+#: lib/choose_repository.tcl:619
+#, tcl-format
+msgid "Location %s already exists."
+msgstr "Вече съществува местоположението „%s“."
+
+#: lib/choose_repository.tcl:630
+msgid "Failed to configure origin"
+msgstr "Неуспешно настройване на хранилището-източник"
+
+#: lib/choose_repository.tcl:642
+msgid "Counting objects"
+msgstr "Преброяване на обекти"
+
+#: lib/choose_repository.tcl:643
+msgid "buckets"
+msgstr "клетки"
+
+#: lib/choose_repository.tcl:667
+#, tcl-format
+msgid "Unable to copy objects/info/alternates: %s"
+msgstr "Обектите/информацията/синонимите не могат да бъдат копирани: %s"
+
+#: lib/choose_repository.tcl:703
+#, tcl-format
+msgid "Nothing to clone from %s."
+msgstr "Няма какво да се клонира от „%s“."
+
+#: lib/choose_repository.tcl:705 lib/choose_repository.tcl:919
+#: lib/choose_repository.tcl:931
+msgid "The 'master' branch has not been initialized."
+msgstr "Основният клон — „master“ не е инициализиран."
+
+#: lib/choose_repository.tcl:718
+msgid "Hardlinks are unavailable.  Falling back to copying."
+msgstr "Не се поддържат твърди връзки. Преминава се към копиране."
+
+#: lib/choose_repository.tcl:730
+#, tcl-format
+msgid "Cloning from %s"
+msgstr "Клониране на „%s“"
+
+#: lib/choose_repository.tcl:761
+msgid "Copying objects"
+msgstr "Копиране на обекти"
+
+#: lib/choose_repository.tcl:762
+msgid "KiB"
+msgstr "KiB"
+
+#: lib/choose_repository.tcl:786
+#, tcl-format
+msgid "Unable to copy object: %s"
+msgstr "Неуспешно копиране на обект: %s"
+
+#: lib/choose_repository.tcl:796
+msgid "Linking objects"
+msgstr "Създаване на връзки към обектите"
+
+#: lib/choose_repository.tcl:797
+msgid "objects"
+msgstr "обекти"
+
+#: lib/choose_repository.tcl:805
+#, tcl-format
+msgid "Unable to hardlink object: %s"
+msgstr "Неуспешно създаване на твърда връзка към обект: %s"
+
+#: lib/choose_repository.tcl:860
+msgid "Cannot fetch branches and objects.  See console output for details."
+msgstr ""
+"Клоните и обектите не могат да бъдат изтеглени. За повече информация "
+"погледнете изхода на конзолата."
+
+#: lib/choose_repository.tcl:871
+msgid "Cannot fetch tags.  See console output for details."
+msgstr ""
+"Етикетите не могат да бъдат изтеглени. За повече информация погледнете "
+"изхода на конзолата."
+
+#: lib/choose_repository.tcl:895
+msgid "Cannot determine HEAD.  See console output for details."
+msgstr ""
+"Върхът HEAD не може да бъде определен. За повече информация погледнете "
+"изхода на конзолата."
+
+#: lib/choose_repository.tcl:904
+#, tcl-format
+msgid "Unable to cleanup %s"
+msgstr "Не може да се зачисти „%s“"
+
+#: lib/choose_repository.tcl:910
+msgid "Clone failed."
+msgstr "Неуспешно клониране."
+
+#: lib/choose_repository.tcl:917
+msgid "No default branch obtained."
+msgstr "Не е получен клон по подразбиране."
+
+#: lib/choose_repository.tcl:928
+#, tcl-format
+msgid "Cannot resolve %s as a commit."
+msgstr "Няма подаване отговарящо на „%s“."
+
+#: lib/choose_repository.tcl:940
+msgid "Creating working directory"
+msgstr "Създаване на работната директория"
+
+#: lib/choose_repository.tcl:941 lib/index.tcl:70 lib/index.tcl:136
+#: lib/index.tcl:207
+msgid "files"
+msgstr "файлове"
+
+#: lib/choose_repository.tcl:970
+msgid "Initial file checkout failed."
+msgstr "Неуспешно първоначално изтегляне."
+
+#: lib/choose_repository.tcl:1014
+msgid "Open"
+msgstr "Отваряне"
+
+#: lib/choose_repository.tcl:1024
+msgid "Repository:"
+msgstr "Хранилище:"
+
+#: lib/choose_repository.tcl:1073
+#, tcl-format
+msgid "Failed to open repository %s:"
+msgstr "Неуспешно отваряне на хранилището „%s“:"
+
+#: lib/about.tcl:26
+msgid "git-gui - a graphical user interface for Git."
+msgstr "git-gui — графичен интерфейс за Git."
+
+#: lib/checkout_op.tcl:85
+#, tcl-format
+msgid "Fetching %s from %s"
+msgstr "Доставяне на „%s“ от „%s“"
+
+#: lib/checkout_op.tcl:133
+#, tcl-format
+msgid "fatal: Cannot resolve %s"
+msgstr "фатална грешка: „%s“ не може да се открие"
+
+#: lib/checkout_op.tcl:175
+#, tcl-format
+msgid "Branch '%s' does not exist."
+msgstr "Клонът „%s“ не съществува."
+
+#: lib/checkout_op.tcl:194
+#, tcl-format
+msgid "Failed to configure simplified git-pull for '%s'."
+msgstr "Неуспешно настройване на опростен git-pull за „%s“."
+
+#: lib/checkout_op.tcl:229
+#, 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:243
+#, tcl-format
+msgid "Merge strategy '%s' not supported."
+msgstr "Стратегия за сливане  „%s“ не се поддържа."
+
+#: lib/checkout_op.tcl:262
+#, tcl-format
+msgid "Failed to update '%s'."
+msgstr "Неуспешно обновяване на „%s“."
+
+#: lib/checkout_op.tcl:274
+msgid "Staging area (index) is already locked."
+msgstr "Индексът вече е заключен."
+
+#: lib/checkout_op.tcl:289
+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:345
+#, tcl-format
+msgid "Updating working directory to '%s'..."
+msgstr "Работната директория се привежда към „%s“…"
+
+#: lib/checkout_op.tcl:346
+msgid "files checked out"
+msgstr "файла са изтеглени"
+
+#: lib/checkout_op.tcl:376
+#, tcl-format
+msgid "Aborted checkout of '%s' (file level merging is required)."
+msgstr ""
+"Преустановяване на изтеглянето на „%s“ (необходимо е пофайлово сливане)."
+
+#: lib/checkout_op.tcl:377
+msgid "File level merge required."
+msgstr "Необходимо е пофайлово сливане."
+
+#: lib/checkout_op.tcl:381
+#, tcl-format
+msgid "Staying on branch '%s'."
+msgstr "Оставане върху клона „%s“."
+
+#: lib/checkout_op.tcl:452
+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:503 lib/checkout_op.tcl:507
+#, tcl-format
+msgid "Checked out '%s'."
+msgstr "„%s“ е изтеглен."
+
+#: lib/checkout_op.tcl:535
+#, tcl-format
+msgid "Resetting '%s' to '%s' will lose the following commits:"
+msgstr ""
+"Зануляването на „%s“ към „%s“ ще доведе до загубването на следните подавания:"
+
+#: lib/checkout_op.tcl:557
+msgid "Recovering lost commits may not be easy."
+msgstr "Възстановяването на загубените подавания може да е трудно."
+
+#: lib/checkout_op.tcl:562
+#, tcl-format
+msgid "Reset '%s'?"
+msgstr "Зануляване на „%s“?"
+
+#: lib/checkout_op.tcl:571 lib/branch_create.tcl:85
+msgid "Reset"
+msgstr "Отначало"
+
+#: lib/checkout_op.tcl:635
+#, 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/branch_create.tcl:23
+msgid "Create Branch"
+msgstr "Създаване на клон"
+
+#: lib/branch_create.tcl:28
+msgid "Create New Branch"
+msgstr "Създаване на нов клон"
+
+#: lib/branch_create.tcl:42
+msgid "Branch Name"
+msgstr "Име на клона"
+
+#: lib/branch_create.tcl:57
+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:97
+msgid "Checkout After Creation"
+msgstr "Преминаване към клона след създаването му"
+
+#: lib/branch_create.tcl:132
+msgid "Please select a tracking branch."
+msgstr "Изберете клон за следени."
+
+#: lib/branch_create.tcl:141
+#, tcl-format
+msgid "Tracking branch %s is not a branch in the remote repository."
+msgstr "Клонът, който се следи „%s“ не съществува в отдалеченото хранилище."
+
+#: lib/console.tcl:59
+msgid "Working... please wait..."
+msgstr "В момента се извършва действие, изчакайте…"
+
+#: lib/console.tcl:186
+msgid "Success"
+msgstr "Успех"
+
+#: lib/console.tcl:200
+msgid "Error: Command Failed"
+msgstr "Грешка: неуспешно изпълнение на команда"
+
+#: lib/choose_rev.tcl:52
+msgid "This Detached Checkout"
+msgstr "Това несвързано изтегляне"
+
+#: lib/choose_rev.tcl:60
+msgid "Revision Expression:"
+msgstr "Израз за версия"
+
+#: lib/choose_rev.tcl:72
+msgid "Local Branch"
+msgstr "Локален клон"
+
+#: lib/choose_rev.tcl:77
+msgid "Tracking Branch"
+msgstr "Следящ клон"
+
+#: lib/choose_rev.tcl:82 lib/choose_rev.tcl:544
+msgid "Tag"
+msgstr "Етикет"
+
+#: lib/choose_rev.tcl:321
+#, tcl-format
+msgid "Invalid revision: %s"
+msgstr "Неправилна версия: %s"
+
+#: lib/choose_rev.tcl:342
+msgid "No revision selected."
+msgstr "Не е избрана версия."
+
+#: lib/choose_rev.tcl:350
+msgid "Revision expression is empty."
+msgstr "Изразът за версия е празен."
+
+#: lib/choose_rev.tcl:537
+msgid "Updated"
+msgstr "Обновен"
+
+#: lib/choose_rev.tcl:565
+msgid "URL"
+msgstr "Адрес"
+
+#: lib/line.tcl:17
+msgid "Goto Line:"
+msgstr "Към ред:"
+
+#: lib/line.tcl:23
+msgid "Go"
+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:48
+msgid "Error loading commit data for amend:"
+msgstr "Грешка при зареждане на данните от подаване, които да се поправят:"
+
+#: lib/commit.tcl:75
+msgid "Unable to obtain your identity:"
+msgstr "Идентификацията ви не може да бъде определена:"
+
+#: lib/commit.tcl:80
+msgid "Invalid GIT_COMMITTER_IDENT:"
+msgstr "Неправилно поле GIT_COMMITTER_IDENT:"
+
+#: lib/commit.tcl:129
+#, tcl-format
+msgid "warning: Tcl does not support encoding '%s'."
+msgstr "предупреждение: Tcl не поддържа кодирането „%s“."
+
+#: lib/commit.tcl:149
+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:173
+#, 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:181
+#, 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:189
+msgid ""
+"No changes to commit.\n"
+"\n"
+"You must stage at least 1 file before you can commit.\n"
+msgstr ""
+"Няма промени за подаване.\n"
+"\n"
+"Трябва да добавите поне 1 файл към индекса, преди да подадете.\n"
+
+#: lib/commit.tcl:204
+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"
+"⁃ Първи ред: описание в едно изречение на промяната.\n"
+"⁃ Втори ред: празен.\n"
+"⁃ Останалите редове: опишете защо се налага тази промяна.\n"
+
+#: lib/commit.tcl:235
+msgid "Calling pre-commit hook..."
+msgstr "Изпълняване на куката преди подаване…"
+
+#: lib/commit.tcl:250
+msgid "Commit declined by pre-commit hook."
+msgstr "Подаването е отхвърлено от куката преди подаване."
+
+#: lib/commit.tcl:269
+msgid ""
+"You are about to commit on a detached head. This is a potentially dangerous "
+"thing to do because if you switch to another branch you will lose your "
+"changes and it can be difficult to retrieve them later from the reflog. You "
+"should probably cancel this commit and create a new branch to continue.\n"
+" \n"
+" Do you really want to proceed with your Commit?"
+msgstr ""
+"Ще подадете към несвързан връх. Това действие е опасно, защото при "
+"преминаването към който и да е клон ще загубите промените си и не е сигурно, "
+"че ще можете по-късно да ги възстановите чрез журнала на указателите. Най-"
+"вероятно трябва първо да създадете клон базиран на текущото състояние и да "
+"подадете към него.\n"
+" \n"
+"Сигурни ли сте, че искате да подадете точно сега?"
+
+#: lib/commit.tcl:290
+msgid "Calling commit-msg hook..."
+msgstr "Изпълняване на куката за съобщението при подаване…"
+
+#: lib/commit.tcl:305
+msgid "Commit declined by commit-msg hook."
+msgstr "Подаването е отхвърлено от куката за съобщението при подаване."
+
+#: lib/commit.tcl:318
+msgid "Committing changes..."
+msgstr "Подаване на промените."
+
+#: lib/commit.tcl:334
+msgid "write-tree failed:"
+msgstr "неуспешно запазване на дървото (write-tree):"
+
+#: lib/commit.tcl:335 lib/commit.tcl:379 lib/commit.tcl:400
+msgid "Commit failed."
+msgstr "Неуспешно подаване."
+
+#: lib/commit.tcl:352
+#, tcl-format
+msgid "Commit %s appears to be corrupt"
+msgstr "Подаването „%s“ изглежда повредено"
+
+#: lib/commit.tcl:357
+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:364
+msgid "No changes to commit."
+msgstr "Няма промени за подаване."
+
+#: lib/commit.tcl:378
+msgid "commit-tree failed:"
+msgstr "неуспешно подаване на дървото (commit-tree):"
+
+#: lib/commit.tcl:399
+msgid "update-ref failed:"
+msgstr "неуспешно обновяване на указателите (update-ref):"
+
+#: lib/commit.tcl:492
+#, tcl-format
+msgid "Created commit %s: %s"
+msgstr "Успешно подаване %s: %s"
+
+#: lib/branch_delete.tcl:16
+msgid "Delete Branch"
+msgstr "Изтриване на клон"
+
+#: lib/branch_delete.tcl:21
+msgid "Delete Local Branch"
+msgstr "Изтриване на локален клон"
+
+#: lib/branch_delete.tcl:39
+msgid "Local Branches"
+msgstr "Локални клони"
+
+#: lib/branch_delete.tcl:51
+msgid "Delete Only If Merged Into"
+msgstr "Изтриване, само ако промените са слети и другаде"
+
+#: lib/branch_delete.tcl:103
+#, tcl-format
+msgid "The following branches are not completely merged into %s:"
+msgstr "Не всички промени в клоните са слети в %s:"
+
+#: lib/branch_delete.tcl:141
+#, tcl-format
+msgid ""
+"Failed to delete branches:\n"
+"%s"
+msgstr ""
+"Неуспешно триене на клони:\n"
+"%s"
+
+#: lib/blame.tcl:73
+msgid "File Viewer"
+msgstr "Преглед на файлове"
+
+#: lib/blame.tcl:79
+msgid "Commit:"
+msgstr "Подаване:"
+
+#: lib/blame.tcl:280
+msgid "Copy Commit"
+msgstr "Копиране на подаване"
+
+#: lib/blame.tcl:284
+msgid "Find Text..."
+msgstr "Търсене на текст…"
+
+#: lib/blame.tcl:288
+msgid "Goto Line..."
+msgstr "Към ред…"
+
+#: lib/blame.tcl:297
+msgid "Do Full Copy Detection"
+msgstr "Пълно търсене на копиране"
+
+#: lib/blame.tcl:301
+msgid "Show History Context"
+msgstr "Показване на контекста от историята"
+
+#: lib/blame.tcl:304
+msgid "Blame Parent Commit"
+msgstr "Анотиране на родителското подаване"
+
+#: lib/blame.tcl:466
+#, tcl-format
+msgid "Reading %s..."
+msgstr "%s се чете…"
+
+#: lib/blame.tcl:594
+msgid "Loading copy/move tracking annotations..."
+msgstr "Зареждане на анотациите за проследяване на копирането/преместването…"
+
+#: lib/blame.tcl:614
+msgid "lines annotated"
+msgstr "реда анотирани"
+
+#: lib/blame.tcl:806
+msgid "Loading original location annotations..."
+msgstr "Зареждане на анотациите за първоначалното местоположение…"
+
+#: lib/blame.tcl:809
+msgid "Annotation complete."
+msgstr "Анотирането завърши."
+
+#: lib/blame.tcl:839
+msgid "Busy"
+msgstr "Операцията не е завършила"
+
+#: lib/blame.tcl:840
+msgid "Annotation process is already running."
+msgstr "В момента тече процес на анотиране."
+
+#: lib/blame.tcl:879
+msgid "Running thorough copy detection..."
+msgstr "Изпълнява се цялостен процес на откриване на копиране…"
+
+#: lib/blame.tcl:947
+msgid "Loading annotation..."
+msgstr "Зареждане на анотации…"
+
+#: lib/blame.tcl:1000
+msgid "Author:"
+msgstr "Автор:"
+
+#: lib/blame.tcl:1004
+msgid "Committer:"
+msgstr "Подал:"
+
+#: lib/blame.tcl:1009
+msgid "Original File:"
+msgstr "Първоначален файл:"
+
+#: lib/blame.tcl:1057
+msgid "Cannot find HEAD commit:"
+msgstr "Подаването за връх HEAD не може да се открие:"
+
+#: lib/blame.tcl:1112
+msgid "Cannot find parent commit:"
+msgstr "Родителското подаване не може да бъде открито"
+
+#: lib/blame.tcl:1127
+msgid "Unable to display parent"
+msgstr "Родителят не може да бъде показан"
+
+#: lib/blame.tcl:1269
+msgid "Originally By:"
+msgstr "Първоначално от:"
+
+#: lib/blame.tcl:1275
+msgid "In File:"
+msgstr "Във файл:"
+
+#: lib/blame.tcl:1280
+msgid "Copied Or Moved Here By:"
+msgstr "Копирано или преместено тук от:"
+
+#: lib/index.tcl:6
+msgid "Unable to unlock the index."
+msgstr "Индексът не може да бъде отключен."
+
+#: lib/index.tcl:17
+msgid "Index Error"
+msgstr "Грешка в индекса"
+
+#: lib/index.tcl:19
+msgid ""
+"Updating the Git index failed.  A rescan will be automatically started to "
+"resynchronize git-gui."
+msgstr ""
+"Неуспешно обновяване на индекса на Git. Автоматично ще започне нова проверка "
+"за синхронизирането на git-gui."
+
+#: lib/index.tcl:30
+msgid "Continue"
+msgstr "Продължаване"
+
+#: lib/index.tcl:33
+msgid "Unlock Index"
+msgstr "Отключване на индекса"
+
+#: lib/index.tcl:298
+#, tcl-format
+msgid "Unstaging %s from commit"
+msgstr "Изваждане на %s от подаването"
+
+#: lib/index.tcl:337
+msgid "Ready to commit."
+msgstr "Готовност за подаване."
+
+#: lib/index.tcl:350
+#, tcl-format
+msgid "Adding %s"
+msgstr "Добавяне на „%s“"
+
+#: lib/index.tcl:380
+#, tcl-format
+msgid "Stage %d untracked files?"
+msgstr "Да се добавят ли %d на брой неследени файла към индекса?"
+
+#: lib/index.tcl:428
+#, tcl-format
+msgid "Revert changes in file %s?"
+msgstr "Да се махнат ли промените във файла „%s“?"
+
+#: lib/index.tcl:430
+#, tcl-format
+msgid "Revert changes in these %i files?"
+msgstr "Да се махнат ли промените в тези %i файла?"
+
+#: lib/index.tcl:438
+msgid "Any unstaged changes will be permanently lost by the revert."
+msgstr ""
+"Всички промени, които не са били вкарани в индекса, ще бъдат безвъзвратно "
+"загубени."
+
+#: lib/index.tcl:441
+msgid "Do Nothing"
+msgstr "Нищо да не се прави"
+
+#: lib/index.tcl:459
+msgid "Reverting selected files"
+msgstr "Махане на промените в избраните файлове"
+
+#: lib/index.tcl:463
+#, tcl-format
+msgid "Reverting %s"
+msgstr "Махане на промените в „%s“"
+
+#: lib/encoding.tcl:443
+msgid "Default"
+msgstr "Стандартното"
+
+#: lib/encoding.tcl:448
+#, tcl-format
+msgid "System (%s)"
+msgstr "Системното (%s)"
+
+#: lib/encoding.tcl:459 lib/encoding.tcl:465
+msgid "Other"
+msgstr "Друго"
+
+#: lib/date.tcl:25
+#, tcl-format
+msgid "Invalid date from Git: %s"
+msgstr "Неправилни данни от Git: %s"
+
+#: lib/database.tcl:42
+msgid "Number of loose objects"
+msgstr "Брой непакетирани обекти"
+
+#: lib/database.tcl:43
+msgid "Disk space used by loose objects"
+msgstr "Дисково пространство заето от непакетирани обекти"
+
+#: lib/database.tcl:44
+msgid "Number of packed objects"
+msgstr "Брой пакетирани обекти"
+
+#: lib/database.tcl:45
+msgid "Number of packs"
+msgstr "Брой пакети"
+
+#: lib/database.tcl:46
+msgid "Disk space used by packed objects"
+msgstr "Дисково пространство заето от пакетирани обекти"
+
+#: lib/database.tcl:47
+msgid "Packed objects waiting for pruning"
+msgstr "Пакетирани обекти за окастряне"
+
+#: lib/database.tcl:48
+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:107
+#, 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.\n"
+"\n"
+"Compress the database now?"
+msgstr ""
+"В това хранилище в момента има към %i непакетирани обекти.\n"
+"\n"
+"Зd добра производителност се препоръчва да компресирате базата с данни за "
+"обектите.\n"
+"\n"
+"Да се започне ли компресирането?"
+
+#: lib/error.tcl:20 lib/error.tcl:116
+msgid "error"
+msgstr "грешка"
+
+#: lib/error.tcl:36
+msgid "warning"
+msgstr "предупреждение"
+
+#: lib/error.tcl:96
+msgid "You must correct the above errors before committing."
+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"
+"\n"
+
+#: lib/merge.tcl:45
+#, 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:55
+#, 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:108
+#, tcl-format
+msgid "%s of %s"
+msgstr "%s от общо %s"
+
+#: lib/merge.tcl:122
+#, tcl-format
+msgid "Merging %s and %s..."
+msgstr "Сливане на „%s“ и „%s“…"
+
+#: lib/merge.tcl:133
+msgid "Merge completed successfully."
+msgstr "Сливането завърши успешно."
+
+#: lib/merge.tcl:135
+msgid "Merge failed.  Conflict resolution is required."
+msgstr "Неуспешно сливане — има конфликти за коригиране."
+
+#: lib/merge.tcl:160
+#, tcl-format
+msgid "Merge Into %s"
+msgstr "Сливане в „%s“"
+
+#: lib/merge.tcl:179
+msgid "Revision To Merge"
+msgstr "Версия за сливане"
+
+#: lib/merge.tcl:214
+msgid ""
+"Cannot abort while amending.\n"
+"\n"
+"You must finish amending this commit.\n"
+msgstr ""
+"Поправянето не може да бъде преустановено.\n"
+"\n"
+"Трябва да завършите поправката на това подаване.\n"
+
+#: lib/merge.tcl:224
+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:230
+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:241
+msgid "Aborting"
+msgstr "Преустановяване"
+
+#: lib/merge.tcl:241
+msgid "files reset"
+msgstr "файла със занулени промени"
+
+#: lib/merge.tcl:269
+msgid "Abort failed."
+msgstr "Неуспешно преустановяване."
+
+#: lib/merge.tcl:271
+msgid "Abort completed.  Ready."
+msgstr "Успешно преустановяване. Готовност за следващо действие."
diff --git a/git-gui/po/glossary/bg.po b/git-gui/po/glossary/bg.po
new file mode 100644 (file)
index 0000000..8b71fad
--- /dev/null
@@ -0,0 +1,287 @@
+# Bulgarian translation of git-gui-glossary po-file.
+# Copyright (C) 2012, 2013, 2014 Alexander Shopov <ash@kambanaria.org>.
+# This file is distributed under the same license as the git package.
+# Alexander Shopov <ash@kambanaria.org>, 2012, 2013, 2014.
+#
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: git-gui-glossary master\n"
+"POT-Creation-Date: 2014-01-13 21:39+0200\n"
+"PO-Revision-Date: 2014-01-13 21:39+0200\n"
+"Last-Translator: Alexander Shopov <ash@kambanaria.org>\n"
+"Language-Team: Bulgarian <dict@fsa-bg.org>\n"
+"Language: bg\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"
+
+#. "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 "доставяне"
+
+#. "One context of consecutive lines in a whole patch, which consists of many such hunks"
+msgid "hunk"
+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 "работно копие/работно дърво"
+
+#. "a commit that succeeds the current one in git's graph of commits (not necessarily directly)"
+msgid "ancestor"
+msgstr "предшественик"
+
+#. "prematurely stop and abandon an operation"
+msgid "abort"
+msgstr "преустановявам"
+
+#. "a repository with only .git directory, without working directory"
+msgid "bare repository"
+msgstr "голо хранилище"
+
+#. "a parent version of the current file"
+msgid "base"
+msgstr "родителска версия"
+
+#. "get the authors responsible for each line in a file"
+msgid "blame"
+msgstr "анотирам/анотиране"
+
+#. "to select and apply a single commit without merging"
+msgid "cherry-pick"
+msgstr "отбирам"
+
+#. "a commit that directly succeeds the current one in git's graph of commits"
+msgid "child"
+msgstr "дете"
+
+#. "clean the state of the git repository, often after manually stopped operation"
+msgid "cleanup"
+msgstr "зачиствам"
+
+#. "a message that gets attached with any commit"
+msgid "commit message"
+msgstr "съобщение при подаване"
+
+#. "a commit that precedes the current one in git's graph of commits (not necessarily directly)"
+msgid "descendant"
+msgstr "наследник"
+
+#. "checkout of a revision rather than a some head"
+msgid "detached checkout"
+msgstr "несвързано изтегляне"
+
+#. "any merge strategy that works on a file by file basis"
+msgid "file level merging"
+msgstr "пофайлово сливане"
+
+#. "the last revision in a branch"
+msgid "head"
+msgstr "глава/връх (на клон, разработка)"
+
+#. "script that gets executed automatically on some event"
+msgid "hook"
+msgstr "кука/автоматичен скрипт"
+
+#. "the first checkout during a clone operation"
+msgid "initial checkout"
+msgstr "първоначално изтегляне"
+
+#. "a branch that resides in the local git repository"
+msgid "local branch"
+msgstr "локален клон"
+
+#. "a Git object that is not part of any pack"
+msgid "loose object"
+msgstr "непакетиран обект"
+
+#. "a branch called by convention 'master' that exists in a newly created git repository"
+msgid "master branch"
+msgstr "основен клон"
+
+#. "a remote called by convention 'origin' that the current git repository has been cloned from"
+msgid "origin"
+msgstr "хранилище-източник"
+
+#. "a file containing many git objects packed together"
+msgid "pack [noun]"
+msgstr "етикет"
+
+#. "a Git object part of some pack"
+msgid "packed object"
+msgstr "пакетиран обект"
+
+#. "a commit that directly precedes the current one in git's graph of commits"
+msgid "parent"
+msgstr "родител"
+
+#. "the log file containing all states of the HEAD reference (in other words past pristine states of the working copy)"
+msgid "reflog"
+msgstr "журнал на указателите"
+
+#. "decide which changes from alternative versions of a file should persist in Git"
+msgid "resolve (a conflict)"
+msgstr "коригирам (конфликт)"
+
+#. "abandon changes and go to pristine version"
+msgid "revert changes"
+msgstr "връщане на оригинала"
+
+#. "expression that signifies a revision in git"
+msgid "revision expression"
+msgstr "израз за версия"
+
+#. "add some content of files and directories to the staging area in preparation for a commit"
+msgid "stage/unstage"
+msgstr "(добавяне) към скелето за подаване/изваждане от скелето за подаване"
+
+#. "temporarily save changes in a stack without committing"
+msgid "stash"
+msgstr "скатавам промени"
+
+#. "file whose content is tracked/not tracked by git"
+msgid "tracked/untracked"
+msgstr "следен/неследен"
index 9b31f69152025e484ddf467d7884c2bf2140a894..409304692dc1ed17a8aa99a429dd6ecce1a4a3c6 100644 (file)
 "update"       ""
 "verify"       ""
 "working copy, working tree"   "The tree of actual checked out files."
+"ancestor"     "a commit that succeeds the current one in git's graph of commits (not necessarily directly)"
+"abort"        "prematurely stop and abandon an operation"
+"bare repository"      "a repository with only .git directory, without working directory"
+"base" "a parent version of the current file"
+"blame"        "get the authors responsible for each line in a file"
+"cherry-pick"  "to select and apply a single commit without merging"
+"child"        "a commit that directly succeeds the current one in git's graph of commits"
+"cleanup"      "clean the state of the git repository, often after manually stopped operation"
+"commit message"       "a message that gets attached with any commit"
+"descendant"   "a commit that precedes the current one in git's graph of commits (not necessarily directly)"
+"detached checkout"    "checkout of a revision rather than a some head"
+"file level merging"   "any merge strategy that works on a file by file basis"
+"head" "the last revision in a branch"
+"hook" "script that gets executed automatically on some event"
+"initial checkout"     "the first checkout during a clone operation"
+"local branch" "a branch that resides in the local git repository"
+"loose object" "a Git object that is not part of any pack"
+"master branch"        "a branch called by convention 'master' that exists in a newly created git repository"
+"origin"       "a remote called by convention 'origin' that the current git repository has been cloned from"
+"pack [noun]"  "a file containing many git objects packed together"
+"packed object"        "a Git object part of some pack"
+"parent"       "a commit that directly precedes the current one in git's graph of commits"
+"reflog"       "the log file containing all states of the HEAD reference (in other words past pristine states of the working copy)"
+"resolve (a conflict)" "decide which changes from alternative versions of a file should persist in Git"
+"revert changes"       "abandon changes and go to pristine version"
+"revision expression"  "expression that signifies a revision in git"
+"stage/unstage"        "add some content of files and directories to the staging area in preparation for a commit"
+"stash"        "temporarily save changes in a stack without committing"
+"tracked/untracked"    "file whose content is tracked/not tracked by git"
old mode 100644 (file)
new mode 100755 (executable)
old mode 100644 (file)
new mode 100755 (executable)
diff --git a/git-lost-found.sh b/git-lost-found.sh
deleted file mode 100755 (executable)
index 0b3e8c7..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-#!/bin/sh
-
-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
-fi
-
-laf="$GIT_DIR/lost-found"
-rm -fr "$laf" && mkdir -p "$laf/commit" "$laf/other" || exit
-
-git fsck --full --no-reflogs |
-while read dangling type sha1
-do
-       case "$dangling" in
-       dangling)
-               if git rev-parse -q --verify "$sha1^0" >/dev/null
-               then
-                       dir="$laf/commit"
-                       git show-branch "$sha1"
-               else
-                       dir="$laf/other"
-               fi
-               echo "$sha1" >"$dir/$sha1"
-               ;;
-       esac
-done
index a280f498179eb2747b32ba5de4111ccb32024848..c45a020301c16614057912b9a26d1466a94654a9 100644 (file)
@@ -1,5 +1,4 @@
-#!/bin/sh
-# git-mergetool--lib is a library for common merge tool functions
+# git-mergetool--lib is a shell library for common merge tool functions
 
 : ${MERGE_TOOLS_DIR=$(git --exec-path)/mergetools}
 
index 06a3cc6122410efcccf0fd77f359ffc2e809aff9..cdfa2df5d8e0add0363f3e246362b437b72c4d58 100755 (executable)
--- a/git-p4.py
+++ b/git-p4.py
@@ -310,8 +310,8 @@ def split_p4_type(p4type):
 #
 # return the raw p4 type of a file (text, text+ko, etc)
 #
-def p4_type(file):
-    results = p4CmdList(["fstat", "-T", "headType", file])
+def p4_type(f):
+    results = p4CmdList(["fstat", "-T", "headType", wildcard_encode(f)])
     return results[0]['headType']
 
 #
@@ -1220,7 +1220,7 @@ def edit_template(self, template_file):
             editor = os.environ.get("P4EDITOR")
         else:
             editor = read_pipe("git var GIT_EDITOR").strip()
-        system(editor + " " + template_file)
+        system([editor, template_file])
 
         # If the file was not saved, prompt to see if this patch should
         # be skipped.  But skip this verification step if configured so.
@@ -1311,7 +1311,7 @@ def applyCommit(self, id):
             else:
                 die("unknown modifier %s for %s" % (modifier, path))
 
-        diffcmd = "git format-patch -k --stdout \"%s^\"..\"%s\"" % (id, id)
+        diffcmd = "git diff-tree -p \"%s\"" % (id)
         patchcmd = diffcmd + " | git apply "
         tryPatchCmd = patchcmd + "--check -"
         applyPatchCmd = patchcmd + "--check --apply -"
@@ -1871,7 +1871,7 @@ def update_client_spec_path_cache(self, files):
                 # assume error is "... file(s) not in client view"
                 continue
             if "clientFile" not in res:
-                die("No clientFile from 'p4 where %s'" % depot_path)
+                die("No clientFile in 'p4 where' output")
             if "unmap" in res:
                 # it will list all of them, but only one not unmap-ped
                 continue
@@ -2075,7 +2075,14 @@ def streamOneP4File(self, file, contents):
             # p4 print on a symlink sometimes contains "target\n";
             # if it does, remove the newline
             data = ''.join(contents)
-            if data[-1] == '\n':
+            if not data:
+                # Some version of p4 allowed creating a symlink that pointed
+                # to nothing.  This causes p4 errors when checking out such
+                # a change, and errors here too.  Work around it by ignoring
+                # the bad symlink; hopefully a future change fixes it.
+                print "\nIgnoring empty symlink in %s" % file['depotFile']
+                return
+            elif data[-1] == '\n':
                 contents = [data[:-1]]
             else:
                 contents = [data]
index 0e87e0915ee16e36385aa4182b3a4271d1308641..55fe8d56c9d5107df0843378ef137d64a48b2980 100644 (file)
@@ -1,4 +1,6 @@
-#!/bin/sh
+# This is a shell library to calculate the remote repository and
+# upstream branch that should be pulled by "git pull" from the current
+# branch.
 
 # git-ls-remote could be called from outside a git managed repository;
 # this would fail in that case and would issue an error message.
index b946fd975bacae7b73645e0dba8ae61da716ccfc..0a5aa2c82187c3caa4b2a0716369314fc8c80c55 100755 (executable)
@@ -172,7 +172,7 @@ error_on_no_merge_candidates () {
        do
                case "$opt" in
                -t|--t|--ta|--tag|--tags)
-                       echo "Fetching tags only, you probably meant:"
+                       echo "It doesn't make sense to pull all tags; you probably meant:"
                        echo "  git fetch --tags"
                        exit 1
                esac
@@ -229,15 +229,7 @@ test true = "$rebase" && {
        test -n "$curr_branch" &&
        . git-parse-remote &&
        remoteref="$(get_remote_merge_branch "$@" 2>/dev/null)" &&
-       oldremoteref="$(git rev-parse -q --verify "$remoteref")" &&
-       for reflog in $(git rev-list -g $remoteref 2>/dev/null)
-       do
-               if test "$reflog" = "$(git merge-base $reflog $curr_branch)"
-               then
-                       oldremoteref="$reflog"
-                       break
-               fi
-       done
+       oldremoteref=$(git merge-base --fork-point "$remoteref" $curr_branch 2>/dev/null)
 }
 orig_head=$(git rev-parse -q --verify HEAD)
 git fetch $verbosity $progress $dry_run $recurse_submodules --update-head-ok "$@" || exit 1
index 34e3102fcbfa64ec101c1fe009c9b14a5976e4c2..a4f683a5d70213151f3713cc0a2d76dfe8293fd3 100644 (file)
@@ -1,4 +1,5 @@
-#!/bin/sh
+# This shell script fragment is sourced by git-rebase to implement
+# its default, fast, patch-based, non-interactive mode.
 #
 # Copyright (c) 2010 Junio C Hamano.
 #
index 3c6bed9a28f7ff96fa619a9acbfc42e4a2c78828..43c19e0829ca727501ba7e4d29c952bc286ccb77 100644 (file)
@@ -1,11 +1,8 @@
-#!/bin/sh
+# This shell script fragment is sourced by git-rebase to implement
+# its interactive mode.  "git rebase --interactive" makes it easy
+# to fix up commits in the middle of a series and rearrange commits.
 #
 # Copyright (c) 2006 Johannes E. Schindelin
-
-# SHORT DESCRIPTION
-#
-# This script makes it easy to fix up commits in the middle of a series,
-# and rearrange commits.
 #
 # The original idea comes from Eric W. Biederman, in
 # http://article.gmane.org/gmane.comp.version-control.git/22407
index 16d18176ece5eecf4a3b0b0a9dea0a5b41ac43d4..e7d96de9adcb133982bbbc6d20cd3b52b3c89214 100644 (file)
@@ -1,4 +1,6 @@
-#!/bin/sh
+# This shell script fragment is sourced by git-rebase to implement
+# its merge-based non-interactive mode that copes well with renamed
+# files.
 #
 # Copyright (c) 2010 Junio C Hamano.
 #
index 226752fbff62f4f27da95f7d711c898503fd7148..8a3efa2983d08e38d40ae8b6dcecd82b0901ffa1 100755 (executable)
@@ -14,6 +14,7 @@ git-rebase --continue | --abort | --skip | --edit-todo
 v,verbose!         display a diffstat of what changed upstream
 q,quiet!           be quiet. implies --no-stat
 autostash!         automatically stash/stash pop before and after
+fork-point         use 'merge-base --fork-point' to refine upstream
 onto=!             rebase onto given branch instead of upstream
 p,preserve-merges! try to recreate merges instead of ignoring them
 s,strategy=!       use the given merge strategy
@@ -66,6 +67,7 @@ verbose=
 diffstat=
 test "$(git config --bool rebase.stat)" = true && diffstat=t
 autostash="$(git config --bool rebase.autostash || echo false)"
+fork_point=auto
 git_am_opt=
 rebase_root=
 force_rebase=
@@ -260,6 +262,12 @@ do
        --no-autosquash)
                autosquash=
                ;;
+       --fork-point)
+               fork_point=t
+               ;;
+       --no-fork-point)
+               fork_point=
+               ;;
        -M|-m)
                do_merge=t
                ;;
@@ -437,6 +445,8 @@ then
                        error_on_missing_default_upstream "rebase" "rebase" \
                                "against" "git rebase <branch>"
                fi
+
+               test "$fork_point" = auto && fork_point=t
                ;;
        *)      upstream_name="$1"
                shift
@@ -522,6 +532,16 @@ case "$#" in
        ;;
 esac
 
+if test "$fork_point" = t
+then
+       new_upstream=$(git merge-base --fork-point "$upstream_name" \
+                       "${switch_to:-HEAD}")
+       if test -n "$new_upstream"
+       then
+               upstream=$new_upstream
+       fi
+fi
+
 if test "$autostash" = true && ! (require_clean_work_tree) 2>/dev/null
 then
        stash_sha1=$(git stash create "autostash") ||
index 3782c3b0cb4f9356790ae581c0d408dc4f4cae75..fdb0029b597898559376b1b28d692450794251f3 100755 (executable)
@@ -291,7 +291,7 @@ sub signal_handler {
                    "smtp-pass:s" => \$smtp_authpass,
                    "smtp-ssl" => sub { $smtp_encryption = 'ssl' },
                    "smtp-encryption=s" => \$smtp_encryption,
-                   "smtp-ssl-cert-path" => \$smtp_ssl_cert_path,
+                   "smtp-ssl-cert-path=s" => \$smtp_ssl_cert_path,
                    "smtp-debug:i" => \$debug_net_smtp,
                    "smtp-domain:s" => \$smtp_domain,
                    "identity=s" => \$identity,
@@ -1095,7 +1095,8 @@ sub ssl_verify_params {
        }
 
        if (!defined $smtp_ssl_cert_path) {
-               $smtp_ssl_cert_path = "/etc/ssl/certs";
+               # use the OpenSSL defaults
+               return (SSL_verify_mode => SSL_VERIFY_PEER());
        }
 
        if ($smtp_ssl_cert_path eq "") {
@@ -1214,10 +1215,14 @@ sub send_message {
                        $smtp_server_port ||= 465; # ssmtp
                        require Net::SMTP::SSL;
                        $smtp_domain ||= maildomain();
+                       require IO::Socket::SSL;
+                       # Net::SMTP::SSL->new() does not forward any SSL options
+                       IO::Socket::SSL::set_client_defaults(
+                               ssl_verify_params());
                        $smtp ||= Net::SMTP::SSL->new($smtp_server,
                                                      Hello => $smtp_domain,
                                                      Port => $smtp_server_port,
-                                                     ssl_verify_params());
+                                                     Debug => $debug_net_smtp);
                }
                else {
                        require Net::SMTP;
index 6a27f6813624e10b479cae244ad3bb9d2127ff6d..e6c3116e181318fa4c1036b0a26a1d0f24a4ace7 100644 (file)
@@ -1,9 +1,8 @@
-#!/bin/sh
+# This shell library is Git's interface to gettext.sh. See po/README
+# for usage instructions.
 #
 # Copyright (c) 2010 Ævar Arnfjörð Bjarmason
 #
-# This is Git's interface to gettext.sh. See po/README for usage
-# instructions.
 
 # Export the TEXTDOMAIN* data that we need for Git
 TEXTDOMAIN=git
index ebfe8f7a4d0697422a826d7878855bf9e92d0060..fffa3c72d75961159888fd156614aa6679f3638c 100644 (file)
@@ -1,9 +1,6 @@
-#!/bin/sh
-#
-# This is included in commands that either have to be run from the toplevel
-# of the repository, or with GIT_DIR environment variable properly.
-# If the GIT_DIR does not look like the right correct git-repository,
-# it dies.
+# This shell scriplet is meant to be included by other shell scripts
+# to set up some variables pointing at the normal git directories and
+# a few helper shell functions.
 
 # Having this variable in your environment would break scripts because
 # you would cause "cd" to be taken to unexpected places.  If you
@@ -162,7 +159,8 @@ git_pager() {
                GIT_PAGER=cat
        fi
        : ${LESS=-FRSX}
-       export LESS
+       : ${LV=-c}
+       export LESS LV
 
        eval "$GIT_PAGER" '"$@"'
 }
index 1e541a21257c70fa67c0fee552ccb05fc5b6a280..f0a94abf1434c65d27caf4cd674f89de62e9333c 100755 (executable)
@@ -358,7 +358,7 @@ parse_flags_and_rev()
        i_tree=
        u_tree=
 
-       REV=$(git rev-parse --no-flags --symbolic "$@") || exit 1
+       REV=$(git rev-parse --no-flags --symbolic --sq "$@") || exit 1
 
        FLAGS=
        for opt
@@ -376,7 +376,7 @@ parse_flags_and_rev()
                esac
        done
 
-       set -- $REV
+       eval set -- $REV
 
        case $# in
                0)
@@ -391,13 +391,13 @@ parse_flags_and_rev()
                ;;
        esac
 
-       REV=$(git rev-parse --quiet --symbolic --verify $1 2>/dev/null) || {
+       REV=$(git rev-parse --quiet --symbolic --verify "$1" 2>/dev/null) || {
                reference="$1"
                die "$(eval_gettext "\$reference is not valid reference")"
        }
 
-       i_commit=$(git rev-parse --quiet --verify $REV^2 2>/dev/null) &&
-       set -- $(git rev-parse $REV $REV^1 $REV: $REV^1: $REV^2: 2>/dev/null) &&
+       i_commit=$(git rev-parse --quiet --verify "$REV^2" 2>/dev/null) &&
+       set -- $(git rev-parse "$REV" "$REV^1" "$REV:" "$REV^1:" "$REV^2:" 2>/dev/null) &&
        s=$1 &&
        w_commit=$1 &&
        b_commit=$2 &&
@@ -408,8 +408,8 @@ parse_flags_and_rev()
        test "$ref_stash" = "$(git rev-parse --symbolic-full-name "${REV%@*}")" &&
        IS_STASH_REF=t
 
-       u_commit=$(git rev-parse --quiet --verify $REV^3 2>/dev/null) &&
-       u_tree=$(git rev-parse $REV^3: 2>/dev/null)
+       u_commit=$(git rev-parse --quiet --verify "$REV^3" 2>/dev/null) &&
+       u_tree=$(git rev-parse "$REV^3:" 2>/dev/null)
 }
 
 is_stash_like()
index 2677f2e8b3c57dc5c1a632e5705b26f74cbd7137..4a30087768a16d77bfcec41889f58b64fdf5f89c 100755 (executable)
@@ -622,7 +622,7 @@ cmd_init()
                   test -z "$(git config submodule."$name".update)"
                then
                        case "$upd" in
-                       rebase | merge | none)
+                       checkout | rebase | merge | none)
                                ;; # known modes of updating
                        *)
                                echo >&2 "warning: unknown update mode '$upd' suggested for submodule '$name'"
@@ -805,6 +805,17 @@ cmd_update()
                        update_module=$update
                else
                        update_module=$(git config submodule."$name".update)
+                       case "$update_module" in
+                       '')
+                               ;; # Unset update mode
+                       checkout | rebase | merge | none)
+                               ;; # Known update modes
+                       !*)
+                               ;; # Custom update command
+                       *)
+                               die "$(eval_gettext "Invalid update mode '$update_module' for submodule '$name'")"
+                               ;;
+                       esac
                fi
 
                displaypath=$(relative_path "$prefix$sm_path")
diff --git a/git.c b/git.c
index cb5208de6a5aab7c222a1d1655fe37089a119fe0..7cf2953eff47ca91b9a134e2e810fea1fc53e386 100644 (file)
--- a/git.c
+++ b/git.c
@@ -54,7 +54,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
                /*
                 * Check remaining flags.
                 */
-               if (!prefixcmp(cmd, "--exec-path")) {
+               if (starts_with(cmd, "--exec-path")) {
                        cmd += 11;
                        if (*cmd == '=')
                                git_set_argv_exec_path(cmd + 1);
@@ -92,7 +92,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
                                *envchanged = 1;
                        (*argv)++;
                        (*argc)--;
-               } else if (!prefixcmp(cmd, "--git-dir=")) {
+               } else if (starts_with(cmd, "--git-dir=")) {
                        setenv(GIT_DIR_ENVIRONMENT, cmd + 10, 1);
                        if (envchanged)
                                *envchanged = 1;
@@ -106,7 +106,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
                                *envchanged = 1;
                        (*argv)++;
                        (*argc)--;
-               } else if (!prefixcmp(cmd, "--namespace=")) {
+               } else if (starts_with(cmd, "--namespace=")) {
                        setenv(GIT_NAMESPACE_ENVIRONMENT, cmd + 12, 1);
                        if (envchanged)
                                *envchanged = 1;
@@ -120,7 +120,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
                                *envchanged = 1;
                        (*argv)++;
                        (*argc)--;
-               } else if (!prefixcmp(cmd, "--work-tree=")) {
+               } else if (starts_with(cmd, "--work-tree=")) {
                        setenv(GIT_WORK_TREE_ENVIRONMENT, cmd + 12, 1);
                        if (envchanged)
                                *envchanged = 1;
@@ -162,7 +162,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
                } else if (!strcmp(cmd, "--shallow-file")) {
                        (*argv)++;
                        (*argc)--;
-                       set_alternate_shallow_file((*argv)[0]);
+                       set_alternate_shallow_file((*argv)[0], 1);
                        if (envchanged)
                                *envchanged = 1;
                } else if (!strcmp(cmd, "-C")) {
@@ -332,127 +332,136 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
        return 0;
 }
 
-static void handle_internal_command(int argc, const char **argv)
+static struct cmd_struct commands[] = {
+       { "add", cmd_add, RUN_SETUP | NEED_WORK_TREE },
+       { "annotate", cmd_annotate, RUN_SETUP },
+       { "apply", cmd_apply, RUN_SETUP_GENTLY },
+       { "archive", cmd_archive },
+       { "bisect--helper", cmd_bisect__helper, RUN_SETUP },
+       { "blame", cmd_blame, RUN_SETUP },
+       { "branch", cmd_branch, RUN_SETUP },
+       { "bundle", cmd_bundle, RUN_SETUP_GENTLY },
+       { "cat-file", cmd_cat_file, RUN_SETUP },
+       { "check-attr", cmd_check_attr, RUN_SETUP },
+       { "check-ignore", cmd_check_ignore, RUN_SETUP | NEED_WORK_TREE },
+       { "check-mailmap", cmd_check_mailmap, RUN_SETUP },
+       { "check-ref-format", cmd_check_ref_format },
+       { "checkout", cmd_checkout, RUN_SETUP | NEED_WORK_TREE },
+       { "checkout-index", cmd_checkout_index,
+               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 },
+       { "clone", cmd_clone },
+       { "column", cmd_column, RUN_SETUP_GENTLY },
+       { "commit", cmd_commit, RUN_SETUP | NEED_WORK_TREE },
+       { "commit-tree", cmd_commit_tree, RUN_SETUP },
+       { "config", cmd_config, RUN_SETUP_GENTLY },
+       { "count-objects", cmd_count_objects, RUN_SETUP },
+       { "credential", cmd_credential, RUN_SETUP_GENTLY },
+       { "describe", cmd_describe, RUN_SETUP },
+       { "diff", cmd_diff },
+       { "diff-files", cmd_diff_files, RUN_SETUP | NEED_WORK_TREE },
+       { "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 },
+       { "fmt-merge-msg", cmd_fmt_merge_msg, RUN_SETUP },
+       { "for-each-ref", cmd_for_each_ref, RUN_SETUP },
+       { "format-patch", cmd_format_patch, RUN_SETUP },
+       { "fsck", cmd_fsck, RUN_SETUP },
+       { "fsck-objects", cmd_fsck, RUN_SETUP },
+       { "gc", cmd_gc, RUN_SETUP },
+       { "get-tar-commit-id", cmd_get_tar_commit_id },
+       { "grep", cmd_grep, RUN_SETUP_GENTLY },
+       { "hash-object", cmd_hash_object },
+       { "help", cmd_help },
+       { "index-pack", cmd_index_pack, RUN_SETUP_GENTLY },
+       { "init", cmd_init_db },
+       { "init-db", cmd_init_db },
+       { "log", cmd_log, RUN_SETUP },
+       { "ls-files", cmd_ls_files, RUN_SETUP },
+       { "ls-remote", cmd_ls_remote, RUN_SETUP_GENTLY },
+       { "ls-tree", cmd_ls_tree, RUN_SETUP },
+       { "mailinfo", cmd_mailinfo },
+       { "mailsplit", cmd_mailsplit },
+       { "merge", cmd_merge, RUN_SETUP | NEED_WORK_TREE },
+       { "merge-base", cmd_merge_base, RUN_SETUP },
+       { "merge-file", cmd_merge_file, RUN_SETUP_GENTLY },
+       { "merge-index", cmd_merge_index, RUN_SETUP },
+       { "merge-ours", cmd_merge_ours, RUN_SETUP },
+       { "merge-recursive", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
+       { "merge-recursive-ours", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
+       { "merge-recursive-theirs", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
+       { "merge-subtree", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
+       { "merge-tree", cmd_merge_tree, RUN_SETUP },
+       { "mktag", cmd_mktag, RUN_SETUP },
+       { "mktree", cmd_mktree, RUN_SETUP },
+       { "mv", cmd_mv, RUN_SETUP | NEED_WORK_TREE },
+       { "name-rev", cmd_name_rev, RUN_SETUP },
+       { "notes", cmd_notes, RUN_SETUP },
+       { "pack-objects", cmd_pack_objects, RUN_SETUP },
+       { "pack-redundant", cmd_pack_redundant, RUN_SETUP },
+       { "pack-refs", cmd_pack_refs, RUN_SETUP },
+       { "patch-id", cmd_patch_id },
+       { "pickaxe", cmd_blame, RUN_SETUP },
+       { "prune", cmd_prune, RUN_SETUP },
+       { "prune-packed", cmd_prune_packed, RUN_SETUP },
+       { "push", cmd_push, RUN_SETUP },
+       { "read-tree", cmd_read_tree, RUN_SETUP },
+       { "receive-pack", cmd_receive_pack },
+       { "reflog", cmd_reflog, RUN_SETUP },
+       { "remote", cmd_remote, RUN_SETUP },
+       { "remote-ext", cmd_remote_ext },
+       { "remote-fd", cmd_remote_fd },
+       { "repack", cmd_repack, RUN_SETUP },
+       { "replace", cmd_replace, RUN_SETUP },
+       { "rerere", cmd_rerere, RUN_SETUP },
+       { "reset", cmd_reset, RUN_SETUP },
+       { "rev-list", cmd_rev_list, RUN_SETUP },
+       { "rev-parse", cmd_rev_parse },
+       { "revert", cmd_revert, RUN_SETUP | NEED_WORK_TREE },
+       { "rm", cmd_rm, RUN_SETUP },
+       { "send-pack", cmd_send_pack, RUN_SETUP },
+       { "shortlog", cmd_shortlog, RUN_SETUP_GENTLY | USE_PAGER },
+       { "show", cmd_show, RUN_SETUP },
+       { "show-branch", cmd_show_branch, RUN_SETUP },
+       { "show-ref", cmd_show_ref, RUN_SETUP },
+       { "stage", cmd_add, RUN_SETUP | NEED_WORK_TREE },
+       { "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
+       { "stripspace", cmd_stripspace },
+       { "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
+       { "tag", cmd_tag, RUN_SETUP },
+       { "unpack-file", cmd_unpack_file, RUN_SETUP },
+       { "unpack-objects", cmd_unpack_objects, RUN_SETUP },
+       { "update-index", cmd_update_index, RUN_SETUP },
+       { "update-ref", cmd_update_ref, RUN_SETUP },
+       { "update-server-info", cmd_update_server_info, RUN_SETUP },
+       { "upload-archive", cmd_upload_archive },
+       { "upload-archive--writer", cmd_upload_archive_writer },
+       { "var", cmd_var, RUN_SETUP_GENTLY },
+       { "verify-pack", cmd_verify_pack },
+       { "verify-tag", cmd_verify_tag, RUN_SETUP },
+       { "version", cmd_version },
+       { "whatchanged", cmd_whatchanged, RUN_SETUP },
+       { "write-tree", cmd_write_tree, RUN_SETUP },
+};
+
+int is_builtin(const char *s)
+{
+       int i;
+       for (i = 0; i < ARRAY_SIZE(commands); i++) {
+               struct cmd_struct *p = commands+i;
+               if (!strcmp(s, p->cmd))
+                       return 1;
+       }
+       return 0;
+}
+
+static void handle_builtin(int argc, const char **argv)
 {
        const char *cmd = argv[0];
-       static struct cmd_struct commands[] = {
-               { "add", cmd_add, RUN_SETUP | NEED_WORK_TREE },
-               { "annotate", cmd_annotate, RUN_SETUP },
-               { "apply", cmd_apply, RUN_SETUP_GENTLY },
-               { "archive", cmd_archive },
-               { "bisect--helper", cmd_bisect__helper, RUN_SETUP },
-               { "blame", cmd_blame, RUN_SETUP },
-               { "branch", cmd_branch, RUN_SETUP },
-               { "bundle", cmd_bundle, RUN_SETUP_GENTLY },
-               { "cat-file", cmd_cat_file, RUN_SETUP },
-               { "check-attr", cmd_check_attr, RUN_SETUP },
-               { "check-ignore", cmd_check_ignore, RUN_SETUP | NEED_WORK_TREE },
-               { "check-mailmap", cmd_check_mailmap, RUN_SETUP },
-               { "check-ref-format", cmd_check_ref_format },
-               { "checkout", cmd_checkout, RUN_SETUP | NEED_WORK_TREE },
-               { "checkout-index", cmd_checkout_index,
-                       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 },
-               { "clone", cmd_clone },
-               { "column", cmd_column, RUN_SETUP_GENTLY },
-               { "commit", cmd_commit, RUN_SETUP | NEED_WORK_TREE },
-               { "commit-tree", cmd_commit_tree, RUN_SETUP },
-               { "config", cmd_config, RUN_SETUP_GENTLY },
-               { "count-objects", cmd_count_objects, RUN_SETUP },
-               { "credential", cmd_credential, RUN_SETUP_GENTLY },
-               { "describe", cmd_describe, RUN_SETUP },
-               { "diff", cmd_diff },
-               { "diff-files", cmd_diff_files, RUN_SETUP | NEED_WORK_TREE },
-               { "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 },
-               { "fmt-merge-msg", cmd_fmt_merge_msg, RUN_SETUP },
-               { "for-each-ref", cmd_for_each_ref, RUN_SETUP },
-               { "format-patch", cmd_format_patch, RUN_SETUP },
-               { "fsck", cmd_fsck, RUN_SETUP },
-               { "fsck-objects", cmd_fsck, RUN_SETUP },
-               { "gc", cmd_gc, RUN_SETUP },
-               { "get-tar-commit-id", cmd_get_tar_commit_id },
-               { "grep", cmd_grep, RUN_SETUP_GENTLY },
-               { "hash-object", cmd_hash_object },
-               { "help", cmd_help },
-               { "index-pack", cmd_index_pack, RUN_SETUP_GENTLY },
-               { "init", cmd_init_db },
-               { "init-db", cmd_init_db },
-               { "log", cmd_log, RUN_SETUP },
-               { "ls-files", cmd_ls_files, RUN_SETUP },
-               { "ls-remote", cmd_ls_remote, RUN_SETUP_GENTLY },
-               { "ls-tree", cmd_ls_tree, RUN_SETUP },
-               { "mailinfo", cmd_mailinfo },
-               { "mailsplit", cmd_mailsplit },
-               { "merge", cmd_merge, RUN_SETUP | NEED_WORK_TREE },
-               { "merge-base", cmd_merge_base, RUN_SETUP },
-               { "merge-file", cmd_merge_file, RUN_SETUP_GENTLY },
-               { "merge-index", cmd_merge_index, RUN_SETUP },
-               { "merge-ours", cmd_merge_ours, RUN_SETUP },
-               { "merge-recursive", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
-               { "merge-recursive-ours", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
-               { "merge-recursive-theirs", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
-               { "merge-subtree", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
-               { "merge-tree", cmd_merge_tree, RUN_SETUP },
-               { "mktag", cmd_mktag, RUN_SETUP },
-               { "mktree", cmd_mktree, RUN_SETUP },
-               { "mv", cmd_mv, RUN_SETUP | NEED_WORK_TREE },
-               { "name-rev", cmd_name_rev, RUN_SETUP },
-               { "notes", cmd_notes, RUN_SETUP },
-               { "pack-objects", cmd_pack_objects, RUN_SETUP },
-               { "pack-redundant", cmd_pack_redundant, RUN_SETUP },
-               { "pack-refs", cmd_pack_refs, RUN_SETUP },
-               { "patch-id", cmd_patch_id },
-               { "peek-remote", cmd_ls_remote, RUN_SETUP_GENTLY },
-               { "pickaxe", cmd_blame, RUN_SETUP },
-               { "prune", cmd_prune, RUN_SETUP },
-               { "prune-packed", cmd_prune_packed, RUN_SETUP },
-               { "push", cmd_push, RUN_SETUP },
-               { "read-tree", cmd_read_tree, RUN_SETUP },
-               { "receive-pack", cmd_receive_pack },
-               { "reflog", cmd_reflog, RUN_SETUP },
-               { "remote", cmd_remote, RUN_SETUP },
-               { "remote-ext", cmd_remote_ext },
-               { "remote-fd", cmd_remote_fd },
-               { "repack", cmd_repack, RUN_SETUP },
-               { "replace", cmd_replace, RUN_SETUP },
-               { "repo-config", cmd_repo_config, RUN_SETUP_GENTLY },
-               { "rerere", cmd_rerere, RUN_SETUP },
-               { "reset", cmd_reset, RUN_SETUP },
-               { "rev-list", cmd_rev_list, RUN_SETUP },
-               { "rev-parse", cmd_rev_parse },
-               { "revert", cmd_revert, RUN_SETUP | NEED_WORK_TREE },
-               { "rm", cmd_rm, RUN_SETUP },
-               { "send-pack", cmd_send_pack, RUN_SETUP },
-               { "shortlog", cmd_shortlog, RUN_SETUP_GENTLY | USE_PAGER },
-               { "show", cmd_show, RUN_SETUP },
-               { "show-branch", cmd_show_branch, RUN_SETUP },
-               { "show-ref", cmd_show_ref, RUN_SETUP },
-               { "stage", cmd_add, RUN_SETUP | NEED_WORK_TREE },
-               { "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
-               { "stripspace", cmd_stripspace },
-               { "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
-               { "tag", cmd_tag, RUN_SETUP },
-               { "tar-tree", cmd_tar_tree },
-               { "unpack-file", cmd_unpack_file, RUN_SETUP },
-               { "unpack-objects", cmd_unpack_objects, RUN_SETUP },
-               { "update-index", cmd_update_index, RUN_SETUP },
-               { "update-ref", cmd_update_ref, RUN_SETUP },
-               { "update-server-info", cmd_update_server_info, RUN_SETUP },
-               { "upload-archive", cmd_upload_archive },
-               { "upload-archive--writer", cmd_upload_archive_writer },
-               { "var", cmd_var, RUN_SETUP_GENTLY },
-               { "verify-pack", cmd_verify_pack },
-               { "verify-tag", cmd_verify_tag, RUN_SETUP },
-               { "version", cmd_version },
-               { "whatchanged", cmd_whatchanged, RUN_SETUP },
-               { "write-tree", cmd_write_tree, RUN_SETUP },
-       };
        int i;
        static const char ext[] = STRIP_EXTENSION;
 
@@ -520,8 +529,8 @@ static int run_argv(int *argcp, const char ***argv)
        int done_alias = 0;
 
        while (1) {
-               /* See if it's an internal command */
-               handle_internal_command(*argcp, *argv);
+               /* See if it's a builtin */
+               handle_builtin(*argcp, *argv);
 
                /* .. then try the external ones */
                execv_dashed_external(*argv);
@@ -566,14 +575,14 @@ int main(int argc, char **av)
         *  - cannot execute it externally (since it would just do
         *    the same thing over again)
         *
-        * So we just directly call the internal command handler, and
-        * die if that one cannot handle it.
+        * So we just directly call the builtin handler, and die if
+        * that one cannot handle it.
         */
-       if (!prefixcmp(cmd, "git-")) {
+       if (starts_with(cmd, "git-")) {
                cmd += 4;
                argv[0] = cmd;
-               handle_internal_command(argc, argv);
-               die("cannot handle %s internally", cmd);
+               handle_builtin(argc, argv);
+               die("cannot handle %s as a builtin", cmd);
        }
 
        /* Look for flags.. */
@@ -581,7 +590,7 @@ int main(int argc, char **av)
        argc--;
        handle_options(&argv, &argc, NULL);
        if (argc > 0) {
-               if (!prefixcmp(argv[0], "--"))
+               if (starts_with(argv[0], "--"))
                        argv[0] += 2;
        } else {
                /* The user didn't specify a command; give them help */
diff --git a/git.rc b/git.rc
index bce6db96a333c2d47b07580c5a9207cf10935378..33aafb786cf7f73eb7d4a769e585cb958999e68a 100644 (file)
--- a/git.rc
+++ b/git.rc
@@ -1,6 +1,6 @@
 1 VERSIONINFO
-FILEVERSION     MAJOR,MINOR,PATCH,0
-PRODUCTVERSION  MAJOR,MINOR,PATCH,0
+FILEVERSION     MAJOR,MINOR,0,0
+PRODUCTVERSION  MAJOR,MINOR,0,0
 BEGIN
   BLOCK "StringFileInfo"
   BEGIN
index 5cd00d80fe2fb80a07a59b7e5327f8ff6c29d8f6..90764e8948f1f95aaddfb381664ecede328bc863 100755 (executable)
@@ -2,7 +2,7 @@
 # Tcl ignores the next line -*- tcl -*- \
 exec wish "$0" -- "$@"
 
-# Copyright © 2005-2011 Paul Mackerras.  All rights reserved.
+# Copyright © 2005-2014 Paul Mackerras.  All rights reserved.
 # This program is free software; it may be used, copied, modified
 # and distributed under the terms of the GNU General Public Licence,
 # either version 2, or (at your option) any later version.
@@ -156,10 +156,12 @@ proc unmerged_files {files} {
 
 proc parseviewargs {n arglist} {
     global vdatemode vmergeonly vflags vdflags vrevs vfiltered vorigargs env
+    global vinlinediff
     global worddiff git_version
 
     set vdatemode($n) 0
     set vmergeonly($n) 0
+    set vinlinediff($n) 0
     set glflags {}
     set diffargs {}
     set nextisval 0
@@ -227,12 +229,20 @@ proc parseviewargs {n arglist} {
            "--until=*" - "--before=*" - "--max-age=*" - "--min-age=*" -
            "--author=*" - "--committer=*" - "--grep=*" - "-[iE]" -
            "--remove-empty" - "--first-parent" - "--cherry-pick" -
-           "-S*" - "--pickaxe-all" - "--pickaxe-regex" -
+           "-S*" - "-G*" - "--pickaxe-all" - "--pickaxe-regex" -
            "--simplify-by-decoration" {
                # These mean that we get a subset of the commits
                set filtered 1
                lappend glflags $arg
            }
+           "-L*" {
+               # Line-log with 'stuck' argument (unstuck form is
+               # not supported)
+               set filtered 1
+               set vinlinediff($n) 1
+               set allknown 0
+               lappend glflags $arg
+           }
            "-n" {
                # This appears to be the only one that has a value as a
                # separate word following it
@@ -1704,8 +1714,17 @@ proc parsecommit {id contents listed} {
        set comment $newcomment
     }
     set hasnote [string first "\nNotes:\n" $contents]
+    set diff ""
+    # If there is diff output shown in the git-log stream, split it
+    # out.  But get rid of the empty line that always precedes the
+    # diff.
+    set i [string first "\n\ndiff" $comment]
+    if {$i >= 0} {
+       set diff [string range $comment $i+1 end]
+       set comment [string range $comment 0 $i-1]
+    }
     set commitinfo($id) [list $headline $auname $audate \
-                            $comname $comdate $comment $hasnote]
+                            $comname $comdate $comment $hasnote $diff]
 }
 
 proc getcommit {id} {
@@ -2244,9 +2263,35 @@ proc makewindow {} {
 
     # build up the bottom bar of upper window
     ${NS}::label .tf.lbar.flabel -text "[mc "Find"] "
-    ${NS}::button .tf.lbar.fnext -text [mc "next"] -command {dofind 1 1}
-    ${NS}::button .tf.lbar.fprev -text [mc "prev"] -command {dofind -1 1}
+
+    set bm_down_data {
+       #define down_width 16
+       #define down_height 16
+       static unsigned char down_bits[] = {
+       0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01,
+       0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01,
+       0x87, 0xe1, 0x8e, 0x71, 0x9c, 0x39, 0xb8, 0x1d,
+       0xf0, 0x0f, 0xe0, 0x07, 0xc0, 0x03, 0x80, 0x01};
+    }
+    image create bitmap bm-down -data $bm_down_data -foreground $uifgcolor
+    ${NS}::button .tf.lbar.fnext -width 26 -command {dofind 1 1}
+    .tf.lbar.fnext configure -image bm-down
+
+    set bm_up_data {
+       #define up_width 16
+       #define up_height 16
+       static unsigned char up_bits[] = {
+       0x80, 0x01, 0xc0, 0x03, 0xe0, 0x07, 0xf0, 0x0f,
+       0xb8, 0x1d, 0x9c, 0x39, 0x8e, 0x71, 0x87, 0xe1,
+       0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01,
+       0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01};
+    }
+    image create bitmap bm-up -data $bm_up_data -foreground $uifgcolor
+    ${NS}::button .tf.lbar.fprev -width 26 -command {dofind -1 1}
+    .tf.lbar.fprev configure -image bm-up
+
     ${NS}::label .tf.lbar.flab2 -text " [mc "commit"] "
+
     pack .tf.lbar.flabel .tf.lbar.fnext .tf.lbar.fprev .tf.lbar.flab2 \
        -side left -fill y
     set gdttype [mc "containing:"]
@@ -2384,7 +2429,8 @@ proc makewindow {} {
     $ctext tag conf msep -font textfontbold
     $ctext tag conf found -back $foundbgcolor
     $ctext tag conf currentsearchhit -back $currentsearchhitbgcolor
-    $ctext tag conf wwrap -wrap word
+    $ctext tag conf wwrap -wrap word -lmargin2 1c
+    $ctext tag conf bold -font textfontbold
 
     .pwbottom add .bleft
     if {!$use_ttk} {
@@ -2741,14 +2787,17 @@ proc savestuff {w} {
     global linkfgcolor circleoutlinecolor
     global autoselect autosellen extdifftool perfile_attrs markbgcolor use_ttk
     global hideremotes want_ttk maxrefs
+    global config_file config_file_tmp
 
     if {$stuffsaved} return
     if {![winfo viewable .]} return
     catch {
-       if {[file exists ~/.gitk-new]} {file delete -force ~/.gitk-new}
-       set f [open "~/.gitk-new" w]
+       if {[file exists $config_file_tmp]} {
+           file delete -force $config_file_tmp
+       }
+       set f [open $config_file_tmp w]
        if {$::tcl_platform(platform) eq {windows}} {
-           file attributes "~/.gitk-new" -hidden true
+           file attributes $config_file_tmp -hidden true
        }
        puts $f [list set mainfont $mainfont]
        puts $f [list set textfont $textfont]
@@ -2825,7 +2874,7 @@ proc savestuff {w} {
        }
        puts $f "}"
        close $f
-       file rename -force "~/.gitk-new" "~/.gitk"
+       file rename -force $config_file_tmp $config_file
     }
     set stuffsaved 1
 }
@@ -2927,7 +2976,7 @@ proc about {} {
     message $w.m -text [mc "
 Gitk - a commit viewer for git
 
-Copyright \u00a9 2005-2011 Paul Mackerras
+Copyright \u00a9 2005-2014 Paul Mackerras
 
 Use and redistribute under the terms of the GNU General Public License"] \
            -justify center -aspect 400 -border 2 -bg white -relief groove
@@ -6387,6 +6436,25 @@ proc bindline {t id} {
     $canv bind $t <Button-1> "lineclick %x %y $id 1"
 }
 
+proc graph_pane_width {} {
+    global use_ttk
+
+    if {$use_ttk} {
+       set g [.tf.histframe.pwclist sashpos 0]
+    } else {
+       set g [.tf.histframe.pwclist sash coord 0]
+    }
+    return [lindex $g 0]
+}
+
+proc totalwidth {l font extra} {
+    set tot 0
+    foreach str $l {
+       set tot [expr {$tot + [font measure $font $str] + $extra}]
+    }
+    return $tot
+}
+
 proc drawtags {id x xt y1} {
     global idtags idheads idotherrefs mainhead
     global linespc lthickness
@@ -6398,9 +6466,27 @@ proc drawtags {id x xt y1} {
     set marks {}
     set ntags 0
     set nheads 0
+    set singletag 0
+    set maxtags 3
+    set maxtagpct 25
+    set maxwidth [expr {[graph_pane_width] * $maxtagpct / 100}]
+    set delta [expr {int(0.5 * ($linespc - $lthickness))}]
+    set extra [expr {$delta + $lthickness + $linespc}]
+
     if {[info exists idtags($id)]} {
        set marks $idtags($id)
        set ntags [llength $marks]
+       if {$ntags > $maxtags ||
+           [totalwidth $marks mainfont $extra] > $maxwidth} {
+           # show just a single "n tags..." tag
+           set singletag 1
+           if {$ntags == 1} {
+               set marks [list "tag..."]
+           } else {
+               set marks [list [format "%d tags..." $ntags]]
+           }
+           set ntags 1
+       }
     }
     if {[info exists idheads($id)]} {
        set marks [concat $marks $idheads($id)]
@@ -6413,7 +6499,6 @@ proc drawtags {id x xt y1} {
        return $xt
     }
 
-    set delta [expr {int(0.5 * ($linespc - $lthickness))}]
     set yt [expr {$y1 - 0.5 * $linespc}]
     set yb [expr {$yt + $linespc - 1}]
     set xvals {}
@@ -6428,7 +6513,7 @@ proc drawtags {id x xt y1} {
        }
        lappend xvals $xt
        lappend wvals $wid
-       set xt [expr {$xt + $delta + $wid + $lthickness + $linespc}]
+       set xt [expr {$xt + $wid + $extra}]
     }
     set t [$canv create line $x $y1 [lindex $xvals end] $y1 \
               -width $lthickness -fill $reflinecolor -tags tag.$id]
@@ -6444,7 +6529,12 @@ proc drawtags {id x xt y1} {
                       $xr $yt $xr $yb $xl $yb $x [expr {$yb - $delta}] \
                       -width 1 -outline $tagoutlinecolor -fill $tagbgcolor \
                       -tags tag.$id]
-           $canv bind $t <1> [list showtag $tag_quoted 1]
+           if {$singletag} {
+               set tagclick [list showtags $id 1]
+           } else {
+               set tagclick [list showtag $tag_quoted 1]
+           }
+           $canv bind $t <1> $tagclick
            set rowtextx([rowofcommit $id]) [expr {$xr + $linespc}]
        } else {
            # draw a head or other ref
@@ -6471,7 +6561,7 @@ proc drawtags {id x xt y1} {
        set t [$canv create text $xl $y1 -anchor w -text $tag -fill $headfgcolor \
                   -font $font -tags [list tag.$id text]]
        if {$ntags >= 0} {
-           $canv bind $t <1> [list showtag $tag_quoted 1]
+           $canv bind $t <1> $tagclick
        } elseif {$nheads >= 0} {
            $canv bind $t $ctxbut [list headmenu %X %Y $id $tag_quoted]
        }
@@ -7080,6 +7170,7 @@ proc selectline {l isnew {desired_loc {}}} {
     global cmitmode showneartags allcommits
     global targetrow targetid lastscrollrows
     global autoselect autosellen jump_to_here
+    global vinlinediff
 
     catch {unset pending_select}
     $canv delete hover
@@ -7221,6 +7312,8 @@ proc selectline {l isnew {desired_loc {}}} {
     init_flist [mc "Comments"]
     if {$cmitmode eq "tree"} {
        gettree $id
+    } elseif {$vinlinediff($curview) == 1} {
+       showinlinediff $id
     } elseif {[llength $olds] <= 1} {
        startdiff $id
     } else {
@@ -7557,6 +7650,39 @@ proc startdiff {ids} {
     }
 }
 
+proc showinlinediff {ids} {
+    global commitinfo commitdata ctext
+    global treediffs
+
+    set info $commitinfo($ids)
+    set diff [lindex $info 7]
+    set difflines [split $diff "\n"]
+
+    initblobdiffvars
+    set treediff {}
+
+    set inhdr 0
+    foreach line $difflines {
+       if {![string compare -length 5 "diff " $line]} {
+           set inhdr 1
+       } elseif {$inhdr && ![string compare -length 4 "+++ " $line]} {
+           # offset also accounts for the b/ prefix
+           lappend treediff [string range $line 6 end]
+           set inhdr 0
+       }
+    }
+
+    set treediffs($ids) $treediff
+    add_flist $treediff
+
+    $ctext conf -state normal
+    foreach line $difflines {
+       parseblobdiffline $ids $line
+    }
+    maybe_scroll_ctext 1
+    $ctext conf -state disabled
+}
+
 # If the filename (name) is under any of the passed filter paths
 # then return true to include the file in the listing.
 proc path_filter {filter name} {
@@ -7710,15 +7836,25 @@ proc changeworddiff {name ix op} {
     reselectline
 }
 
+proc initblobdiffvars {} {
+    global diffencoding targetline diffnparents
+    global diffinhdr currdiffsubmod diffseehere
+    set targetline {}
+    set diffnparents 0
+    set diffinhdr 0
+    set diffencoding [get_path_encoding {}]
+    set currdiffsubmod ""
+    set diffseehere -1
+}
+
 proc getblobdiffs {ids} {
     global blobdifffd diffids env
-    global diffinhdr treediffs
+    global treediffs
     global diffcontext
     global ignorespace
     global worddiff
     global limitdiffs vfilelimit curview
-    global diffencoding targetline diffnparents
-    global git_version currdiffsubmod
+    global git_version
 
     set textconv {}
     if {[package vcompare $git_version "1.6.1"] >= 0} {
@@ -7742,13 +7878,9 @@ proc getblobdiffs {ids} {
        error_popup [mc "Error getting diffs: %s" $err]
        return
     }
-    set targetline {}
-    set diffnparents 0
-    set diffinhdr 0
-    set diffencoding [get_path_encoding {}]
     fconfigure $bdf -blocking 0 -encoding binary -eofchar {}
     set blobdifffd($ids) $bdf
-    set currdiffsubmod ""
+    initblobdiffvars
     filerun $bdf [list getblobdiffline $bdf $diffids]
 }
 
@@ -7814,13 +7946,17 @@ proc makediffhdr {fname ids} {
     set diffline 0
 }
 
+proc blobdiffmaybeseehere {ateof} {
+    global diffseehere
+    if {$diffseehere >= 0} {
+       mark_ctext_line [lindex [split $diffseehere .] 0]
+    }
+    maybe_scroll_ctext $ateof
+}
+
 proc getblobdiffline {bdf ids} {
-    global diffids blobdifffd ctext curdiffstart
-    global diffnexthead diffnextnote difffilestart
-    global ctext_file_names ctext_file_lines
-    global diffinhdr treediffs mergemax diffnparents
-    global diffencoding jump_to_here targetline diffline currdiffsubmod
-    global worddiff
+    global diffids blobdifffd
+    global ctext
 
     set nr 0
     $ctext conf -state normal
@@ -7829,212 +7965,220 @@ proc getblobdiffline {bdf ids} {
            catch {close $bdf}
            return 0
        }
-       if {![string compare -length 5 "diff " $line]} {
-           if {![regexp {^diff (--cc|--git) } $line m type]} {
-               set line [encoding convertfrom $line]
-               $ctext insert end "$line\n" hunksep
-               continue
+       parseblobdiffline $ids $line
+    }
+    $ctext conf -state disabled
+    blobdiffmaybeseehere [eof $bdf]
+    if {[eof $bdf]} {
+       catch {close $bdf}
+       return 0
+    }
+    return [expr {$nr >= 1000? 2: 1}]
+}
+
+proc parseblobdiffline {ids line} {
+    global ctext curdiffstart
+    global diffnexthead diffnextnote difffilestart
+    global ctext_file_names ctext_file_lines
+    global diffinhdr treediffs mergemax diffnparents
+    global diffencoding jump_to_here targetline diffline currdiffsubmod
+    global worddiff diffseehere
+
+    if {![string compare -length 5 "diff " $line]} {
+       if {![regexp {^diff (--cc|--git) } $line m type]} {
+           set line [encoding convertfrom $line]
+           $ctext insert end "$line\n" hunksep
+           continue
+       }
+       # start of a new file
+       set diffinhdr 1
+       $ctext insert end "\n"
+       set curdiffstart [$ctext index "end - 1c"]
+       lappend ctext_file_names ""
+       lappend ctext_file_lines [lindex [split $curdiffstart "."] 0]
+       $ctext insert end "\n" filesep
+
+       if {$type eq "--cc"} {
+           # start of a new file in a merge diff
+           set fname [string range $line 10 end]
+           if {[lsearch -exact $treediffs($ids) $fname] < 0} {
+               lappend treediffs($ids) $fname
+               add_flist [list $fname]
            }
-           # start of a new file
-           set diffinhdr 1
-           $ctext insert end "\n"
-           set curdiffstart [$ctext index "end - 1c"]
-           lappend ctext_file_names ""
-           lappend ctext_file_lines [lindex [split $curdiffstart "."] 0]
-           $ctext insert end "\n" filesep
-
-           if {$type eq "--cc"} {
-               # start of a new file in a merge diff
-               set fname [string range $line 10 end]
-               if {[lsearch -exact $treediffs($ids) $fname] < 0} {
-                   lappend treediffs($ids) $fname
-                   add_flist [list $fname]
-               }
 
+       } else {
+           set line [string range $line 11 end]
+           # If the name hasn't changed the length will be odd,
+           # the middle char will be a space, and the two bits either
+           # side will be a/name and b/name, or "a/name" and "b/name".
+           # If the name has changed we'll get "rename from" and
+           # "rename to" or "copy from" and "copy to" lines following
+           # this, and we'll use them to get the filenames.
+           # This complexity is necessary because spaces in the
+           # filename(s) don't get escaped.
+           set l [string length $line]
+           set i [expr {$l / 2}]
+           if {!(($l & 1) && [string index $line $i] eq " " &&
+                 [string range $line 2 [expr {$i - 1}]] eq \
+                     [string range $line [expr {$i + 3}] end])} {
+               return
+           }
+           # unescape if quoted and chop off the a/ from the front
+           if {[string index $line 0] eq "\""} {
+               set fname [string range [lindex $line 0] 2 end]
            } else {
-               set line [string range $line 11 end]
-               # If the name hasn't changed the length will be odd,
-               # the middle char will be a space, and the two bits either
-               # side will be a/name and b/name, or "a/name" and "b/name".
-               # If the name has changed we'll get "rename from" and
-               # "rename to" or "copy from" and "copy to" lines following
-               # this, and we'll use them to get the filenames.
-               # This complexity is necessary because spaces in the
-               # filename(s) don't get escaped.
-               set l [string length $line]
-               set i [expr {$l / 2}]
-               if {!(($l & 1) && [string index $line $i] eq " " &&
-                     [string range $line 2 [expr {$i - 1}]] eq \
-                         [string range $line [expr {$i + 3}] end])} {
-                   continue
-               }
-               # unescape if quoted and chop off the a/ from the front
-               if {[string index $line 0] eq "\""} {
-                   set fname [string range [lindex $line 0] 2 end]
-               } else {
-                   set fname [string range $line 2 [expr {$i - 1}]]
-               }
+               set fname [string range $line 2 [expr {$i - 1}]]
            }
-           makediffhdr $fname $ids
+       }
+       makediffhdr $fname $ids
+
+    } elseif {![string compare -length 16 "* Unmerged path " $line]} {
+       set fname [encoding convertfrom [string range $line 16 end]]
+       $ctext insert end "\n"
+       set curdiffstart [$ctext index "end - 1c"]
+       lappend ctext_file_names $fname
+       lappend ctext_file_lines [lindex [split $curdiffstart "."] 0]
+       $ctext insert end "$line\n" filesep
+       set i [lsearch -exact $treediffs($ids) $fname]
+       if {$i >= 0} {
+           setinlist difffilestart $i $curdiffstart
+       }
+
+    } elseif {![string compare -length 2 "@@" $line]} {
+       regexp {^@@+} $line ats
+       set line [encoding convertfrom $diffencoding $line]
+       $ctext insert end "$line\n" hunksep
+       if {[regexp { \+(\d+),\d+ @@} $line m nl]} {
+           set diffline $nl
+       }
+       set diffnparents [expr {[string length $ats] - 1}]
+       set diffinhdr 0
 
-       } elseif {![string compare -length 16 "* Unmerged path " $line]} {
-           set fname [encoding convertfrom [string range $line 16 end]]
-           $ctext insert end "\n"
-           set curdiffstart [$ctext index "end - 1c"]
-           lappend ctext_file_names $fname
-           lappend ctext_file_lines [lindex [split $curdiffstart "."] 0]
+    } elseif {![string compare -length 10 "Submodule " $line]} {
+       # start of a new submodule
+       if {[regexp -indices "\[0-9a-f\]+\\.\\." $line nameend]} {
+           set fname [string range $line 10 [expr [lindex $nameend 0] - 2]]
+       } else {
+           set fname [string range $line 10 [expr [string first "contains " $line] - 2]]
+       }
+       if {$currdiffsubmod != $fname} {
+           $ctext insert end "\n";     # Add newline after commit message
+       }
+       set curdiffstart [$ctext index "end - 1c"]
+       lappend ctext_file_names ""
+       if {$currdiffsubmod != $fname} {
+           lappend ctext_file_lines $fname
+           makediffhdr $fname $ids
+           set currdiffsubmod $fname
+           $ctext insert end "\n$line\n" filesep
+       } else {
            $ctext insert end "$line\n" filesep
+       }
+    } elseif {![string compare -length 3 "  >" $line]} {
+       set $currdiffsubmod ""
+       set line [encoding convertfrom $diffencoding $line]
+       $ctext insert end "$line\n" dresult
+    } elseif {![string compare -length 3 "  <" $line]} {
+       set $currdiffsubmod ""
+       set line [encoding convertfrom $diffencoding $line]
+       $ctext insert end "$line\n" d0
+    } elseif {$diffinhdr} {
+       if {![string compare -length 12 "rename from " $line]} {
+           set fname [string range $line [expr 6 + [string first " from " $line] ] end]
+           if {[string index $fname 0] eq "\""} {
+               set fname [lindex $fname 0]
+           }
+           set fname [encoding convertfrom $fname]
            set i [lsearch -exact $treediffs($ids) $fname]
            if {$i >= 0} {
                setinlist difffilestart $i $curdiffstart
            }
-
-       } elseif {![string compare -length 2 "@@" $line]} {
-           regexp {^@@+} $line ats
-           set line [encoding convertfrom $diffencoding $line]
-           $ctext insert end "$line\n" hunksep
-           if {[regexp { \+(\d+),\d+ @@} $line m nl]} {
-               set diffline $nl
+       } elseif {![string compare -length 10 $line "rename to "] ||
+                 ![string compare -length 8 $line "copy to "]} {
+           set fname [string range $line [expr 4 + [string first " to " $line] ] end]
+           if {[string index $fname 0] eq "\""} {
+               set fname [lindex $fname 0]
            }
-           set diffnparents [expr {[string length $ats] - 1}]
+           makediffhdr $fname $ids
+       } elseif {[string compare -length 3 $line "---"] == 0} {
+           # do nothing
+           return
+       } elseif {[string compare -length 3 $line "+++"] == 0} {
            set diffinhdr 0
+           return
+       }
+       $ctext insert end "$line\n" filesep
 
-       } elseif {![string compare -length 10 "Submodule " $line]} {
-           # start of a new submodule
-           if {[regexp -indices "\[0-9a-f\]+\\.\\." $line nameend]} {
-               set fname [string range $line 10 [expr [lindex $nameend 0] - 2]]
-           } else {
-               set fname [string range $line 10 [expr [string first "contains " $line] - 2]]
-           }
-           if {$currdiffsubmod != $fname} {
-               $ctext insert end "\n";     # Add newline after commit message
-           }
-           set curdiffstart [$ctext index "end - 1c"]
-           lappend ctext_file_names ""
-           if {$currdiffsubmod != $fname} {
-               lappend ctext_file_lines $fname
-               makediffhdr $fname $ids
-               set currdiffsubmod $fname
-               $ctext insert end "\n$line\n" filesep
-           } else {
-               $ctext insert end "$line\n" filesep
-           }
-       } elseif {![string compare -length 3 "  >" $line]} {
-           set $currdiffsubmod ""
-           set line [encoding convertfrom $diffencoding $line]
-           $ctext insert end "$line\n" dresult
-       } elseif {![string compare -length 3 "  <" $line]} {
-           set $currdiffsubmod ""
-           set line [encoding convertfrom $diffencoding $line]
-           $ctext insert end "$line\n" d0
-       } elseif {$diffinhdr} {
-           if {![string compare -length 12 "rename from " $line]} {
-               set fname [string range $line [expr 6 + [string first " from " $line] ] end]
-               if {[string index $fname 0] eq "\""} {
-                   set fname [lindex $fname 0]
-               }
-               set fname [encoding convertfrom $fname]
-               set i [lsearch -exact $treediffs($ids) $fname]
-               if {$i >= 0} {
-                   setinlist difffilestart $i $curdiffstart
+    } else {
+       set line [string map {\x1A ^Z} \
+                     [encoding convertfrom $diffencoding $line]]
+       # parse the prefix - one ' ', '-' or '+' for each parent
+       set prefix [string range $line 0 [expr {$diffnparents - 1}]]
+       set tag [expr {$diffnparents > 1? "m": "d"}]
+       set dowords [expr {$worddiff ne [mc "Line diff"] && $diffnparents == 1}]
+       set words_pre_markup ""
+       set words_post_markup ""
+       if {[string trim $prefix " -+"] eq {}} {
+           # prefix only has " ", "-" and "+" in it: normal diff line
+           set num [string first "-" $prefix]
+           if {$dowords} {
+               set line [string range $line 1 end]
+           }
+           if {$num >= 0} {
+               # removed line, first parent with line is $num
+               if {$num >= $mergemax} {
+                   set num "max"
                }
-           } elseif {![string compare -length 10 $line "rename to "] ||
-                     ![string compare -length 8 $line "copy to "]} {
-               set fname [string range $line [expr 4 + [string first " to " $line] ] end]
-               if {[string index $fname 0] eq "\""} {
-                   set fname [lindex $fname 0]
+               if {$dowords && $worddiff eq [mc "Markup words"]} {
+                   $ctext insert end "\[-$line-\]" $tag$num
+               } else {
+                   $ctext insert end "$line" $tag$num
                }
-               makediffhdr $fname $ids
-           } elseif {[string compare -length 3 $line "---"] == 0} {
-               # do nothing
-               continue
-           } elseif {[string compare -length 3 $line "+++"] == 0} {
-               set diffinhdr 0
-               continue
-           }
-           $ctext insert end "$line\n" filesep
-
-       } else {
-           set line [string map {\x1A ^Z} \
-                          [encoding convertfrom $diffencoding $line]]
-           # parse the prefix - one ' ', '-' or '+' for each parent
-           set prefix [string range $line 0 [expr {$diffnparents - 1}]]
-           set tag [expr {$diffnparents > 1? "m": "d"}]
-           set dowords [expr {$worddiff ne [mc "Line diff"] && $diffnparents == 1}]
-           set words_pre_markup ""
-           set words_post_markup ""
-           if {[string trim $prefix " -+"] eq {}} {
-               # prefix only has " ", "-" and "+" in it: normal diff line
-               set num [string first "-" $prefix]
-               if {$dowords} {
-                   set line [string range $line 1 end]
+               if {!$dowords} {
+                   $ctext insert end "\n" $tag$num
                }
-               if {$num >= 0} {
-                   # removed line, first parent with line is $num
-                   if {$num >= $mergemax} {
-                       set num "max"
-                   }
-                   if {$dowords && $worddiff eq [mc "Markup words"]} {
-                       $ctext insert end "\[-$line-\]" $tag$num
-                   } else {
-                       $ctext insert end "$line" $tag$num
-                   }
-                   if {!$dowords} {
-                       $ctext insert end "\n" $tag$num
-                   }
-               } else {
-                   set tags {}
-                   if {[string first "+" $prefix] >= 0} {
-                       # added line
-                       lappend tags ${tag}result
-                       if {$diffnparents > 1} {
-                           set num [string first " " $prefix]
-                           if {$num >= 0} {
-                               if {$num >= $mergemax} {
-                                   set num "max"
-                               }
-                               lappend tags m$num
+           } else {
+               set tags {}
+               if {[string first "+" $prefix] >= 0} {
+                   # added line
+                   lappend tags ${tag}result
+                   if {$diffnparents > 1} {
+                       set num [string first " " $prefix]
+                       if {$num >= 0} {
+                           if {$num >= $mergemax} {
+                               set num "max"
                            }
-                       }
-                       set words_pre_markup "{+"
-                       set words_post_markup "+}"
-                   }
-                   if {$targetline ne {}} {
-                       if {$diffline == $targetline} {
-                           set seehere [$ctext index "end - 1 chars"]
-                           set targetline {}
-                       } else {
-                           incr diffline
+                           lappend tags m$num
                        }
                    }
-                   if {$dowords && $worddiff eq [mc "Markup words"]} {
-                       $ctext insert end "$words_pre_markup$line$words_post_markup" $tags
+                   set words_pre_markup "{+"
+                   set words_post_markup "+}"
+               }
+               if {$targetline ne {}} {
+                   if {$diffline == $targetline} {
+                       set diffseehere [$ctext index "end - 1 chars"]
+                       set targetline {}
                    } else {
-                       $ctext insert end "$line" $tags
-                   }
-                   if {!$dowords} {
-                       $ctext insert end "\n" $tags
+                       incr diffline
                    }
                }
-           } elseif {$dowords && $prefix eq "~"} {
-               $ctext insert end "\n" {}
-           } else {
-               # "\ No newline at end of file",
-               # or something else we don't recognize
-               $ctext insert end "$line\n" hunksep
+               if {$dowords && $worddiff eq [mc "Markup words"]} {
+                   $ctext insert end "$words_pre_markup$line$words_post_markup" $tags
+               } else {
+                   $ctext insert end "$line" $tags
+               }
+               if {!$dowords} {
+                   $ctext insert end "\n" $tags
+               }
            }
+       } elseif {$dowords && $prefix eq "~"} {
+           $ctext insert end "\n" {}
+       } else {
+           # "\ No newline at end of file",
+           # or something else we don't recognize
+           $ctext insert end "$line\n" hunksep
        }
     }
-    if {[info exists seehere]} {
-       mark_ctext_line [lindex [split $seehere .] 0]
-    }
-    maybe_scroll_ctext [eof $bdf]
-    $ctext conf -state disabled
-    if {[eof $bdf]} {
-       catch {close $bdf}
-       return 0
-    }
-    return [expr {$nr >= 1000? 2: 1}]
 }
 
 proc changediffdisp {} {
@@ -10878,6 +11022,23 @@ proc listrefs {id} {
     return [list $x $y $z]
 }
 
+proc add_tag_ctext {tag} {
+    global ctext cached_tagcontent tagids
+
+    if {![info exists cached_tagcontent($tag)]} {
+       catch {
+           set cached_tagcontent($tag) [exec git cat-file -p $tag]
+       }
+    }
+    $ctext insert end "[mc "Tag"]: $tag\n" bold
+    if {[info exists cached_tagcontent($tag)]} {
+       set text $cached_tagcontent($tag)
+    } else {
+       set text "[mc "Id"]:  $tagids($tag)"
+    }
+    appendwithlinks $text {}
+}
+
 proc showtag {tag isnew} {
     global ctext cached_tagcontent tagids linknum tagobjid
 
@@ -10888,17 +11049,28 @@ proc showtag {tag isnew} {
     clear_ctext
     settabs 0
     set linknum 0
-    if {![info exists cached_tagcontent($tag)]} {
-       catch {
-           set cached_tagcontent($tag) [exec git cat-file -p $tag]
-       }
+    add_tag_ctext $tag
+    maybe_scroll_ctext 1
+    $ctext conf -state disabled
+    init_flist {}
+}
+
+proc showtags {id isnew} {
+    global idtags ctext linknum
+
+    if {$isnew} {
+       addtohistory [list showtags $id 0] savectextpos
     }
-    if {[info exists cached_tagcontent($tag)]} {
-       set text $cached_tagcontent($tag)
-    } else {
-       set text "[mc "Tag"]: $tag\n[mc "Id"]:  $tagids($tag)"
+    $ctext conf -state normal
+    clear_ctext
+    settabs 0
+    set linknum 0
+    set sep {}
+    foreach tag $idtags($id) {
+       $ctext insert end $sep
+       add_tag_ctext $tag
+       set sep "\n\n"
     }
-    appendwithlinks $text {}
     maybe_scroll_ctext 1
     $ctext conf -state disabled
     init_flist {}
@@ -11915,7 +12087,29 @@ namespace import ::msgcat::mc
 ## And eventually load the actual message catalog
 ::msgcat::mcload $gitk_msgsdir
 
-catch {source ~/.gitk}
+catch {
+    # follow the XDG base directory specification by default. See
+    # http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
+    if {[info exists env(XDG_CONFIG_HOME)] && $env(XDG_CONFIG_HOME) ne ""} {
+       # XDG_CONFIG_HOME environment variable is set
+       set config_file [file join $env(XDG_CONFIG_HOME) git gitk]
+       set config_file_tmp [file join $env(XDG_CONFIG_HOME) git gitk-tmp]
+    } else {
+       # default XDG_CONFIG_HOME
+       set config_file "~/.config/git/gitk"
+       set config_file_tmp "~/.config/git/gitk-tmp"
+    }
+    if {![file exists $config_file]} {
+       # for backward compatibility use the old config file if it exists
+       if {[file exists "~/.gitk"]} {
+           set config_file "~/.gitk"
+           set config_file_tmp "~/.gitk-tmp"
+       } elseif {![file exists [file dirname $config_file]]} {
+           file mkdir [file dirname $config_file]
+       }
+    }
+    source $config_file
+}
 
 parsefont mainfont $mainfont
 eval font create mainfont [fontflags mainfont]
diff --git a/gitk-git/po/bg.po b/gitk-git/po/bg.po
new file mode 100644 (file)
index 0000000..782397e
--- /dev/null
@@ -0,0 +1,1334 @@
+# Bulgarian translation of gitk po-file.
+# Copyright (C) 2014 Alexander Shopov <ash@kambanaria.org>.
+# This file is distributed under the same license as the git package.
+# Alexander Shopov <ash@kambanaria.org>, 2014.
+#
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: gitk master\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-01-26 15:47-0800\n"
+"PO-Revision-Date: 2014-01-08 08:03+0200\n"
+"Last-Translator: Alexander Shopov <ash@kambanaria.org>\n"
+"Language-Team: Bulgarian <dict@fsa-bg.org>\n"
+"Language: bg\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"
+
+#: gitk:140
+msgid "Couldn't get list of unmerged files:"
+msgstr "Списъкът с неслети файлове не може да бъде получен:"
+
+#: gitk:212 gitk:2353
+msgid "Color words"
+msgstr "Оцветяване на думите"
+
+#: gitk:217 gitk:2353 gitk:8103 gitk:8136
+msgid "Markup words"
+msgstr "Отбелязване на думите"
+
+#: gitk:322
+msgid "Error parsing revisions:"
+msgstr "Грешка при разбор на версиите:"
+
+#: gitk:378
+msgid "Error executing --argscmd command:"
+msgstr "Грешка при изпълнение на командата с „--argscmd“."
+
+#: gitk:391
+msgid "No files selected: --merge specified but no files are unmerged."
+msgstr "Не са избрани файлове — указана е опцията „--merge“, но няма неслети файлове."
+
+#: gitk:394
+msgid ""
+"No files selected: --merge specified but no unmerged files are within file "
+"limit."
+msgstr "Не са избрани файлове — указана е опцията „--merge“, но няма неслети файлове в ограниченията."
+
+#: gitk:416 gitk:564
+msgid "Error executing git log:"
+msgstr "Грешка при изпълнение на „git log“:"
+
+#: gitk:434 gitk:580
+msgid "Reading"
+msgstr "Прочитане"
+
+#: gitk:494 gitk:4429
+msgid "Reading commits..."
+msgstr "Прочитане на подаванията…"
+
+#: gitk:497 gitk:1635 gitk:4432
+msgid "No commits selected"
+msgstr "Не са избрани подавания"
+
+#: gitk:1509
+msgid "Can't parse git log output:"
+msgstr "Изходът от „git log“ не може да се анализира:"
+
+#: gitk:1738
+msgid "No commit information available"
+msgstr "Липсва информация за подавания"
+
+#: gitk:1895
+msgid "mc"
+msgstr "mc"
+
+#: gitk:1930 gitk:4222 gitk:9552 gitk:11122 gitk:11401
+msgid "OK"
+msgstr "Добре"
+
+#: gitk:1932 gitk:4224 gitk:9079 gitk:9158 gitk:9274 gitk:9323 gitk:9554
+#: gitk:11123 gitk:11402
+msgid "Cancel"
+msgstr "Отказ"
+
+#: gitk:2067
+msgid "Update"
+msgstr "Обновяване"
+
+#: gitk:2068
+msgid "Reload"
+msgstr "Презареждане"
+
+#: gitk:2069
+msgid "Reread references"
+msgstr "Наново прочитане на настройките"
+
+#: gitk:2070
+msgid "List references"
+msgstr "Изброяване на указателите"
+
+#: gitk:2072
+msgid "Start git gui"
+msgstr "Стартиране на git gui"
+
+#: gitk:2074
+msgid "Quit"
+msgstr "Спиране на програмата"
+
+#: gitk:2066
+msgid "File"
+msgstr "Файл"
+
+#: gitk:2078
+msgid "Preferences"
+msgstr "Настройки"
+
+#: gitk:2077
+msgid "Edit"
+msgstr "Редактиране"
+
+#: gitk:2082
+msgid "New view..."
+msgstr "Нов изглед…"
+
+#: gitk:2083
+msgid "Edit view..."
+msgstr "Редактиране на изгледа…"
+
+#: gitk:2084
+msgid "Delete view"
+msgstr "Изтриване на изгледа"
+
+#: gitk:2086
+msgid "All files"
+msgstr "Всички файлове"
+
+#: gitk:2081 gitk:3975
+msgid "View"
+msgstr "Изглед"
+
+#: gitk:2091 gitk:2101 gitk:2945
+msgid "About gitk"
+msgstr "Относно gitk"
+
+#: gitk:2092 gitk:2106
+msgid "Key bindings"
+msgstr "Клавишни комбинации"
+
+#: gitk:2090 gitk:2105
+msgid "Help"
+msgstr "Помощ"
+
+#: gitk:2183 gitk:8535
+msgid "SHA1 ID:"
+msgstr "SHA1:"
+
+#: gitk:2227
+msgid "Row"
+msgstr "Ред"
+
+#: gitk:2265
+msgid "Find"
+msgstr "Търсене"
+
+#: gitk:2266
+msgid "next"
+msgstr "следващо"
+
+#: gitk:2267
+msgid "prev"
+msgstr "предишно"
+
+#: gitk:2268
+msgid "commit"
+msgstr "подаване"
+
+#: gitk:2271 gitk:2273 gitk:4590 gitk:4613 gitk:4637 gitk:6653 gitk:6725
+#: gitk:6810
+msgid "containing:"
+msgstr "съдържащо:"
+
+#: gitk:2274 gitk:3457 gitk:3462 gitk:4666
+msgid "touching paths:"
+msgstr "засягащо пътищата:"
+
+#: gitk:2275 gitk:4680
+msgid "adding/removing string:"
+msgstr "добавящо/премахващо низ"
+
+#: gitk:2276 gitk:4682
+msgid "changing lines matching:"
+msgstr "променящо редове напасващи:"
+
+#: gitk:2285 gitk:2287 gitk:4669
+msgid "Exact"
+msgstr "Точно"
+
+#: gitk:2287 gitk:4757 gitk:6621
+msgid "IgnCase"
+msgstr "Без регистър"
+
+#: gitk:2287 gitk:4639 gitk:4755 gitk:6617
+msgid "Regexp"
+msgstr "Рег. изр."
+
+#: gitk:2289 gitk:2290 gitk:4777 gitk:4807 gitk:4814 gitk:6746 gitk:6814
+msgid "All fields"
+msgstr "Всички полета"
+
+#: gitk:2290 gitk:4774 gitk:4807 gitk:6684
+msgid "Headline"
+msgstr "Първи ред"
+
+#: gitk:2291 gitk:4774 gitk:6684 gitk:6814 gitk:7283
+msgid "Comments"
+msgstr "Коментари"
+
+#: gitk:2291 gitk:4774 gitk:4779 gitk:4814 gitk:6684 gitk:7218 gitk:8713
+#: gitk:8728
+msgid "Author"
+msgstr "Автор"
+
+#: gitk:2291 gitk:4774 gitk:6684 gitk:7220
+msgid "Committer"
+msgstr "Подаващ"
+
+#: gitk:2322
+msgid "Search"
+msgstr "Търсене"
+
+#: gitk:2330
+msgid "Diff"
+msgstr "Разлики"
+
+#: gitk:2332
+msgid "Old version"
+msgstr "Стара версия"
+
+#: gitk:2334
+msgid "New version"
+msgstr "Нова версия"
+
+#: gitk:2336
+msgid "Lines of context"
+msgstr "Контекст в редове"
+
+#: gitk:2346
+msgid "Ignore space change"
+msgstr "Празните знаци без значение"
+
+#: gitk:2350 gitk:2352 gitk:7842 gitk:8089
+msgid "Line diff"
+msgstr "Поредови разлики"
+
+#: gitk:2417
+msgid "Patch"
+msgstr "Кръпка"
+
+#: gitk:2419
+msgid "Tree"
+msgstr "Дърво"
+
+#: gitk:2577 gitk:2597
+msgid "Diff this -> selected"
+msgstr "Разлики между това и избраното"
+
+#: gitk:2578 gitk:2598
+msgid "Diff selected -> this"
+msgstr "Разлики между избраното и това"
+
+#: gitk:2579 gitk:2599
+msgid "Make patch"
+msgstr "Създаване на кръпка"
+
+#: gitk:2580 gitk:9137
+msgid "Create tag"
+msgstr "Създаване на етикет"
+
+#: gitk:2581 gitk:9254
+msgid "Write commit to file"
+msgstr "Запазване на подаването във файл"
+
+#: gitk:2582 gitk:9311
+msgid "Create new branch"
+msgstr "Създаване на нов клон"
+
+#: gitk:2583
+msgid "Cherry-pick this commit"
+msgstr "Отбиране на това подаване"
+
+#: gitk:2584
+msgid "Reset HEAD branch to here"
+msgstr "Привеждане на върха на клона към текущото подаване"
+
+#: gitk:2585
+msgid "Mark this commit"
+msgstr "Отбелязване на това подаване"
+
+#: gitk:2586
+msgid "Return to mark"
+msgstr "Връщане към отбелязаното подаване"
+
+#: gitk:2587
+msgid "Find descendant of this and mark"
+msgstr "Откриване и отбелязване на наследниците"
+
+#: gitk:2588
+msgid "Compare with marked commit"
+msgstr "Сравнение с отбелязаното подаване"
+
+#: gitk:2589 gitk:2600
+msgid "Diff this -> marked commit"
+msgstr "Разлики между това и отбелязаното"
+
+#: gitk:2590 gitk:2601
+msgid "Diff marked commit -> this"
+msgstr "Разлики между отбелязаното и това"
+
+#: gitk:2591
+msgid "Revert this commit"
+msgstr "Отмяна на това подаване"
+
+#: gitk:2607
+msgid "Check out this branch"
+msgstr "Изтегляне на този клон"
+
+#: gitk:2608
+msgid "Remove this branch"
+msgstr "Изтриване на този клон"
+
+#: gitk:2615
+msgid "Highlight this too"
+msgstr "Отбелязване и на това"
+
+#: gitk:2616
+msgid "Highlight this only"
+msgstr "Отбелязване само на това"
+
+#: gitk:2617
+msgid "External diff"
+msgstr "Външна програма за разлики"
+
+#: gitk:2618
+msgid "Blame parent commit"
+msgstr "Анотиране на родителското подаване"
+
+#: gitk:2625
+msgid "Show origin of this line"
+msgstr "Показване на произхода на този ред"
+
+#: gitk:2626
+msgid "Run git gui blame on this line"
+msgstr "Изпълнение на „git gui blame“ върху този ред"
+
+#: gitk:2947
+msgid ""
+"\n"
+"Gitk - a commit viewer for git\n"
+"\n"
+"Copyright © 2005-2011 Paul Mackerras\n"
+"\n"
+"Use and redistribute under the terms of the GNU General Public License"
+msgstr ""
+"\n"
+"Gitk — визуализация на подаванията в Git\n"
+"\n"
+"Авторски права: © 2005-2011 Paul Mackerras\n"
+"\n"
+"Използвайте и разпространявайте при условията на ОПЛ на ГНУ"
+
+#: gitk:2955 gitk:3020 gitk:9738
+msgid "Close"
+msgstr "Затваряне"
+
+#: gitk:2976
+msgid "Gitk key bindings"
+msgstr "Клавишни комбинации"
+
+#: gitk:2979
+msgid "Gitk key bindings:"
+msgstr "Клавишни комбинации:"
+
+#: gitk:2981
+#, tcl-format
+msgid "<%s-Q>\t\tQuit"
+msgstr "<%s-Q>\t\tСпиране на програмата"
+
+#: gitk:2982
+#, tcl-format
+msgid "<%s-W>\t\tClose window"
+msgstr "<%s-W>\t\tЗатваряне на прозореца"
+
+#: gitk:2983
+msgid "<Home>\t\tMove to first commit"
+msgstr "<Home>\t\tКъм първото подаване"
+
+#: gitk:2984
+msgid "<End>\t\tMove to last commit"
+msgstr "<End>\t\tКъм последното подаване"
+
+#: gitk:2985
+msgid "<Up>, p, k\tMove up one commit"
+msgstr "<Up>, p, k\tЕдно подаване нагоре"
+
+#: gitk:2986
+msgid "<Down>, n, j\tMove down one commit"
+msgstr "<Down>, n, j\tЕдно подаване надолу"
+
+#: gitk:2987
+msgid "<Left>, z, h\tGo back in history list"
+msgstr "<Left>, z, h\tНазад в историята"
+
+#: gitk:2988
+msgid "<Right>, x, l\tGo forward in history list"
+msgstr "<Right>, x, l\tНапред в историята"
+
+#: gitk:2989
+msgid "<PageUp>\tMove up one page in commit list"
+msgstr "<PageUp>\tЕдна страница нагоре в списъка с подаванията"
+
+#: gitk:2990
+msgid "<PageDown>\tMove down one page in commit list"
+msgstr "<PageDown>\tЕдна страница надолу в списъка с подаванията"
+
+#: gitk:2991
+#, tcl-format
+msgid "<%s-Home>\tScroll to top of commit list"
+msgstr "<%s-Home>\tКъм началото на списъка с подаванията"
+
+#: gitk:2992
+#, tcl-format
+msgid "<%s-End>\tScroll to bottom of commit list"
+msgstr "<%s-End>\tКъм края на списъка с подаванията"
+
+#: gitk:2993
+#, tcl-format
+msgid "<%s-Up>\tScroll commit list up one line"
+msgstr "<%s-Up>\tПридвижване на списъка с подавания с един ред нагоре"
+
+#: gitk:2994
+#, tcl-format
+msgid "<%s-Down>\tScroll commit list down one line"
+msgstr "<%s-Down>\tПридвижване на списъка с подавания с един ред надолу"
+
+#: gitk:2995
+#, tcl-format
+msgid "<%s-PageUp>\tScroll commit list up one page"
+msgstr "<%s-PageUp>\tПридвижване на списъка с подавания с една страница нагоре"
+
+#: gitk:2996
+#, tcl-format
+msgid "<%s-PageDown>\tScroll commit list down one page"
+msgstr "<%s-PageDown>\tПридвижване на списъка с подавания с една страница надолу"
+
+#: gitk:2997
+msgid "<Shift-Up>\tFind backwards (upwards, later commits)"
+msgstr "<Shift-Up>\tТърсене назад (визуално нагоре, исторически — последващи)"
+
+#: gitk:2998
+msgid "<Shift-Down>\tFind forwards (downwards, earlier commits)"
+msgstr "<Shift-Down>\tТърсене напред (визуално надолу, исторически — предхождащи)"
+
+#: gitk:2999
+msgid "<Delete>, b\tScroll diff view up one page"
+msgstr "<Delete>, b\tПридвижване на изгледа за разлики една страница нагоре"
+
+#: gitk:3000
+msgid "<Backspace>\tScroll diff view up one page"
+msgstr "<Backspace>\tПридвижване на изгледа за разлики една страница нагоре"
+
+#: gitk:3001
+msgid "<Space>\t\tScroll diff view down one page"
+msgstr "<Space>\t\tПридвижване на изгледа за разлики една страница надолу"
+
+#: gitk:3002
+msgid "u\t\tScroll diff view up 18 lines"
+msgstr "u\t\tПридвижване на изгледа за разлики 18 реда нагоре"
+
+#: gitk:3003
+msgid "d\t\tScroll diff view down 18 lines"
+msgstr "d\t\tПридвижване на изгледа за разлики 18 реда надолу"
+
+#: gitk:3004
+#, tcl-format
+msgid "<%s-F>\t\tFind"
+msgstr "<%s-F>\t\tТърсене"
+
+#: gitk:3005
+#, tcl-format
+msgid "<%s-G>\t\tMove to next find hit"
+msgstr "<%s-G>\t\tКъм следващата поява"
+
+#: gitk:3006
+msgid "<Return>\tMove to next find hit"
+msgstr "<Return>\tКъм следващата поява"
+
+#: gitk:3007
+msgid "/\t\tFocus the search box"
+msgstr "/\t\tФокус върху полето за търсене"
+
+#: gitk:3008
+msgid "?\t\tMove to previous find hit"
+msgstr "?\t\tКъм предишната поява"
+
+#: gitk:3009
+msgid "f\t\tScroll diff view to next file"
+msgstr "f\t\tПридвижване на изгледа за разлики към следващия ред"
+
+#: gitk:3010
+#, tcl-format
+msgid "<%s-S>\t\tSearch for next hit in diff view"
+msgstr "<%s-S>\t\tТърсене на следващата поява в изгледа за разлики"
+
+#: gitk:3011
+#, tcl-format
+msgid "<%s-R>\t\tSearch for previous hit in diff view"
+msgstr "<%s-R>\t\tТърсене на предишната поява в изгледа за разлики"
+
+#: gitk:3012
+#, tcl-format
+msgid "<%s-KP+>\tIncrease font size"
+msgstr "<%s-KP+>\tПо-голям размер на шрифта"
+
+#: gitk:3013
+#, tcl-format
+msgid "<%s-plus>\tIncrease font size"
+msgstr "<%s-plus>\tПо-голям размер на шрифта"
+
+#: gitk:3014
+#, tcl-format
+msgid "<%s-KP->\tDecrease font size"
+msgstr "<%s-KP->\tПо-малък размер на шрифта"
+
+#: gitk:3015
+#, tcl-format
+msgid "<%s-minus>\tDecrease font size"
+msgstr "<%s-minus>\tПо-малък размер на шрифта"
+
+#: gitk:3016
+msgid "<F5>\t\tUpdate"
+msgstr "<F5>\t\tОбновяване"
+
+#: gitk:3471 gitk:3480
+#, tcl-format
+msgid "Error creating temporary directory %s:"
+msgstr "Грешка при създаването на временната директория „%s“:"
+
+#: gitk:3493
+#, tcl-format
+msgid "Error getting \"%s\" from %s:"
+msgstr "Грешка при получаването на „%s“ от %s:"
+
+#: gitk:3556
+msgid "command failed:"
+msgstr "неуспешно изпълнение на команда:"
+
+#: gitk:3705
+msgid "No such commit"
+msgstr "Такова подаване няма"
+
+#: gitk:3719
+msgid "git gui blame: command failed:"
+msgstr "git gui blame: неуспешно изпълнение на команда:"
+
+#: gitk:3750
+#, tcl-format
+msgid "Couldn't read merge head: %s"
+msgstr "Върхът за сливане не може да бъде прочетен: %s"
+
+#: gitk:3758
+#, tcl-format
+msgid "Error reading index: %s"
+msgstr "Грешка при прочитане на индекса: %s"
+
+#: gitk:3783
+#, tcl-format
+msgid "Couldn't start git blame: %s"
+msgstr "Командата „git blame“ не може да бъде стартирана: %s"
+
+#: gitk:3786 gitk:6652
+msgid "Searching"
+msgstr "Търсене"
+
+#: gitk:3818
+#, tcl-format
+msgid "Error running git blame: %s"
+msgstr "Грешка при изпълнението на „git blame“: %s"
+
+#: gitk:3846
+#, tcl-format
+msgid "That line comes from commit %s,  which is not in this view"
+msgstr "Този ред идва от подаването %s,  което не е в изгледа"
+
+#: gitk:3860
+msgid "External diff viewer failed:"
+msgstr "Неуспешно изпълнение на външната програма за разлики:"
+
+#: gitk:3978
+msgid "Gitk view definition"
+msgstr "Дефиниция на изглед в Gitk"
+
+#: gitk:3982
+msgid "Remember this view"
+msgstr "Запазване на този изглед"
+
+#: gitk:3983
+msgid "References (space separated list):"
+msgstr "Указатели (списък с разделител интервал):"
+
+#: gitk:3984
+msgid "Branches & tags:"
+msgstr "Клони и етикети:"
+
+#: gitk:3985
+msgid "All refs"
+msgstr "Всички указатели"
+
+#: gitk:3986
+msgid "All (local) branches"
+msgstr "Всички (локални) клони"
+
+#: gitk:3987
+msgid "All tags"
+msgstr "Всички етикети"
+
+#: gitk:3988
+msgid "All remote-tracking branches"
+msgstr "Всички следящи клони"
+
+#: gitk:3989
+msgid "Commit Info (regular expressions):"
+msgstr "Информация за подаване (рег. изр.):"
+
+#: gitk:3990
+msgid "Author:"
+msgstr "Автор:"
+
+#: gitk:3991
+msgid "Committer:"
+msgstr "Подал:"
+
+#: gitk:3992
+msgid "Commit Message:"
+msgstr "Съобщение при подаване:"
+
+#: gitk:3993
+msgid "Matches all Commit Info criteria"
+msgstr "Съвпадение по коя да е информация за подаването"
+
+#: gitk:3994
+msgid "Changes to Files:"
+msgstr "Промени по файловете:"
+
+#: gitk:3995
+msgid "Fixed String"
+msgstr "Дословен низ"
+
+#: gitk:3996
+msgid "Regular Expression"
+msgstr "Регулярен израз"
+
+#: gitk:3997
+msgid "Search string:"
+msgstr "Низ за търсене:"
+
+#: gitk:3998
+msgid ""
+"Commit Dates (\"2 weeks ago\", \"2009-03-17 15:27:38\", \"March 17, 2009 "
+"15:27:38\"):"
+msgstr "Дата на подаване („2 weeks ago“ (преди 2 седмици), „2009-03-17 15:27:38“, „March 17, 2009 15:27:38“):"
+
+#: gitk:3999
+msgid "Since:"
+msgstr "От:"
+
+#: gitk:4000
+msgid "Until:"
+msgstr "До:"
+
+#: gitk:4001
+msgid "Limit and/or skip a number of revisions (positive integer):"
+msgstr "Ограничаване и/или прескачане на определен брой версии (неотрицателно цяло число):"
+
+#: gitk:4002
+msgid "Number to show:"
+msgstr "Брой показани:"
+
+#: gitk:4003
+msgid "Number to skip:"
+msgstr "Брой прескочени:"
+
+#: gitk:4004
+msgid "Miscellaneous options:"
+msgstr "Разни:"
+
+#: gitk:4005
+msgid "Strictly sort by date"
+msgstr "Подреждане по дата"
+
+#: gitk:4006
+msgid "Mark branch sides"
+msgstr "Отбелязване на страните по клона"
+
+#: gitk:4007
+msgid "Limit to first parent"
+msgstr "Само първият родител"
+
+#: gitk:4008
+msgid "Simple history"
+msgstr "Опростена история"
+
+#: gitk:4009
+msgid "Additional arguments to git log:"
+msgstr "Допълнителни аргументи към „git log“:"
+
+#: gitk:4010
+msgid "Enter files and directories to include, one per line:"
+msgstr "Въведете файловете и директориите за включване, по елемент на ред"
+
+#: gitk:4011
+msgid "Command to generate more commits to include:"
+msgstr "Команда за генерирането на допълнителни подавания, които да бъдат включени:"
+
+#: gitk:4135
+msgid "Gitk: edit view"
+msgstr "Gitk: редактиране на изглед"
+
+#: gitk:4143
+msgid "-- criteria for selecting revisions"
+msgstr "— критерии за избор на версии"
+
+#: gitk:4148
+msgid "View Name"
+msgstr "Име на изглед"
+
+#: gitk:4223
+msgid "Apply (F5)"
+msgstr "Прилагане (F5)"
+
+#: gitk:4261
+msgid "Error in commit selection arguments:"
+msgstr "Грешка в аргументите за избор на подавания:"
+
+#: gitk:4314 gitk:4366 gitk:4827 gitk:4841 gitk:6107 gitk:12184 gitk:12185
+msgid "None"
+msgstr "Няма"
+
+#: gitk:4924 gitk:4929
+msgid "Descendant"
+msgstr "Наследник"
+
+#: gitk:4925
+msgid "Not descendant"
+msgstr "Не е наследник"
+
+#: gitk:4932 gitk:4937
+msgid "Ancestor"
+msgstr "Предшественик"
+
+#: gitk:4933
+msgid "Not ancestor"
+msgstr "Не е предшественик"
+
+#: gitk:5223
+msgid "Local changes checked in to index but not committed"
+msgstr "Локални промени добавени към индекса, но неподадени"
+
+#: gitk:5259
+msgid "Local uncommitted changes, not checked in to index"
+msgstr "Локални промени извън индекса"
+
+#: gitk:7032
+msgid "and many more"
+msgstr "и още много"
+
+#: gitk:7035
+msgid "many"
+msgstr "много"
+
+#: gitk:7222
+msgid "Tags:"
+msgstr "Етикети:"
+
+#: gitk:7239 gitk:7245 gitk:8708
+msgid "Parent"
+msgstr "Родител"
+
+#: gitk:7250
+msgid "Child"
+msgstr "Дете"
+
+#: gitk:7259
+msgid "Branch"
+msgstr "Клон"
+
+#: gitk:7262
+msgid "Follows"
+msgstr "Следва"
+
+#: gitk:7265
+msgid "Precedes"
+msgstr "Предшества"
+
+#: gitk:7849
+#, tcl-format
+msgid "Error getting diffs: %s"
+msgstr "Грешка при получаването на разликите: %s"
+
+#: gitk:8533
+msgid "Goto:"
+msgstr "Към ред:"
+
+#: gitk:8554
+#, tcl-format
+msgid "Short SHA1 id %s is ambiguous"
+msgstr "Съкратената SHA1 %s не е еднозначна"
+
+#: gitk:8561
+#, tcl-format
+msgid "Revision %s is not known"
+msgstr "Непозната версия %s"
+
+#: gitk:8571
+#, tcl-format
+msgid "SHA1 id %s is not known"
+msgstr "Непозната SHA1 %s"
+
+#: gitk:8573
+#, tcl-format
+msgid "Revision %s is not in the current view"
+msgstr "Версия %s не е в текущия изглед"
+
+#: gitk:8715 gitk:8730
+msgid "Date"
+msgstr "Дата"
+
+#: gitk:8718
+msgid "Children"
+msgstr "Деца"
+
+#: gitk:8781
+#, tcl-format
+msgid "Reset %s branch to here"
+msgstr "Зануляване на клона „%s“ към текущото подаване"
+
+#: gitk:8783
+msgid "Detached head: can't reset"
+msgstr "Несвързан връх: невъзможно зануляване"
+
+#: gitk:8888 gitk:8894
+msgid "Skipping merge commit "
+msgstr "Пропускане на подаването на сливането"
+
+#: gitk:8903 gitk:8908
+msgid "Error getting patch ID for "
+msgstr "Грешка при получаването на идентификатора на "
+
+#: gitk:8904 gitk:8909
+msgid " - stopping\n"
+msgstr " — спиране\n"
+
+#: gitk:8914 gitk:8917 gitk:8925 gitk:8939 gitk:8948
+msgid "Commit "
+msgstr "Подаване"
+
+#: gitk:8918
+msgid ""
+" is the same patch as\n"
+"       "
+msgstr ""
+" е същата кръпка като\n"
+"       "
+
+#: gitk:8926
+msgid ""
+" differs from\n"
+"       "
+msgstr ""
+" се различава от\n"
+"       "
+
+#: gitk:8928
+msgid ""
+"Diff of commits:\n"
+"\n"
+msgstr "Разлика между подаванията:\n\n"
+
+#: gitk:8940 gitk:8949
+#, tcl-format
+msgid " has %s children - stopping\n"
+msgstr " има %s деца — спиране\n"
+
+#: gitk:8968
+#, tcl-format
+msgid "Error writing commit to file: %s"
+msgstr "Грешка при запазването на подаването във файл: %s"
+
+#: gitk:8974
+#, tcl-format
+msgid "Error diffing commits: %s"
+msgstr "Грешка при изчисляването на разликите между подаванията: %s"
+
+#: gitk:9020
+msgid "Top"
+msgstr "Най-горе"
+
+#: gitk:9021
+msgid "From"
+msgstr "От"
+
+#: gitk:9026
+msgid "To"
+msgstr "До"
+
+#: gitk:9050
+msgid "Generate patch"
+msgstr "Генериране на кръпка"
+
+#: gitk:9052
+msgid "From:"
+msgstr "От:"
+
+#: gitk:9061
+msgid "To:"
+msgstr "До:"
+
+#: gitk:9070
+msgid "Reverse"
+msgstr "Обръщане"
+
+#: gitk:9072 gitk:9268
+msgid "Output file:"
+msgstr "Запазване във файла:"
+
+#: gitk:9078
+msgid "Generate"
+msgstr "Генериране"
+
+#: gitk:9116
+msgid "Error creating patch:"
+msgstr "Грешка при създаването на кръпка:"
+
+#: gitk:9139 gitk:9256 gitk:9313
+msgid "ID:"
+msgstr "Идентификатор:"
+
+#: gitk:9148
+msgid "Tag name:"
+msgstr "Име на етикет:"
+
+#: gitk:9151
+msgid "Tag message is optional"
+msgstr "Съобщението за етикет е незадължително"
+
+#: gitk:9153
+msgid "Tag message:"
+msgstr "Съобщение за етикет:"
+
+#: gitk:9157 gitk:9322
+msgid "Create"
+msgstr "Създаване"
+
+#: gitk:9175
+msgid "No tag name specified"
+msgstr "Липсва име на етикет"
+
+#: gitk:9179
+#, tcl-format
+msgid "Tag \"%s\" already exists"
+msgstr "Етикетът „%s“ вече съществува"
+
+#: gitk:9189
+msgid "Error creating tag:"
+msgstr "Грешка при създаването на етикет:"
+
+#: gitk:9265
+msgid "Command:"
+msgstr "Команда:"
+
+#: gitk:9273
+msgid "Write"
+msgstr "Pdmdpldke"
+
+#: gitk:9291
+msgid "Error writing commit:"
+msgstr "Грешка при запазването на подаването:"
+
+#: gitk:9318
+msgid "Name:"
+msgstr "Име:"
+
+#: gitk:9341
+msgid "Please specify a name for the new branch"
+msgstr "Укажете име за новия клон"
+
+#: gitk:9346
+#, tcl-format
+msgid "Branch '%s' already exists. Overwrite?"
+msgstr "Клонът „%s“ вече съществува. Да бъде ли презаписан?"
+
+#: gitk:9413
+#, tcl-format
+msgid "Commit %s is already included in branch %s -- really re-apply it?"
+msgstr "Подаването „%s“ вече е включено в клона „%s“ — да бъде ли приложено отново?"
+
+#: gitk:9418
+msgid "Cherry-picking"
+msgstr "Отбиране"
+
+#: gitk:9427
+#, tcl-format
+msgid ""
+"Cherry-pick failed because of local changes to file '%s'.\n"
+"Please commit, reset or stash your changes and try again."
+msgstr ""
+"Неуспешно отбиране, защото във файла „%s“ има локални промени.\n"
+"Подайте, занулете или ги скатайте и пробвайте отново."
+
+#: gitk:9433
+msgid ""
+"Cherry-pick failed because of merge conflict.\n"
+"Do you wish to run git citool to resolve it?"
+msgstr ""
+"Неуспешно отбиране поради конфликти при сливане.\n"
+"Искате ли да ги коригирате чрез „git citool“?"
+
+#: gitk:9449 gitk:9507
+msgid "No changes committed"
+msgstr "Не са подадени промени"
+
+#: gitk:9476
+#, tcl-format
+msgid "Commit %s is not included in branch %s -- really revert it?"
+msgstr "Подаването „%s“ не е включено в клона „%s“. Да бъде ли отменено?"
+
+#: gitk:9481
+msgid "Reverting"
+msgstr "Отмяна"
+
+#: gitk:9489
+#, tcl-format
+msgid ""
+"Revert failed because of local changes to the following files:%s Please "
+"commit, reset or stash  your changes and try again."
+msgstr ""
+"Неуспешна отмяна, защото във файла „%s“ има локални промени.\n"
+"Подайте, занулете или ги скатайте и пробвайте отново.<"
+
+#: gitk:9493
+msgid ""
+"Revert failed because of merge conflict.\n"
+" Do you wish to run git citool to resolve it?"
+msgstr ""
+"Неуспешно отмяна поради конфликти при сливане.\n"
+"Искате ли да ги коригирате чрез „git citool“?"
+
+#: gitk:9536
+msgid "Confirm reset"
+msgstr "Потвърждаване на зануляването"
+
+#: gitk:9538
+#, tcl-format
+msgid "Reset branch %s to %s?"
+msgstr "Да се занули ли клона „%s“ към „%s“?"
+
+#: gitk:9540
+msgid "Reset type:"
+msgstr "Вид зануляване:"
+
+#: gitk:9543
+msgid "Soft: Leave working tree and index untouched"
+msgstr "Слабо: работното дърво и индекса остават същите"
+
+#: gitk:9546
+msgid "Mixed: Leave working tree untouched, reset index"
+msgstr "Смесено: работното дърво остава същото, индексът се занулява"
+
+#: gitk:9549
+msgid ""
+"Hard: Reset working tree and index\n"
+"(discard ALL local changes)"
+msgstr ""
+"Силно: зануляване и на работното дърво, и на индекса\n"
+"(*ВСИЧКИ* локални промени ще бъдат безвъзвратно загубени)"
+
+#: gitk:9566
+msgid "Resetting"
+msgstr "Зануляване"
+
+#: gitk:9626
+msgid "Checking out"
+msgstr "Изтегляне"
+
+#: gitk:9679
+msgid "Cannot delete the currently checked-out branch"
+msgstr "Текущо изтегленият клон не може да бъде изтрит"
+
+#: gitk:9685
+#, tcl-format
+msgid ""
+"The commits on branch %s aren't on any other branch.\n"
+"Really delete branch %s?"
+msgstr ""
+"Подаванията на клона „%s“ не са на никой друг клон.\n"
+"Наистина ли да се изтрие клона „%s“?"
+
+#: gitk:9716
+#, tcl-format
+msgid "Tags and heads: %s"
+msgstr "Етикети и върхове: %s"
+
+#: gitk:9731
+msgid "Filter"
+msgstr "Филтриране"
+
+#: gitk:10027
+msgid ""
+"Error reading commit topology information; branch and preceding/following "
+"tag information will be incomplete."
+msgstr "Грешка при прочитането на топологията на подаванията. Информацията за клона и предшестващите/следващите етикети ще е непълна."
+
+#: gitk:11004
+msgid "Tag"
+msgstr "Етикет"
+
+#: gitk:11008
+msgid "Id"
+msgstr "Идентификатор"
+
+#: gitk:11091
+msgid "Gitk font chooser"
+msgstr "Избор на шрифт за Gitk"
+
+#: gitk:11108
+msgid "B"
+msgstr "Ч"
+
+#: gitk:11111
+msgid "I"
+msgstr "К"
+
+#: gitk:11229
+msgid "Commit list display options"
+msgstr "Настройки на списъка с подавания"
+
+#: gitk:11232
+msgid "Maximum graph width (lines)"
+msgstr "Максимална широчина на графа (в редове)"
+
+#: gitk:11235
+#, tcl-format
+msgid "Maximum graph width (% of pane)"
+msgstr "Максимална широчина на графа (% от панела)"
+
+#: gitk:11238
+msgid "Show local changes"
+msgstr "Показване на локалните промени"
+
+#: gitk:11241
+msgid "Auto-select SHA1 (length)"
+msgstr "Автоматично избиране на SHA1 (дължина)"
+
+#: gitk:11245
+msgid "Hide remote refs"
+msgstr "Скриване на отдалечените указатели"
+
+#: gitk:11249
+msgid "Diff display options"
+msgstr "Настройки на показването на разликите"
+
+#: gitk:11251
+msgid "Tab spacing"
+msgstr "Широчина на табулатора"
+
+#: gitk:11254
+msgid "Display nearby tags/heads"
+msgstr "Извеждане на близките етикети и върхове"
+
+#: gitk:11257
+msgid "Maximum # tags/heads to show"
+msgstr "Максимален брой етикети/върхове за показване"
+
+#: gitk:11260
+msgid "Limit diffs to listed paths"
+msgstr "Разлика само в избраните пътища"
+
+#: gitk:11263
+msgid "Support per-file encodings"
+msgstr "Поддръжка на различни кодирания за всеки файл"
+
+#: gitk:11269 gitk:11416
+msgid "External diff tool"
+msgstr "Външен инструмент за разлики"
+
+#: gitk:11270
+msgid "Choose..."
+msgstr "Избор…"
+
+#: gitk:11275
+msgid "General options"
+msgstr "Общи настройки"
+
+#: gitk:11278
+msgid "Use themed widgets"
+msgstr "Използване на тема за графичните обекти"
+
+#: gitk:11280
+msgid "(change requires restart)"
+msgstr "(промяната изисква рестартиране на Gitk)"
+
+#: gitk:11282
+msgid "(currently unavailable)"
+msgstr "(в момента недостъпно)"
+
+#: gitk:11293
+msgid "Colors: press to choose"
+msgstr "Цветове: избира се с натискане"
+
+#: gitk:11296
+msgid "Interface"
+msgstr "Интерфейс"
+
+#: gitk:11297
+msgid "interface"
+msgstr "интерфейс"
+
+#: gitk:11300
+msgid "Background"
+msgstr "Фон"
+
+#: gitk:11301 gitk:11331
+msgid "background"
+msgstr "Фон"
+
+#: gitk:11304
+msgid "Foreground"
+msgstr "Знаци"
+
+#: gitk:11305
+msgid "foreground"
+msgstr "знаци"
+
+#: gitk:11308
+msgid "Diff: old lines"
+msgstr "Разлика: стари редове"
+
+#: gitk:11309
+msgid "diff old lines"
+msgstr "разлика, стари редове"
+
+#: gitk:11313
+msgid "Diff: new lines"
+msgstr "Разлика: нови редове"
+
+#: gitk:11314
+msgid "diff new lines"
+msgstr "разлика, нови редове"
+
+#: gitk:11318
+msgid "Diff: hunk header"
+msgstr "Разлика: начало на парче"
+
+#: gitk:11320
+msgid "diff hunk header"
+msgstr "разлика, начало на парче"
+
+#: gitk:11324
+msgid "Marked line bg"
+msgstr "Фон на отбелязан ред"
+
+#: gitk:11326
+msgid "marked line background"
+msgstr "Фон на отбелязан ред"
+
+#: gitk:11330
+msgid "Select bg"
+msgstr "Избор на фон"
+
+#: gitk:11339
+msgid "Fonts: press to choose"
+msgstr "Шрифтове: избира се с натискане"
+
+#: gitk:11341
+msgid "Main font"
+msgstr "Основен шрифт"
+
+#: gitk:11342
+msgid "Diff display font"
+msgstr "Шрифт за разликите"
+
+#: gitk:11343
+msgid "User interface font"
+msgstr "Шрифт на интерфейса"
+
+#: gitk:11365
+msgid "Gitk preferences"
+msgstr "Настройки на Gitk"
+
+#: gitk:11374
+msgid "General"
+msgstr "Общи"
+
+#: gitk:11375
+msgid "Colors"
+msgstr "Цветове"
+
+#: gitk:11376
+msgid "Fonts"
+msgstr "Шрифтове"
+
+#: gitk:11426
+#, tcl-format
+msgid "Gitk: choose color for %s"
+msgstr "Gitk: избор на цвят на %s"
+
+#: gitk:12080
+msgid "Cannot find a git repository here."
+msgstr "Тук липсва хранилище на Git."
+
+#: gitk:12127
+#, tcl-format
+msgid "Ambiguous argument '%s': both revision and filename"
+msgstr "Нееднозначен аргумент „%s“: има и такава версия, и такъв файл"
+
+#: gitk:12139
+msgid "Bad arguments to gitk:"
+msgstr "Неправилни аргументи на gitk:"
+
+#: gitk:12242
+msgid "Command line"
+msgstr "Команден ред"
old mode 100644 (file)
new mode 100755 (executable)
index 68c77f6f8ffd120f0011eb07accdc2849afca834..3bc0f0b5ca0995910e0e517b545b5fb9af626a8b 100755 (executable)
@@ -548,6 +548,20 @@ sub evaluate_uri {
                'sub' => sub { feature_bool('remote_heads', @_) },
                'override' => 0,
                'default' => [0]},
+
+       # Enable showing branches under other refs in addition to heads
+
+       # To set system wide extra branch refs have in $GITWEB_CONFIG
+       # $feature{'extra-branch-refs'}{'default'} = ['dirs', 'of', 'choice'];
+       # To have project specific config enable override in $GITWEB_CONFIG
+       # $feature{'extra-branch-refs'}{'override'} = 1;
+       # and in project config gitweb.extrabranchrefs = dirs of choice
+       # Every directory is separated with whitespace.
+
+       'extra-branch-refs' => {
+               'sub' => \&feature_extra_branch_refs,
+               'override' => 0,
+               'default' => []},
 );
 
 sub gitweb_get_feature {
@@ -626,6 +640,21 @@ sub feature_avatar {
        return @val ? @val : @_;
 }
 
+sub feature_extra_branch_refs {
+       my (@branch_refs) = @_;
+       my $values = git_get_project_config('extrabranchrefs');
+
+       if ($values) {
+               $values = config_to_multi ($values);
+               @branch_refs = ();
+               foreach my $value (@{$values}) {
+                       push @branch_refs, split /\s+/, $value;
+               }
+       }
+
+       return @branch_refs;
+}
+
 # checking HEAD file with -e is fragile if the repository was
 # initialized long time ago (i.e. symlink HEAD) and was pack-ref'ed
 # and then pruned.
@@ -656,6 +685,18 @@ sub filter_snapshot_fmts {
                !$known_snapshot_formats{$_}{'disabled'}} @fmts;
 }
 
+sub filter_and_validate_refs {
+       my @refs = @_;
+       my %unique_refs = ();
+
+       foreach my $ref (@refs) {
+               die_error(500, "Invalid ref '$ref' in 'extra-branch-refs' feature") unless (is_valid_ref_format($ref));
+               # 'heads' are added implicitly in get_branch_refs().
+               $unique_refs{$ref} = 1 if ($ref ne 'heads');
+       }
+       return sort keys %unique_refs;
+}
+
 # If it is set to code reference, it is code that it is to be run once per
 # request, allowing updating configurations that change with each request,
 # while running other code in config file only once.
@@ -994,7 +1035,7 @@ sub evaluate_path_info {
 sub evaluate_and_validate_params {
        our $action = $input_params{'action'};
        if (defined $action) {
-               if (!validate_action($action)) {
+               if (!is_valid_action($action)) {
                        die_error(400, "Invalid action parameter");
                }
        }
@@ -1002,7 +1043,7 @@ sub evaluate_and_validate_params {
        # parameters which are pathnames
        our $project = $input_params{'project'};
        if (defined $project) {
-               if (!validate_project($project)) {
+               if (!is_valid_project($project)) {
                        undef $project;
                        die_error(404, "No such project");
                }
@@ -1010,21 +1051,21 @@ sub evaluate_and_validate_params {
 
        our $project_filter = $input_params{'project_filter'};
        if (defined $project_filter) {
-               if (!validate_pathname($project_filter)) {
+               if (!is_valid_pathname($project_filter)) {
                        die_error(404, "Invalid project_filter parameter");
                }
        }
 
        our $file_name = $input_params{'file_name'};
        if (defined $file_name) {
-               if (!validate_pathname($file_name)) {
+               if (!is_valid_pathname($file_name)) {
                        die_error(400, "Invalid file parameter");
                }
        }
 
        our $file_parent = $input_params{'file_parent'};
        if (defined $file_parent) {
-               if (!validate_pathname($file_parent)) {
+               if (!is_valid_pathname($file_parent)) {
                        die_error(400, "Invalid file parent parameter");
                }
        }
@@ -1032,21 +1073,21 @@ sub evaluate_and_validate_params {
        # parameters which are refnames
        our $hash = $input_params{'hash'};
        if (defined $hash) {
-               if (!validate_refname($hash)) {
+               if (!is_valid_refname($hash)) {
                        die_error(400, "Invalid hash parameter");
                }
        }
 
        our $hash_parent = $input_params{'hash_parent'};
        if (defined $hash_parent) {
-               if (!validate_refname($hash_parent)) {
+               if (!is_valid_refname($hash_parent)) {
                        die_error(400, "Invalid hash parent parameter");
                }
        }
 
        our $hash_base = $input_params{'hash_base'};
        if (defined $hash_base) {
-               if (!validate_refname($hash_base)) {
+               if (!is_valid_refname($hash_base)) {
                        die_error(400, "Invalid hash base parameter");
                }
        }
@@ -1066,7 +1107,7 @@ sub evaluate_and_validate_params {
 
        our $hash_parent_base = $input_params{'hash_parent_base'};
        if (defined $hash_parent_base) {
-               if (!validate_refname($hash_parent_base)) {
+               if (!is_valid_refname($hash_parent_base)) {
                        die_error(400, "Invalid hash parent base parameter");
                }
        }
@@ -1113,7 +1154,7 @@ sub evaluate_git_dir {
        our $git_dir = "$projectroot/$project" if $project;
 }
 
-our (@snapshot_fmts, $git_avatar);
+our (@snapshot_fmts, $git_avatar, @extra_branch_refs);
 sub configure_gitweb_features {
        # list of supported snapshot formats
        our @snapshot_fmts = gitweb_get_feature('snapshot');
@@ -1131,6 +1172,13 @@ sub configure_gitweb_features {
        } else {
                $git_avatar = '';
        }
+
+       our @extra_branch_refs = gitweb_get_feature('extra-branch-refs');
+       @extra_branch_refs = filter_and_validate_refs (@extra_branch_refs);
+}
+
+sub get_branch_refs {
+       return ('heads', @extra_branch_refs);
 }
 
 # custom error handler: 'die <message>' is Internal Server Error
@@ -1418,27 +1466,30 @@ sub href {
 ## ======================================================================
 ## validation, quoting/unquoting and escaping
 
-sub validate_action {
-       my $input = shift || return undef;
+sub is_valid_action {
+       my $input = shift;
        return undef unless exists $actions{$input};
-       return $input;
+       return 1;
 }
 
-sub validate_project {
-       my $input = shift || return undef;
-       if (!validate_pathname($input) ||
+sub is_valid_project {
+       my $input = shift;
+
+       return unless defined $input;
+       if (!is_valid_pathname($input) ||
                !(-d "$projectroot/$input") ||
                !check_export_ok("$projectroot/$input") ||
                ($strict_export && !project_in_list($input))) {
                return undef;
        } else {
-               return $input;
+               return 1;
        }
 }
 
-sub validate_pathname {
-       my $input = shift || return undef;
+sub is_valid_pathname {
+       my $input = shift;
 
+       return undef unless defined $input;
        # no '.' or '..' as elements of path, i.e. no '.' nor '..'
        # at the beginning, at the end, and between slashes.
        # also this catches doubled slashes
@@ -1449,24 +1500,33 @@ sub validate_pathname {
        if ($input =~ m!\0!) {
                return undef;
        }
-       return $input;
+       return 1;
 }
 
-sub validate_refname {
-       my $input = shift || return undef;
+sub is_valid_ref_format {
+       my $input = shift;
 
-       # textual hashes are O.K.
-       if ($input =~ m/^[0-9a-fA-F]{40}$/) {
-               return $input;
-       }
-       # it must be correct pathname
-       $input = validate_pathname($input)
-               or return undef;
+       return undef unless defined $input;
        # restrictions on ref name according to git-check-ref-format
        if ($input =~ m!(/\.|\.\.|[\000-\040\177 ~^:?*\[]|/$)!) {
                return undef;
        }
-       return $input;
+       return 1;
+}
+
+sub is_valid_refname {
+       my $input = shift;
+
+       return undef unless defined $input;
+       # textual hashes are O.K.
+       if ($input =~ m/^[0-9a-fA-F]{40}$/) {
+               return 1;
+       }
+       # it must be correct pathname
+       is_valid_pathname($input) or return undef;
+       # check git-check-ref-format restrictions
+       is_valid_ref_format($input) or return undef;
+       return 1;
 }
 
 # decode sequences of octets in utf8 into Perl's internal form,
@@ -2515,6 +2575,7 @@ sub format_snapshot_links {
 sub get_feed_info {
        my $format = shift || 'Atom';
        my %res = (action => lc($format));
+       my $matched_ref = 0;
 
        # feed links are possible only for project views
        return unless (defined $project);
@@ -2522,12 +2583,17 @@ sub get_feed_info {
        # or don't have specific feed yet (so they should use generic)
        return if (!$action || $action =~ /^(?:tags|heads|forks|tag|search)$/x);
 
-       my $branch;
-       # branches refs uses 'refs/heads/' prefix (fullname) to differentiate
-       # from tag links; this also makes possible to detect branch links
-       if ((defined $hash_base && $hash_base =~ m!^refs/heads/(.*)$!) ||
-           (defined $hash      && $hash      =~ m!^refs/heads/(.*)$!)) {
-               $branch = $1;
+       my $branch = undef;
+       # branches refs uses 'refs/' + $get_branch_refs()[x] + '/' prefix
+       # (fullname) to differentiate from tag links; this also makes
+       # possible to detect branch links
+       for my $ref (get_branch_refs()) {
+               if ((defined $hash_base && $hash_base =~ m!^refs/\Q$ref\E/(.*)$!) ||
+                   (defined $hash      && $hash      =~ m!^refs/\Q$ref\E/(.*)$!)) {
+                       $branch = $1;
+                       $matched_ref = $ref;
+                       last;
+               }
        }
        # find log type for feed description (title)
        my $type = 'log';
@@ -2540,7 +2606,7 @@ sub get_feed_info {
        }
 
        $res{-title} = $type;
-       $res{'hash'} = (defined $branch ? "refs/heads/$branch" : undef);
+       $res{'hash'} = (defined $branch ? "refs/$matched_ref/$branch" : undef);
        $res{'file_name'} = $file_name;
 
        return %res;
@@ -3193,7 +3259,7 @@ sub git_get_last_activity {
             '--format=%(committer)',
             '--sort=-committerdate',
             '--count=1',
-            'refs/heads') or return;
+            map { "refs/$_" } get_branch_refs ()) or return;
        my $most_recent = <$fd>;
        close $fd or return;
        if (defined $most_recent &&
@@ -3644,7 +3710,7 @@ sub parse_from_to_diffinfo {
 
 sub git_get_heads_list {
        my ($limit, @classes) = @_;
-       @classes = ('heads') unless @classes;
+       @classes = get_branch_refs() unless @classes;
        my @patterns = map { "refs/$_" } @classes;
        my @headslist;
 
@@ -3662,9 +3728,16 @@ sub git_get_heads_list {
                my ($committer, $epoch, $tz) =
                        ($committerinfo =~ /^(.*) ([0-9]+) (.*)$/);
                $ref_item{'fullname'}  = $name;
-               $name =~ s!^refs/(?:head|remote)s/!!;
+               my $strip_refs = join '|', map { quotemeta } get_branch_refs();
+               $name =~ s!^refs/($strip_refs|remotes)/!!;
+               $ref_item{'name'} = $name;
+               # for refs neither in 'heads' nor 'remotes' we want to
+               # show their ref dir
+               my $ref_dir = (defined $1) ? $1 : '';
+               if ($ref_dir ne '' and $ref_dir ne 'heads' and $ref_dir ne 'remotes') {
+                   $ref_item{'name'} .= ' (' . $ref_dir . ')';
+               }
 
-               $ref_item{'name'}  = $name;
                $ref_item{'id'}    = $hash;
                $ref_item{'title'} = $title || '(no commit message)';
                $ref_item{'epoch'} = $epoch;
@@ -7156,6 +7229,15 @@ sub git_tree {
        git_footer_html();
 }
 
+sub sanitize_for_filename {
+    my $name = shift;
+
+    $name =~ s!/!-!g;
+    $name =~ s/[^[:alnum:]_.-]//g;
+
+    return $name;
+}
+
 sub snapshot_name {
        my ($project, $hash) = @_;
 
@@ -7163,9 +7245,7 @@ sub snapshot_name {
        # path/to/project/.git -> project
        my $name = to_utf8($project);
        $name =~ s,([^/])/*\.git$,$1,;
-       $name = basename($name);
-       # sanitize name
-       $name =~ s/[[:cntrl:]]/?/g;
+       $name = sanitize_for_filename(basename($name));
 
        my $ver = $hash;
        if ($hash =~ /^[0-9a-fA-F]+$/) {
@@ -7179,13 +7259,25 @@ sub snapshot_name {
                $ver = $1;
        } else {
                # branches and other need shortened SHA-1 hash
-               if ($hash =~ m!^refs/(?:heads|remotes)/(.*)$!) {
-                       $ver = $1;
+               my $strip_refs = join '|', map { quotemeta } get_branch_refs();
+               if ($hash =~ m!^refs/($strip_refs|remotes)/(.*)$!) {
+                       my $ref_dir = (defined $1) ? $1 : '';
+                       $ver = $2;
+
+                       $ref_dir = sanitize_for_filename($ref_dir);
+                       # for refs neither in heads nor remotes we want to
+                       # add a ref dir to archive name
+                       if ($ref_dir ne '' and $ref_dir ne 'heads' and $ref_dir ne 'remotes') {
+                               $ver = $ref_dir . '-' . $ver;
+                       }
                }
                $ver .= '-' . git_get_short_hash($project, $hash);
        }
+       # special case of sanitization for filename - we change
+       # slashes to dots instead of dashes
        # in case of hierarchical branch names
        $ver =~ s!/!.!g;
+       $ver =~ s/[^[:alnum:]_.-]//g;
 
        # name = project-version_string
        $name = "$name-$ver";
diff --git a/help.c b/help.c
index f068925bb7f26055fb699cda7173a0c06ac46a22..df7d16d7ce52ce9b9f274db92b24aa64bac56ebf 100644 (file)
--- a/help.c
+++ b/help.c
@@ -148,7 +148,7 @@ static void list_commands_in_dir(struct cmdnames *cmds,
        while ((de = readdir(dir)) != NULL) {
                int entlen;
 
-               if (prefixcmp(de->d_name, prefix))
+               if (!starts_with(de->d_name, prefix))
                        continue;
 
                strbuf_setlen(&buf, len);
@@ -255,7 +255,7 @@ static int git_unknown_cmd_config(const char *var, const char *value, void *cb)
        if (!strcmp(var, "help.autocorrect"))
                autocorrect = git_config_int(var,value);
        /* Also use aliases for command lookup */
-       if (!prefixcmp(var, "alias."))
+       if (starts_with(var, "alias."))
                add_cmdname(&aliases, var + 6, strlen(var + 6));
 
        return git_default_config(var, value, cb);
@@ -329,7 +329,7 @@ const char *help_unknown_cmd(const char *cmd)
                if ((n < ARRAY_SIZE(common_cmds)) && !cmp) {
                        /* Yes, this is one of the common commands */
                        n++; /* use the entry from common_cmds[] */
-                       if (!prefixcmp(candidate, cmd)) {
+                       if (starts_with(candidate, cmd)) {
                                /* Give prefix match a very good score */
                                main_cmds.names[i]->len = 0;
                                continue;
@@ -414,7 +414,7 @@ static int append_similar_ref(const char *refname, const unsigned char *sha1,
        struct similar_ref_cb *cb = (struct similar_ref_cb *)(cb_data);
        char *branch = strrchr(refname, '/') + 1;
        /* A remote branch of the same name is deemed similar */
-       if (!prefixcmp(refname, "refs/remotes/") &&
+       if (starts_with(refname, "refs/remotes/") &&
            !strcmp(branch, cb->base_ref))
                string_list_append(cb->similar_refs,
                                   refname + strlen("refs/remotes/"));
index 8c464bd8051ed813175b01f832e5e78a91bba8ea..d2c0a625cef558df252af6927cd3a86424674862 100644 (file)
@@ -226,7 +226,7 @@ static int http_config(const char *var, const char *value, void *cb)
                return 0;
        }
 
-       if (!prefixcmp(var, "http.")) {
+       if (starts_with(var, "http.")) {
                int i;
 
                for (i = 0; i < ARRAY_SIZE(rpc_service); i++) {
@@ -247,7 +247,7 @@ static struct rpc_service *select_service(const char *name)
        struct rpc_service *svc = NULL;
        int i;
 
-       if (prefixcmp(name, "git-"))
+       if (!starts_with(name, "git-"))
                forbidden("Unsupported service: '%s'", name);
 
        for (i = 0; i < ARRAY_SIZE(rpc_service); i++) {
index 34cb70f90ea14a18848bd270463f5a34dc2c8fd3..d4b40c9c60e8caa2b8f8e676ac18b678b6ca2ed6 100644 (file)
@@ -771,7 +771,7 @@ static void handle_new_lock_ctx(struct xml_ctx *ctx, int tag_closed)
                        lock->owner = xmalloc(strlen(ctx->cdata) + 1);
                        strcpy(lock->owner, ctx->cdata);
                } else if (!strcmp(ctx->name, DAV_ACTIVELOCK_TIMEOUT)) {
-                       if (!prefixcmp(ctx->cdata, "Second-"))
+                       if (starts_with(ctx->cdata, "Second-"))
                                lock->timeout =
                                        strtol(ctx->cdata + 7, NULL, 10);
                } else if (!strcmp(ctx->name, DAV_ACTIVELOCK_TOKEN)) {
@@ -1579,7 +1579,7 @@ static void fetch_symref(const char *path, char **symref, unsigned char *sha1)
                return;
 
        /* If it's a symref, set the refname; otherwise try for a sha1 */
-       if (!prefixcmp((char *)buffer.buf, "ref: ")) {
+       if (starts_with((char *)buffer.buf, "ref: ")) {
                *symref = xmemdupz((char *)buffer.buf + 5, buffer.len - 6);
        } else {
                get_sha1_hex(buffer.buf, sha1);
diff --git a/http.c b/http.c
index ccb813b86fb47dac7dcb3a1eb86faaa9e8ecc923..70eaa26e88cbcfa62e7d5e967b4e519432319210 100644 (file)
--- a/http.c
+++ b/http.c
@@ -460,7 +460,7 @@ void http_init(struct remote *remote, const char *url, int proactive_auth)
                credential_from_url(&http_auth, url);
                if (!ssl_cert_password_required &&
                    getenv("GIT_SSL_CERT_PASSWORD_PROTECTED") &&
-                   !prefixcmp(url, "https://"))
+                   starts_with(url, "https://"))
                        ssl_cert_password_required = 1;
        }
 
@@ -1000,7 +1000,7 @@ static int update_url_from_redirect(struct strbuf *base,
        if (!strcmp(asked, got->buf))
                return 0;
 
-       if (prefixcmp(asked, base->buf))
+       if (!starts_with(asked, base->buf))
                die("BUG: update_url_from_redirect: %s is not a superset of %s",
                    asked, base->buf);
 
@@ -1106,7 +1106,7 @@ int http_fetch_ref(const char *base, struct ref *ref)
                strbuf_rtrim(&buffer);
                if (buffer.len == 40)
                        ret = get_sha1_hex(buffer.buf, ref->old_sha1);
-               else if (!prefixcmp(buffer.buf, "ref: ")) {
+               else if (starts_with(buffer.buf, "ref: ")) {
                        ref->symref = xstrdup(buffer.buf + 5);
                        ret = 0;
                }
@@ -1207,8 +1207,8 @@ int http_get_info_packs(const char *base_url, struct packed_git **packs_head)
                case 'P':
                        i++;
                        if (i + 52 <= buf.len &&
-                           !prefixcmp(data + i, " pack-") &&
-                           !prefixcmp(data + i + 46, ".pack\n")) {
+                           starts_with(data + i, " pack-") &&
+                           starts_with(data + i + 46, ".pack\n")) {
                                get_sha1_hex(data + i + 6, sha1);
                                fetch_and_setup_pack_index(packs_head, sha1,
                                                      base_url);
index 6f5cc4f782ea82fc14ee596c7bac96e2b6f907e7..0bc6f7fae151ea364e9d141b30b8e68d544a4448 100644 (file)
@@ -1263,7 +1263,7 @@ static int count_messages(struct strbuf *all_msgs)
        char *p = all_msgs->buf;
 
        while (1) {
-               if (!prefixcmp(p, "From ")) {
+               if (starts_with(p, "From ")) {
                        p = strstr(p+5, "\nFrom: ");
                        if (!p) break;
                        p = strstr(p+7, "\nDate: ");
@@ -1297,7 +1297,7 @@ static int split_msg(struct strbuf *all_msgs, struct strbuf *msg, int *ofs)
        data = &all_msgs->buf[*ofs];
        len = all_msgs->len - *ofs;
 
-       if (len < 5 || prefixcmp(data, "From "))
+       if (len < 5 || !starts_with(data, "From "))
                return 0;
 
        p = strchr(data, '\n');
@@ -1339,13 +1339,13 @@ static int git_imap_config(const char *key, const char *val, void *cb)
        if (!strcmp("folder", key)) {
                imap_folder = xstrdup(val);
        } else if (!strcmp("host", key)) {
-               if (!prefixcmp(val, "imap:"))
+               if (starts_with(val, "imap:"))
                        val += 5;
-               else if (!prefixcmp(val, "imaps:")) {
+               else if (starts_with(val, "imaps:")) {
                        val += 6;
                        server.use_ssl = 1;
                }
-               if (!prefixcmp(val, "//"))
+               if (starts_with(val, "//"))
                        val += 2;
                server.host = xstrdup(val);
        } else if (!strcmp("user", key))
index 6cbedf0280076d8fe6e49241d89938955ecddead..206816fa9c47eb419775bcf2005f9cd5144bc297 100644 (file)
@@ -162,15 +162,17 @@ void mark_edges_uninteresting(struct rev_info *revs, show_edge_fn show_edge)
                }
                mark_edge_parents_uninteresting(commit, revs, show_edge);
        }
-       for (i = 0; i < revs->cmdline.nr; i++) {
-               struct object *obj = revs->cmdline.rev[i].item;
-               struct commit *commit = (struct commit *)obj;
-               if (obj->type != OBJ_COMMIT || !(obj->flags & UNINTERESTING))
-                       continue;
-               mark_tree_uninteresting(commit->tree);
-               if (revs->edge_hint && !(obj->flags & SHOWN)) {
-                       obj->flags |= SHOWN;
-                       show_edge(commit);
+       if (revs->edge_hint) {
+               for (i = 0; i < revs->cmdline.nr; i++) {
+                       struct object *obj = revs->cmdline.rev[i].item;
+                       struct commit *commit = (struct commit *)obj;
+                       if (obj->type != OBJ_COMMIT || !(obj->flags & UNINTERESTING))
+                               continue;
+                       mark_tree_uninteresting(commit->tree);
+                       if (!(obj->flags & SHOWN)) {
+                               obj->flags |= SHOWN;
+                               show_edge(commit);
+                       }
                }
        }
 }
index e958d0748f11d82995f63d06b7e6ed05b2e7e4f1..08970bf46e49f6447ada24e81e5792ea9f92f419 100644 (file)
@@ -98,7 +98,7 @@ static int add_ref_decoration(const char *refname, const unsigned char *sha1, in
        struct object *obj;
        enum decoration_type type = DECORATION_NONE;
 
-       if (!prefixcmp(refname, "refs/replace/")) {
+       if (starts_with(refname, "refs/replace/")) {
                unsigned char original_sha1[20];
                if (!read_replace_refs)
                        return 0;
@@ -116,11 +116,11 @@ static int add_ref_decoration(const char *refname, const unsigned char *sha1, in
        if (!obj)
                return 0;
 
-       if (!prefixcmp(refname, "refs/heads/"))
+       if (starts_with(refname, "refs/heads/"))
                type = DECORATION_REF_LOCAL;
-       else if (!prefixcmp(refname, "refs/remotes/"))
+       else if (starts_with(refname, "refs/remotes/"))
                type = DECORATION_REF_REMOTE;
-       else if (!prefixcmp(refname, "refs/tags/"))
+       else if (starts_with(refname, "refs/tags/"))
                type = DECORATION_REF_TAG;
        else if (!strcmp(refname, "refs/stash"))
                type = DECORATION_REF_STASH;
@@ -134,6 +134,8 @@ static int add_ref_decoration(const char *refname, const unsigned char *sha1, in
                obj = ((struct tag *)obj)->tagged;
                if (!obj)
                        break;
+               if (!obj->parsed)
+                       parse_object(obj->sha1);
                add_name_decoration(DECORATION_REF_TAG, refname, obj);
        }
        return 0;
index dbb7104c043b14f2a43d67e1ca87ce88f4a55408..8400a8e937d8303ecc2ace1136a0700187865f58 100644 (file)
@@ -693,7 +693,7 @@ static int make_room_for_path(struct merge_options *o, const char *path)
        /* Make sure leading directories are created */
        status = safe_create_leading_directories_const(path);
        if (status) {
-               if (status == -3) {
+               if (status == SCLD_EXISTS) {
                        /* something else exists */
                        error(msg, path, _(": perhaps a D/F conflict?"));
                        return -1;
@@ -2063,13 +2063,13 @@ int parse_merge_opt(struct merge_options *o, const char *s)
                o->recursive_variant = MERGE_RECURSIVE_THEIRS;
        else if (!strcmp(s, "subtree"))
                o->subtree_shift = "";
-       else if (!prefixcmp(s, "subtree="))
+       else if (starts_with(s, "subtree="))
                o->subtree_shift = s + strlen("subtree=");
        else if (!strcmp(s, "patience"))
                o->xdl_opts = DIFF_WITH_ALG(o, PATIENCE_DIFF);
        else if (!strcmp(s, "histogram"))
                o->xdl_opts = DIFF_WITH_ALG(o, HISTOGRAM_DIFF);
-       else if (!prefixcmp(s, "diff-algorithm=")) {
+       else if (starts_with(s, "diff-algorithm=")) {
                long value = parse_algorithm_value(s + strlen("diff-algorithm="));
                if (value < 0)
                        return -1;
@@ -2088,7 +2088,7 @@ int parse_merge_opt(struct merge_options *o, const char *s)
                o->renormalize = 1;
        else if (!strcmp(s, "no-renormalize"))
                o->renormalize = 0;
-       else if (!prefixcmp(s, "rename-threshold=")) {
+       else if (starts_with(s, "rename-threshold=")) {
                const char *score = s + strlen("rename-threshold=");
                if ((o->rename_score = parse_rename_score(&score)) == -1 || *score != 0)
                        return -1;
index 7bb3473dbe44c1f8325ee6891620eec67163dfcd..2975dcd581024cd483953b47636466b79c95bafe 100644 (file)
@@ -70,7 +70,7 @@ static combine_notes_fn parse_combine_notes_fn(const char *v)
 static int notes_rewrite_config(const char *k, const char *v, void *cb)
 {
        struct notes_rewrite_cfg *c = cb;
-       if (!prefixcmp(k, "notes.rewrite.") && !strcmp(k+14, c->cmd)) {
+       if (starts_with(k, "notes.rewrite.") && !strcmp(k+14, c->cmd)) {
                c->enabled = git_config_bool(k, v);
                return 0;
        } else if (!c->mode_from_env && !strcmp(k, "notes.rewritemode")) {
@@ -85,7 +85,7 @@ static int notes_rewrite_config(const char *k, const char *v, void *cb)
        } else if (!c->refs_from_env && !strcmp(k, "notes.rewriteref")) {
                /* note that a refs/ prefix is implied in the
                 * underlying for_each_glob_ref */
-               if (!prefixcmp(v, "refs/notes/"))
+               if (starts_with(v, "refs/notes/"))
                        string_list_add_refs_by_glob(c->refs, v);
                else
                        warning(_("Refusing to rewrite notes in %s"
diff --git a/notes.c b/notes.c
index b69c0b82577e0d4958ddf3efbb4aec5a9c5fa99e..5f07c0b7f28e09bd2f7969ea08b00b319a226920 100644 (file)
--- a/notes.c
+++ b/notes.c
@@ -1243,9 +1243,9 @@ static void format_note(struct notes_tree *t, const unsigned char *object_sha1,
                if (!ref || !strcmp(ref, GIT_NOTES_DEFAULT_REF)) {
                        strbuf_addstr(sb, "\nNotes:\n");
                } else {
-                       if (!prefixcmp(ref, "refs/"))
+                       if (starts_with(ref, "refs/"))
                                ref += 5;
-                       if (!prefixcmp(ref, "notes/"))
+                       if (starts_with(ref, "notes/"))
                                ref += 6;
                        strbuf_addf(sb, "\nNotes (%s):\n", ref);
                }
@@ -1293,9 +1293,9 @@ int copy_note(struct notes_tree *t,
 
 void expand_notes_ref(struct strbuf *sb)
 {
-       if (!prefixcmp(sb->buf, "refs/notes/"))
+       if (starts_with(sb->buf, "refs/notes/"))
                return; /* we're happy */
-       else if (!prefixcmp(sb->buf, "notes/"))
+       else if (starts_with(sb->buf, "notes/"))
                strbuf_insert(sb, 0, "refs/", 5);
        else
                strbuf_insert(sb, 0, "refs/notes/", 11);
index ca9e63be18f9333bf0f603ddb9ddd923588be0aa..605d01b25cbd9d796810f8aa810894a2c198da28 100644 (file)
@@ -44,14 +44,13 @@ static int need_large_offset(off_t offset, const struct pack_idx_option *opts)
  */
 const char *write_idx_file(const char *index_name, struct pack_idx_entry **objects,
                           int nr_objects, const struct pack_idx_option *opts,
-                          unsigned char *sha1)
+                          const unsigned char *sha1)
 {
        struct sha1file *f;
        struct pack_idx_entry **sorted_by_sha, **list, **last;
        off_t last_obj_offset = 0;
        uint32_t array[256];
        int i, fd;
-       git_SHA_CTX ctx;
        uint32_t index_version;
 
        if (nr_objects) {
@@ -114,9 +113,6 @@ const char *write_idx_file(const char *index_name, struct pack_idx_entry **objec
        }
        sha1write(f, array, 256 * 4);
 
-       /* compute the SHA1 hash of sorted object names. */
-       git_SHA1_Init(&ctx);
-
        /*
         * Write the actual SHA1 entries..
         */
@@ -128,7 +124,6 @@ const char *write_idx_file(const char *index_name, struct pack_idx_entry **objec
                        sha1write(f, &offset, 4);
                }
                sha1write(f, obj->sha1, 20);
-               git_SHA1_Update(&ctx, obj->sha1, 20);
                if ((opts->flags & WRITE_IDX_STRICT) &&
                    (i && !hashcmp(list[-2]->sha1, obj->sha1)))
                        die("The same object %s appears twice in the pack",
@@ -178,7 +173,6 @@ const char *write_idx_file(const char *index_name, struct pack_idx_entry **objec
        sha1write(f, sha1, 20);
        sha1close(f, NULL, ((opts->flags & WRITE_IDX_VERIFY)
                            ? CSUM_CLOSE : CSUM_FSYNC));
-       git_SHA1_Final(sha1, &ctx);
        return index_name;
 }
 
@@ -189,8 +183,7 @@ off_t write_pack_header(struct sha1file *f, uint32_t nr_entries)
        hdr.hdr_signature = htonl(PACK_SIGNATURE);
        hdr.hdr_version = htonl(PACK_VERSION);
        hdr.hdr_entries = htonl(nr_entries);
-       if (sha1write(f, &hdr, sizeof(hdr)))
-               return 0;
+       sha1write(f, &hdr, sizeof(hdr));
        return sizeof(hdr);
 }
 
diff --git a/pack.h b/pack.h
index aa6ee7d606f1d5336bff9a3067b44057c362e788..12d951659c16462cf2da6a43e2c4974b85e39712 100644 (file)
--- a/pack.h
+++ b/pack.h
@@ -76,7 +76,7 @@ struct pack_idx_entry {
 struct progress;
 typedef int (*verify_fn)(const unsigned char*, enum object_type, unsigned long, void*, int*);
 
-extern const char *write_idx_file(const char *index_name, struct pack_idx_entry **objects, int nr_objects, const struct pack_idx_option *, unsigned char *sha1);
+extern const char *write_idx_file(const char *index_name, struct pack_idx_entry **objects, int nr_objects, const struct pack_idx_option *, const unsigned char *sha1);
 extern int check_pack_crc(struct packed_git *p, struct pack_window **w_curs, off_t offset, off_t len, unsigned int nr);
 extern int verify_pack_index(struct packed_git *);
 extern int verify_pack(struct packed_git *, verify_fn fn, struct progress *, uint32_t);
diff --git a/pager.c b/pager.c
index fa19765eb9e60d2dbc17887773fff4ad66a32c37..0cc75a8eee32a0195a74c6819a04745f341e45e3 100644 (file)
--- a/pager.c
+++ b/pager.c
@@ -80,8 +80,15 @@ void setup_pager(void)
        pager_process.use_shell = 1;
        pager_process.argv = pager_argv;
        pager_process.in = -1;
-       if (!getenv("LESS")) {
-               static const char *env[] = { "LESS=FRSX", NULL };
+       if (!getenv("LESS") || !getenv("LV")) {
+               static const char *env[3];
+               int i = 0;
+
+               if (!getenv("LESS"))
+                       env[i++] = "LESS=FRSX";
+               if (!getenv("LV"))
+                       env[i++] = "LV=-c";
+               env[i] = NULL;
                pager_process.env = env;
        }
        if (start_command(&pager_process))
@@ -151,7 +158,7 @@ int decimal_width(int number)
 static int pager_command_config(const char *var, const char *value, void *data)
 {
        struct pager_config *c = data;
-       if (!prefixcmp(var, "pager.") && !strcmp(var + 6, c->cmd)) {
+       if (starts_with(var, "pager.") && !strcmp(var + 6, c->cmd)) {
                int b = git_config_maybe_bool(var, value);
                if (b >= 0)
                        c->want = b;
index 62e9b1cc6831768315dd74eac30348668de22da3..7b8d3faa171309e84bff23c70206a9c8bd99cc71 100644 (file)
@@ -273,13 +273,13 @@ static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg,
                        if (options->flags & PARSE_OPT_NONEG)
                                continue;
                        /* negated and abbreviated very much? */
-                       if (!prefixcmp("no-", arg)) {
+                       if (starts_with("no-", arg)) {
                                flags |= OPT_UNSET;
                                goto is_abbreviated;
                        }
                        /* negated? */
-                       if (prefixcmp(arg, "no-")) {
-                               if (!prefixcmp(long_name, "no-")) {
+                       if (!starts_with(arg, "no-")) {
+                               if (starts_with(long_name, "no-")) {
                                        long_name += 3;
                                        opt_flags |= OPT_UNSET;
                                        goto again;
@@ -289,7 +289,7 @@ static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg,
                        flags |= OPT_UNSET;
                        rest = skip_prefix(arg + 3, long_name);
                        /* abbreviated and negated? */
-                       if (!rest && !prefixcmp(long_name, arg + 3))
+                       if (!rest && starts_with(long_name, arg + 3))
                                goto is_abbreviated;
                        if (!rest)
                                continue;
@@ -334,7 +334,7 @@ static void check_typos(const char *arg, const struct option *options)
        if (strlen(arg) < 3)
                return;
 
-       if (!prefixcmp(arg, "no-")) {
+       if (starts_with(arg, "no-")) {
                error ("did you mean `--%s` (with two dashes ?)", arg);
                exit(129);
        }
@@ -342,7 +342,7 @@ static void check_typos(const char *arg, const struct option *options)
        for (; options->type != OPTION_END; options++) {
                if (!options->long_name)
                        continue;
-               if (!prefixcmp(options->long_name, arg)) {
+               if (starts_with(options->long_name, arg)) {
                        error ("did you mean `--%s` (with two dashes ?)", arg);
                        exit(129);
                }
index 8736006ed73560ebb05abac2debb04d5117dc114..d670cb9669f213951b9b2cac332c7869be6ca2ee 100644 (file)
@@ -161,9 +161,6 @@ struct option {
          N_("no-op (backward compatibility)"),         \
          PARSE_OPT_HIDDEN | PARSE_OPT_NOARG, parse_opt_noop_cb }
 
-/* Deprecated synonym */
-#define OPT_BOOLEAN OPT_COUNTUP
-
 /* parse_options() will filter out the processed options and leave the
  * non-option arguments in argv[]. usagestr strings should be marked
  * for translation with N_().
@@ -232,15 +229,15 @@ extern int parse_opt_tertiary(const struct option *, const char *, int);
 extern int parse_opt_string_list(const struct option *, const char *, int);
 extern int parse_opt_noop_cb(const struct option *, const char *, int);
 
-#define OPT__VERBOSE(var, h)  OPT_BOOLEAN('v', "verbose", (var), (h))
-#define OPT__QUIET(var, h)    OPT_BOOLEAN('q', "quiet",   (var), (h))
+#define OPT__VERBOSE(var, h)  OPT_COUNTUP('v', "verbose", (var), (h))
+#define OPT__QUIET(var, h)    OPT_COUNTUP('q', "quiet",   (var), (h))
 #define OPT__VERBOSITY(var) \
        { OPTION_CALLBACK, 'v', "verbose", (var), NULL, N_("be more verbose"), \
          PARSE_OPT_NOARG, &parse_opt_verbosity_cb, 0 }, \
        { OPTION_CALLBACK, 'q', "quiet", (var), NULL, N_("be more quiet"), \
          PARSE_OPT_NOARG, &parse_opt_verbosity_cb, 0 }
-#define OPT__DRY_RUN(var, h)  OPT_BOOLEAN('n', "dry-run", (var), (h))
-#define OPT__FORCE(var, h)    OPT_BOOLEAN('f', "force",   (var), (h))
+#define OPT__DRY_RUN(var, h)  OPT_BOOL('n', "dry-run", (var), (h))
+#define OPT__FORCE(var, h)    OPT_COUNTUP('f', "force",   (var), (h))
 #define OPT__ABBREV(var)  \
        { OPTION_CALLBACK, 0, "abbrev", (var), N_("n"), \
          N_("use <n> digits to display SHA-1s"),       \
index 2b7f2e2e09cbaf742970e3e20b7eb9c491933c6f..6cb9fd3014a424d11647e48de3b841012ccc2ffe 100644 (file)
@@ -169,7 +169,7 @@ static unsigned prefix_pathspec(struct pathspec_item *item,
                                        magic |= pathspec_magic[i].bit;
                                        break;
                                }
-                               if (!prefixcmp(copyfrom, "prefix:")) {
+                               if (starts_with(copyfrom, "prefix:")) {
                                        char *endptr;
                                        pathspec_prefix = strtol(copyfrom + 7,
                                                                 &endptr, 10);
index 5273ee886764423df0bacfa9b2fbc9b27c4852c4..6e804a271ca4c6b38b9769f16ace4fb9bc0b88cd 100644 (file)
@@ -1599,6 +1599,7 @@ sub tie_for_persistent_memoization {
                my %lookup_svn_merge_cache;
                my %check_cherry_pick_cache;
                my %has_no_changes_cache;
+               my %_rev_list_cache;
 
                tie_for_persistent_memoization(\%lookup_svn_merge_cache,
                    "$cache_path/lookup_svn_merge");
@@ -1620,6 +1621,14 @@ sub tie_for_persistent_memoization {
                        SCALAR_CACHE => ['HASH' => \%has_no_changes_cache],
                        LIST_CACHE => 'FAULT',
                ;
+
+               tie_for_persistent_memoization(\%_rev_list_cache,
+                   "$cache_path/_rev_list");
+               memoize '_rev_list',
+                       SCALAR_CACHE => 'FAULT',
+                       LIST_CACHE => ['HASH' => \%_rev_list_cache],
+               ;
+
        }
 
        sub unmemoize_svn_mergeinfo_functions {
@@ -1629,6 +1638,7 @@ sub tie_for_persistent_memoization {
                Memoize::unmemoize 'lookup_svn_merge';
                Memoize::unmemoize 'check_cherry_pick';
                Memoize::unmemoize 'has_no_changes';
+               Memoize::unmemoize '_rev_list';
        }
 
        sub clear_memoized_mergeinfo_caches {
@@ -1959,11 +1969,25 @@ sub rebuild_from_rev_db {
        unlink $path or croak "unlink: $!";
 }
 
+#define a global associate map to record rebuild status
+my %rebuild_status;
+#define a global associate map to record rebuild verify status
+my %rebuild_verify_status;
+
 sub rebuild {
        my ($self) = @_;
        my $map_path = $self->map_path;
        my $partial = (-e $map_path && ! -z $map_path);
-       return unless ::verify_ref($self->refname.'^0');
+       my $verify_key = $self->refname.'^0';
+       if (!$rebuild_verify_status{$verify_key}) {
+               my $verify_result = ::verify_ref($verify_key);
+               if ($verify_result) {
+                       $rebuild_verify_status{$verify_key} = 1;
+               }
+       }
+       if (!$rebuild_verify_status{$verify_key}) {
+               return;
+       }
        if (!$partial && ($self->use_svm_props || $self->no_metadata)) {
                my $rev_db = $self->rev_db_path;
                $self->rebuild_from_rev_db($rev_db);
@@ -1977,10 +2001,21 @@ sub rebuild {
        print "Rebuilding $map_path ...\n" if (!$partial);
        my ($base_rev, $head) = ($partial ? $self->rev_map_max_norebuild(1) :
                (undef, undef));
+       my $key_value = ($head ? "$head.." : "") . $self->refname;
+       if (exists $rebuild_status{$key_value}) {
+               print "Done rebuilding $map_path\n" if (!$partial || !$head);
+               my $rev_db_path = $self->rev_db_path;
+               if (-f $self->rev_db_path) {
+                       unlink $self->rev_db_path or croak "unlink: $!";
+               }
+               $self->unlink_rev_db_symlink;
+               return;
+       }
        my ($log, $ctx) =
-           command_output_pipe(qw/rev-list --pretty=raw --reverse/,
-                               ($head ? "$head.." : "") . $self->refname,
+               command_output_pipe(qw/rev-list --pretty=raw --reverse/,
+                               $key_value,
                                '--');
+       $rebuild_status{$key_value} = 1;
        my $metadata_url = $self->metadata_url;
        remove_username($metadata_url);
        my $svn_uuid = $self->rewrite_uuid || $self->ra_uuid;
index b3bcd476daa7d4ab32c30c6fbd2f0a73ec9d0b4f..34e8af966c4a97bd3fafd5d9cfecbfb982f78897 100644 (file)
@@ -304,8 +304,12 @@ sub C {
        my ($self, $m, $deletions) = @_;
        my ($dir, $file) = split_path($m->{file_b});
        my $pbat = $self->ensure_path($dir, $deletions);
+       # workaround for a bug in svn serf backend (v1.8.5 and below):
+       # store third argument to ->add_file() in a local variable, to make it
+       # have the same lifetime as $fbat
+       my $upa = $self->url_path($m->{file_a});
        my $fbat = $self->add_file($self->repo_path($m->{file_b}), $pbat,
-                               $self->url_path($m->{file_a}), $self->{r});
+                               $upa, $self->{r});
        print "\tC\t$m->{file_a} => $m->{file_b}\n" unless $::_q;
        $self->chg_file($fbat, $m);
        $self->close_file($fbat,undef,$self->{pool});
@@ -323,8 +327,10 @@ sub R {
        my ($self, $m, $deletions) = @_;
        my ($dir, $file) = split_path($m->{file_b});
        my $pbat = $self->ensure_path($dir, $deletions);
+       # workaround for a bug in svn serf backend, see comment in C() above
+       my $upa = $self->url_path($m->{file_a});
        my $fbat = $self->add_file($self->repo_path($m->{file_b}), $pbat,
-                               $self->url_path($m->{file_a}), $self->{r});
+                               $upa, $self->{r});
        print "\tR\t$m->{file_a} => $m->{file_b}\n" unless $::_q;
        $self->apply_autoprops($file, $fbat);
        $self->chg_file($fbat, $m);
index 3f8350a57d64ca3335d279c6563fad54b56eb22a..34f2869ab5995e3f9143af8cc2c46a4717ae8be8 100644 (file)
@@ -117,6 +117,7 @@ sub run_pager {
        }
        open STDIN, '<&', $rfd or fatal "Can't redirect stdin: $!";
        $ENV{LESS} ||= 'FRSX';
+       $ENV{LV} ||= '-c';
        exec $pager or fatal "Can't run pager: $! ($pager)";
 }
 
index 70f19501d0e0c0dd625976519fd039d5c7a8b314..bc63b3b80e5b2853f920ba66fe977c7cc0921c78 100644 (file)
@@ -24,8 +24,8 @@ static void packet_trace(const char *buf, unsigned int len, int write)
        strbuf_addf(&out, "packet: %12s%c ",
                    packet_trace_prefix, write ? '>' : '<');
 
-       if ((len >= 4 && !prefixcmp(buf, "PACK")) ||
-           (len >= 5 && !prefixcmp(buf+1, "PACK"))) {
+       if ((len >= 4 && starts_with(buf, "PACK")) ||
+           (len >= 5 && starts_with(buf+1, "PACK"))) {
                strbuf_addstr(&out, "PACK ...");
                unsetenv(trace_key);
        }
index 5e2d709e152d930fbc01633511241b404d2ce252..c0bbb65bf6aa3066da667aa5c69058f0e6b5ded2 100644 (file)
--- a/po/de.po
+++ b/po/de.po
@@ -72,7 +72,7 @@ msgstr "Archivformat"
 
 #: archive.c:328 builtin/log.c:1193
 msgid "prefix"
-msgstr "Prefix"
+msgstr "Präfix"
 
 #: archive.c:329
 msgid "prepend prefix to each pathname in the archive"
@@ -3716,7 +3716,7 @@ msgid ""
 msgstr ""
 "Eingabehilfe:\n"
 "1          - nummeriertes Element auswählen\n"
-"foo        - Element anhand eines eindeutigen Prefix auswählen\n"
+"foo        - Element anhand eines eindeutigen Präfix auswählen\n"
 "           - (leer) nichts auswählen"
 
 #: builtin/clean.c:298
@@ -3734,7 +3734,7 @@ msgstr ""
 "1          - einzelnes Element auswählen\n"
 "3-5        - Bereich von Elementen auswählen\n"
 "2-3,6-9    - mehrere Bereiche auswählen\n"
-"foo        - Element anhand eines eindeutigen Prefix auswählen\n"
+"foo        - Element anhand eines eindeutigen Präfix auswählen\n"
 "-...       - angegebenes Element abwählen\n"
 "*          - alle Elemente auswählen\n"
 "           - (leer) Auswahl beenden"
@@ -6452,7 +6452,7 @@ msgstr "kennzeichnet die Serie als n-te Fassung"
 
 #: builtin/log.c:1194
 msgid "Use [<prefix>] instead of [PATCH]"
-msgstr "verwendet [<Prefix>] anstatt [PATCH]"
+msgstr "verwendet [<Präfix>] anstatt [PATCH]"
 
 #: builtin/log.c:1197
 msgid "store resulting files in <dir>"
@@ -8182,7 +8182,7 @@ msgid ""
 "[-u [--exclude-per-directory=<gitignore>] | -i]] [--no-sparse-checkout] [--"
 "index-output=<file>] (--empty | <tree-ish1> [<tree-ish2> [<tree-ish3>]])"
 msgstr ""
-"git read-tree [[-m [--trivial] [--aggressive] | --reset | --prefix=<Prefix>] "
+"git read-tree [[-m [--trivial] [--aggressive] | --reset | --prefix=<Präfix>] "
 "[-u [--exclude-per-directory=<gitignore>] | -i]] [--no-sparse-checkout] [--"
 "index-output=<Datei>] (--empty | <Commit-Referenz1> [<Commit-Referenz2> "
 "[<Commit-Referenz3>]])"
@@ -9782,15 +9782,15 @@ msgstr "gibt Tag-Inhalte aus"
 
 #: builtin/write-tree.c:13
 msgid "git write-tree [--missing-ok] [--prefix=<prefix>/]"
-msgstr "git write-tree [--missing-ok] [--prefix=<Prefix>/]"
+msgstr "git write-tree [--missing-ok] [--prefix=<Präfix>/]"
 
 #: builtin/write-tree.c:26
 msgid "<prefix>/"
-msgstr "<Prefix>/"
+msgstr "<Präfix>/"
 
 #: builtin/write-tree.c:27
 msgid "write tree object for a subdirectory <prefix>"
-msgstr "schreibt das \"Tree\"-Objekt für ein Unterverzeichnis <Prefix>"
+msgstr "schreibt das \"Tree\"-Objekt für ein Unterverzeichnis <Präfix>"
 
 #: builtin/write-tree.c:30
 msgid "only useful for debugging"
index 962e82be8633217b811a8c8c87b892721b35f662..87db08bd749e74fef0b2372ab71ea98baf57b322 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -40,7 +40,7 @@ static int git_pretty_formats_config(const char *var, const char *value, void *c
        const char *fmt;
        int i;
 
-       if (prefixcmp(var, "pretty."))
+       if (!starts_with(var, "pretty."))
                return 0;
 
        name = var + strlen("pretty.");
@@ -67,7 +67,7 @@ static int git_pretty_formats_config(const char *var, const char *value, void *c
        commit_format->name = xstrdup(name);
        commit_format->format = CMIT_FMT_USERFORMAT;
        git_config_string(&fmt, var, value);
-       if (!prefixcmp(fmt, "format:") || !prefixcmp(fmt, "tformat:")) {
+       if (starts_with(fmt, "format:") || starts_with(fmt, "tformat:")) {
                commit_format->is_tformat = fmt[0] == 't';
                fmt = strchr(fmt, ':') + 1;
        } else if (strchr(fmt, '%'))
@@ -115,7 +115,7 @@ static struct cmt_fmt_map *find_commit_format_recursive(const char *sought,
        for (i = 0; i < commit_formats_len; i++) {
                size_t match_len;
 
-               if (prefixcmp(commit_formats[i].name, sought))
+               if (!starts_with(commit_formats[i].name, sought))
                        continue;
 
                match_len = strlen(commit_formats[i].name);
@@ -151,7 +151,7 @@ void get_commit_format(const char *arg, struct rev_info *rev)
                rev->commit_format = CMIT_FMT_DEFAULT;
                return;
        }
-       if (!prefixcmp(arg, "format:") || !prefixcmp(arg, "tformat:")) {
+       if (starts_with(arg, "format:") || starts_with(arg, "tformat:")) {
                save_user_format(rev, strchr(arg, ':') + 1, arg[0] == 't');
                return;
        }
@@ -840,10 +840,10 @@ static void parse_commit_header(struct format_commit_context *context)
 
                if (i == eol) {
                        break;
-               } else if (!prefixcmp(msg + i, "author ")) {
+               } else if (starts_with(msg + i, "author ")) {
                        context->author.off = i + 7;
                        context->author.len = eol - i - 7;
-               } else if (!prefixcmp(msg + i, "committer ")) {
+               } else if (starts_with(msg + i, "committer ")) {
                        context->committer.off = i + 10;
                        context->committer.len = eol - i - 10;
                }
@@ -983,7 +983,7 @@ static size_t parse_color(struct strbuf *sb, /* in UTF-8 */
 
                if (!end)
                        return 0;
-               if (!prefixcmp(begin, "auto,")) {
+               if (starts_with(begin, "auto,")) {
                        if (!want_color(c->pretty_ctx->color))
                                return end - placeholder + 1;
                        begin += 5;
@@ -994,16 +994,16 @@ static size_t parse_color(struct strbuf *sb, /* in UTF-8 */
                strbuf_addstr(sb, color);
                return end - placeholder + 1;
        }
-       if (!prefixcmp(placeholder + 1, "red")) {
+       if (starts_with(placeholder + 1, "red")) {
                strbuf_addstr(sb, GIT_COLOR_RED);
                return 4;
-       } else if (!prefixcmp(placeholder + 1, "green")) {
+       } else if (starts_with(placeholder + 1, "green")) {
                strbuf_addstr(sb, GIT_COLOR_GREEN);
                return 6;
-       } else if (!prefixcmp(placeholder + 1, "blue")) {
+       } else if (starts_with(placeholder + 1, "blue")) {
                strbuf_addstr(sb, GIT_COLOR_BLUE);
                return 5;
-       } else if (!prefixcmp(placeholder + 1, "reset")) {
+       } else if (starts_with(placeholder + 1, "reset")) {
                strbuf_addstr(sb, GIT_COLOR_RESET);
                return 6;
        } else
@@ -1060,11 +1060,11 @@ static size_t parse_padding_placeholder(struct strbuf *sb,
                        end = strchr(start, ')');
                        if (!end || end == start)
                                return 0;
-                       if (!prefixcmp(start, "trunc)"))
+                       if (starts_with(start, "trunc)"))
                                c->truncate = trunc_right;
-                       else if (!prefixcmp(start, "ltrunc)"))
+                       else if (starts_with(start, "ltrunc)"))
                                c->truncate = trunc_left;
-                       else if (!prefixcmp(start, "mtrunc)"))
+                       else if (starts_with(start, "mtrunc)"))
                                c->truncate = trunc_middle;
                        else
                                return 0;
@@ -1089,7 +1089,7 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
        /* these are independent of the commit */
        switch (placeholder[0]) {
        case 'C':
-               if (!prefixcmp(placeholder + 1, "(auto)")) {
+               if (starts_with(placeholder + 1, "(auto)")) {
                        c->auto_color = 1;
                        return 7; /* consumed 7 bytes, "C(auto)" */
                } else {
@@ -1556,7 +1556,7 @@ static void pp_header(struct pretty_print_context *pp,
                        continue;
                }
 
-               if (!prefixcmp(line, "parent ")) {
+               if (starts_with(line, "parent ")) {
                        if (linelen != 48)
                                die("bad parent line in commit");
                        continue;
@@ -1580,11 +1580,11 @@ static void pp_header(struct pretty_print_context *pp,
                 * FULL shows both authors but not dates.
                 * FULLER shows both authors and dates.
                 */
-               if (!prefixcmp(line, "author ")) {
+               if (starts_with(line, "author ")) {
                        strbuf_grow(sb, linelen + 80);
                        pp_user_info(pp, "Author", sb, line + 7, encoding);
                }
-               if (!prefixcmp(line, "committer ") &&
+               if (starts_with(line, "committer ") &&
                    (pp->fmt == CMIT_FMT_FULL || pp->fmt == CMIT_FMT_FULLER)) {
                        strbuf_grow(sb, linelen + 80);
                        pp_user_info(pp, "Commit", sb, line + 10, encoding);
index d851807feb9849813635471d7099e67ed743a2d7..d7bb17cb663c2f47ad59d34ecea17c6cc977e815 100644 (file)
--- a/prompt.c
+++ b/prompt.c
@@ -22,6 +22,7 @@ static char *do_askpass(const char *cmd, const char *prompt)
        if (start_command(&pass))
                return NULL;
 
+       strbuf_reset(&buffer);
        if (strbuf_read(&buffer, pass.out, 20) < 0)
                err = 1;
 
@@ -38,7 +39,7 @@ static char *do_askpass(const char *cmd, const char *prompt)
 
        strbuf_setlen(&buffer, strcspn(buffer.buf, "\r\n"));
 
-       return strbuf_detach(&buffer, NULL);
+       return buffer.buf;
 }
 
 char *git_prompt(const char *prompt, int flags)
diff --git a/refs.c b/refs.c
index 5e5a3824b992132aaac8d86698feebcb773e13c6..89228e23732ef7e2e83a2e84e82ffb5bd7afec3f 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -637,7 +637,7 @@ static int do_one_ref(struct ref_entry *entry, void *cb_data)
        struct ref_entry *old_current_ref;
        int retval;
 
-       if (prefixcmp(entry->name, data->base))
+       if (!starts_with(entry->name, data->base))
                return 0;
 
        if (!(data->flags & DO_FOR_EACH_INCLUDE_BROKEN) &&
@@ -1042,7 +1042,7 @@ static void read_packed_refs(FILE *f, struct ref_dir *dir)
                if (refname) {
                        last = create_ref_entry(refname, sha1, REF_ISPACKED, 1);
                        if (peeled == PEELED_FULLY ||
-                           (peeled == PEELED_TAGS && !prefixcmp(refname, "refs/tags/")))
+                           (peeled == PEELED_TAGS && starts_with(refname, "refs/tags/")))
                                last->flag |= REF_KNOWS_PEELED;
                        add_ref(dir, last);
                        continue;
@@ -1376,7 +1376,7 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
                                        return NULL;
                        }
                        buffer[len] = 0;
-                       if (!prefixcmp(buffer, "refs/") &&
+                       if (starts_with(buffer, "refs/") &&
                                        !check_refname_format(buffer, 0)) {
                                strcpy(refname_buffer, buffer);
                                refname = refname_buffer;
@@ -1415,7 +1415,7 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
                /*
                 * Is it a symbolic ref?
                 */
-               if (prefixcmp(buffer, "ref:")) {
+               if (!starts_with(buffer, "ref:")) {
                        /*
                         * Please note that FETCH_HEAD has a second
                         * line containing other data.
@@ -1837,7 +1837,7 @@ int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,
        struct ref_filter filter;
        int ret;
 
-       if (!prefix && prefixcmp(pattern, "refs/"))
+       if (!prefix && !starts_with(pattern, "refs/"))
                strbuf_addstr(&real_pattern, "refs/");
        else if (prefix)
                strbuf_addstr(&real_pattern, prefix);
@@ -1874,13 +1874,13 @@ int for_each_rawref(each_ref_fn fn, void *cb_data)
 const char *prettify_refname(const char *name)
 {
        return name + (
-               !prefixcmp(name, "refs/heads/") ? 11 :
-               !prefixcmp(name, "refs/tags/") ? 10 :
-               !prefixcmp(name, "refs/remotes/") ? 13 :
+               starts_with(name, "refs/heads/") ? 11 :
+               starts_with(name, "refs/tags/") ? 10 :
+               starts_with(name, "refs/remotes/") ? 13 :
                0);
 }
 
-const char *ref_rev_parse_rules[] = {
+static const char *ref_rev_parse_rules[] = {
        "%.*s",
        "refs/%.*s",
        "refs/tags/%.*s",
@@ -1890,12 +1890,12 @@ const char *ref_rev_parse_rules[] = {
        NULL
 };
 
-int refname_match(const char *abbrev_name, const char *full_name, const char **rules)
+int refname_match(const char *abbrev_name, const char *full_name)
 {
        const char **p;
        const int abbrev_name_len = strlen(abbrev_name);
 
-       for (p = rules; *p; p++) {
+       for (p = ref_rev_parse_rules; *p; p++) {
                if (!strcmp(full_name, mkpath(*p, abbrev_name_len, abbrev_name))) {
                        return 1;
                }
@@ -2039,6 +2039,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
        int type, lflags;
        int mustexist = (old_sha1 && !is_null_sha1(old_sha1));
        int missing = 0;
+       int attempts_remaining = 3;
 
        lock = xcalloc(1, sizeof(struct ref_lock));
        lock->lock_fd = -1;
@@ -2080,7 +2081,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
 
        lock->lk = xcalloc(1, sizeof(struct lock_file));
 
-       lflags = LOCK_DIE_ON_ERROR;
+       lflags = 0;
        if (flags & REF_NODEREF) {
                refname = orig_refname;
                lflags |= LOCK_NODEREF;
@@ -2093,13 +2094,32 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
        if ((flags & REF_NODEREF) && (type & REF_ISSYMREF))
                lock->force_write = 1;
 
-       if (safe_create_leading_directories(ref_file)) {
+ retry:
+       switch (safe_create_leading_directories(ref_file)) {
+       case SCLD_OK:
+               break; /* success */
+       case SCLD_VANISHED:
+               if (--attempts_remaining > 0)
+                       goto retry;
+               /* fall through */
+       default:
                last_errno = errno;
                error("unable to create directory for %s", ref_file);
                goto error_return;
        }
 
        lock->lock_fd = hold_lock_file_for_update(lock->lk, ref_file, lflags);
+       if (lock->lock_fd < 0) {
+               if (errno == ENOENT && --attempts_remaining > 0)
+                       /*
+                        * Maybe somebody just deleted one of the
+                        * directories leading to ref_file.  Try
+                        * again:
+                        */
+                       goto retry;
+               else
+                       unable_to_lock_index_die(ref_file, errno);
+       }
        return old_sha1 ? verify_lock(lock, old_sha1, mustexist) : lock;
 
  error_return:
@@ -2244,7 +2264,7 @@ static int pack_if_possible_fn(struct ref_entry *entry, void *cb_data)
        struct pack_refs_cb_data *cb = cb_data;
        enum peel_status peel_status;
        struct ref_entry *packed_entry;
-       int is_tag_ref = !prefixcmp(entry->name, "refs/tags/");
+       int is_tag_ref = starts_with(entry->name, "refs/tags/");
 
        /* ALWAYS pack tags */
        if (!(cb->flags & PACK_REFS_ALL) && !is_tag_ref)
@@ -2508,6 +2528,51 @@ int delete_ref(const char *refname, const unsigned char *sha1, int delopt)
  */
 #define TMP_RENAMED_LOG  "logs/refs/.tmp-renamed-log"
 
+static int rename_tmp_log(const char *newrefname)
+{
+       int attempts_remaining = 4;
+
+ retry:
+       switch (safe_create_leading_directories(git_path("logs/%s", newrefname))) {
+       case SCLD_OK:
+               break; /* success */
+       case SCLD_VANISHED:
+               if (--attempts_remaining > 0)
+                       goto retry;
+               /* fall through */
+       default:
+               error("unable to create directory for %s", newrefname);
+               return -1;
+       }
+
+       if (rename(git_path(TMP_RENAMED_LOG), git_path("logs/%s", newrefname))) {
+               if ((errno==EISDIR || errno==ENOTDIR) && --attempts_remaining > 0) {
+                       /*
+                        * rename(a, b) when b is an existing
+                        * directory ought to result in ISDIR, but
+                        * Solaris 5.8 gives ENOTDIR.  Sheesh.
+                        */
+                       if (remove_empty_directories(git_path("logs/%s", newrefname))) {
+                               error("Directory not empty: logs/%s", newrefname);
+                               return -1;
+                       }
+                       goto retry;
+               } else if (errno == ENOENT && --attempts_remaining > 0) {
+                       /*
+                        * Maybe another process just deleted one of
+                        * the directories in the path to newrefname.
+                        * Try again from the beginning.
+                        */
+                       goto retry;
+               } else {
+                       error("unable to move logfile "TMP_RENAMED_LOG" to logs/%s: %s",
+                               newrefname, strerror(errno));
+                       return -1;
+               }
+       }
+       return 0;
+}
+
 int rename_ref(const char *oldrefname, const char *newrefname, const char *logmsg)
 {
        unsigned char sha1[20], orig_sha1[20];
@@ -2555,30 +2620,9 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms
                }
        }
 
-       if (log && safe_create_leading_directories(git_path("logs/%s", newrefname))) {
-               error("unable to create directory for %s", newrefname);
+       if (log && rename_tmp_log(newrefname))
                goto rollback;
-       }
 
- retry:
-       if (log && rename(git_path(TMP_RENAMED_LOG), git_path("logs/%s", newrefname))) {
-               if (errno==EISDIR || errno==ENOTDIR) {
-                       /*
-                        * rename(a, b) when b is an existing
-                        * directory ought to result in ISDIR, but
-                        * Solaris 5.8 gives ENOTDIR.  Sheesh.
-                        */
-                       if (remove_empty_directories(git_path("logs/%s", newrefname))) {
-                               error("Directory not empty: logs/%s", newrefname);
-                               goto rollback;
-                       }
-                       goto retry;
-               } else {
-                       error("unable to move logfile "TMP_RENAMED_LOG" to logs/%s: %s",
-                               newrefname, strerror(errno));
-                       goto rollback;
-               }
-       }
        logmoved = log;
 
        lock = lock_ref_sha1_basic(newrefname, NULL, 0, NULL);
@@ -2679,9 +2723,9 @@ int log_ref_setup(const char *refname, char *logfile, int bufsize)
 
        git_snpath(logfile, bufsize, "logs/%s", refname);
        if (log_all_ref_updates &&
-           (!prefixcmp(refname, "refs/heads/") ||
-            !prefixcmp(refname, "refs/remotes/") ||
-            !prefixcmp(refname, "refs/notes/") ||
+           (starts_with(refname, "refs/heads/") ||
+            starts_with(refname, "refs/remotes/") ||
+            starts_with(refname, "refs/notes/") ||
             !strcmp(refname, "HEAD"))) {
                if (safe_create_leading_directories(logfile) < 0)
                        return error("unable to create directory for %s",
@@ -2751,7 +2795,7 @@ static int log_ref_write(const char *refname, const unsigned char *old_sha1,
 
 static int is_branch(const char *refname)
 {
-       return !strcmp(refname, "HEAD") || !prefixcmp(refname, "refs/heads/");
+       return !strcmp(refname, "HEAD") || starts_with(refname, "refs/heads/");
 }
 
 int write_ref_sha1(struct ref_lock *lock,
@@ -3334,29 +3378,6 @@ int update_refs(const char *action, const struct ref_update **updates_orig,
        return ret;
 }
 
-/*
- * generate a format suitable for scanf from a ref_rev_parse_rules
- * rule, that is replace the "%.*s" spec with a "%s" spec
- */
-static void gen_scanf_fmt(char *scanf_fmt, const char *rule)
-{
-       char *spec;
-
-       spec = strstr(rule, "%.*s");
-       if (!spec || strstr(spec + 4, "%.*s"))
-               die("invalid rule in ref_rev_parse_rules: %s", rule);
-
-       /* copy all until spec */
-       strncpy(scanf_fmt, rule, spec - rule);
-       scanf_fmt[spec - rule] = '\0';
-       /* copy new spec */
-       strcat(scanf_fmt, "%s");
-       /* copy remaining rule */
-       strcat(scanf_fmt, spec + 4);
-
-       return;
-}
-
 char *shorten_unambiguous_ref(const char *refname, int strict)
 {
        int i;
@@ -3364,23 +3385,29 @@ char *shorten_unambiguous_ref(const char *refname, int strict)
        static int nr_rules;
        char *short_name;
 
-       /* pre generate scanf formats from ref_rev_parse_rules[] */
        if (!nr_rules) {
+               /*
+                * Pre-generate scanf formats from ref_rev_parse_rules[].
+                * Generate a format suitable for scanf from a
+                * ref_rev_parse_rules rule by interpolating "%s" at the
+                * location of the "%.*s".
+                */
                size_t total_len = 0;
+               size_t offset = 0;
 
                /* the rule list is NULL terminated, count them first */
                for (nr_rules = 0; ref_rev_parse_rules[nr_rules]; nr_rules++)
-                       /* no +1 because strlen("%s") < strlen("%.*s") */
-                       total_len += strlen(ref_rev_parse_rules[nr_rules]);
+                       /* -2 for strlen("%.*s") - strlen("%s"); +1 for NUL */
+                       total_len += strlen(ref_rev_parse_rules[nr_rules]) - 2 + 1;
 
                scanf_fmts = xmalloc(nr_rules * sizeof(char *) + total_len);
 
-               total_len = 0;
+               offset = 0;
                for (i = 0; i < nr_rules; i++) {
-                       scanf_fmts[i] = (char *)&scanf_fmts[nr_rules]
-                                       + total_len;
-                       gen_scanf_fmt(scanf_fmts[i], ref_rev_parse_rules[i]);
-                       total_len += strlen(ref_rev_parse_rules[i]);
+                       assert(offset < total_len);
+                       scanf_fmts[i] = (char *)&scanf_fmts[nr_rules] + offset;
+                       offset += snprintf(scanf_fmts[i], total_len - offset,
+                                          ref_rev_parse_rules[i], 2, "%s") + 1;
                }
        }
 
@@ -3450,7 +3477,7 @@ int parse_hide_refs_config(const char *var, const char *value, const char *secti
 {
        if (!strcmp("transfer.hiderefs", var) ||
            /* NEEDSWORK: use parse_config_key() once both are merged */
-           (!prefixcmp(var, section) && var[strlen(section)] == '.' &&
+           (starts_with(var, section) && var[strlen(section)] == '.' &&
             !strcmp(var + strlen(section), ".hiderefs"))) {
                char *ref;
                int len;
@@ -3478,7 +3505,7 @@ int ref_is_hidden(const char *refname)
                return 0;
        for_each_string_list_item(item, hide_refs) {
                int len;
-               if (prefixcmp(refname, item->string))
+               if (!starts_with(refname, item->string))
                        continue;
                len = strlen(item->string);
                if (!refname[len] || refname[len] == '/')
index 91b07a41456fe5cdb2157a648353d982b88f7e9a..10cb0114eafdfd9760b3fdb3c0217d801195ba13 100644 (file)
@@ -10,6 +10,7 @@
 #include "sideband.h"
 #include "argv-array.h"
 #include "credential.h"
+#include "sha1-array.h"
 
 static struct remote *remote;
 /* always ends with a trailing slash */
@@ -20,6 +21,8 @@ struct options {
        unsigned long depth;
        unsigned progress : 1,
                check_self_contained_and_connected : 1,
+               cloning : 1,
+               update_shallow : 1,
                followtags : 1,
                dry_run : 1,
                thin : 1;
@@ -87,8 +90,23 @@ static int set_option(const char *name, const char *value)
                string_list_append(&cas_options, val.buf);
                strbuf_release(&val);
                return 0;
-       }
-       else {
+       } else if (!strcmp(name, "cloning")) {
+               if (!strcmp(value, "true"))
+                       options.cloning = 1;
+               else if (!strcmp(value, "false"))
+                       options.cloning = 0;
+               else
+                       return -1;
+               return 0;
+       } else if (!strcmp(name, "update-shallow")) {
+               if (!strcmp(value, "true"))
+                       options.update_shallow = 1;
+               else if (!strcmp(value, "false"))
+                       options.update_shallow = 0;
+               else
+                       return -1;
+               return 0;
+       } else {
                return 1 /* unsupported */;
        }
 }
@@ -99,6 +117,7 @@ struct discovery {
        char *buf;
        size_t len;
        struct ref *refs;
+       struct sha1_array shallow;
        unsigned proto_git : 1;
 };
 static struct discovery *last_discovery;
@@ -107,7 +126,7 @@ static struct ref *parse_git_refs(struct discovery *heads, int for_push)
 {
        struct ref *list = NULL;
        get_remote_heads(-1, heads->buf, heads->len, &list,
-                        for_push ? REF_NORMAL : 0, NULL);
+                        for_push ? REF_NORMAL : 0, NULL, &heads->shallow);
        return list;
 }
 
@@ -168,6 +187,7 @@ static void free_discovery(struct discovery *d)
        if (d) {
                if (d == last_discovery)
                        last_discovery = NULL;
+               free(d->shallow.sha1);
                free(d->buf_alloc);
                free_refs(d->refs);
                free(d);
@@ -217,7 +237,7 @@ static struct discovery* discover_refs(const char *service, int for_push)
        free_discovery(last);
 
        strbuf_addf(&refs_url, "%sinfo/refs", url.buf);
-       if ((!prefixcmp(url.buf, "http://") || !prefixcmp(url.buf, "https://")) &&
+       if ((starts_with(url.buf, "http://") || starts_with(url.buf, "https://")) &&
             git_env_bool("GIT_SMART_HTTP", 1)) {
                maybe_smart = 1;
                if (!strchr(url.buf, '?'))
@@ -699,7 +719,7 @@ static int fetch_git(struct discovery *heads,
        struct strbuf preamble = STRBUF_INIT;
        char *depth_arg = NULL;
        int argc = 0, i, err;
-       const char *argv[16];
+       const char *argv[17];
 
        argv[argc++] = "fetch-pack";
        argv[argc++] = "--stateless-rpc";
@@ -715,6 +735,10 @@ static int fetch_git(struct discovery *heads,
        }
        if (options.check_self_contained_and_connected)
                argv[argc++] = "--check-self-contained-and-connected";
+       if (options.cloning)
+               argv[argc++] = "--cloning";
+       if (options.update_shallow)
+               argv[argc++] = "--update-shallow";
        if (!options.progress)
                argv[argc++] = "--no-progress";
        if (options.depth) {
@@ -730,7 +754,8 @@ static int fetch_git(struct discovery *heads,
                struct ref *ref = to_fetch[i];
                if (!ref->name || !*ref->name)
                        die("cannot fetch by sha1 over smart http");
-               packet_buf_write(&preamble, "%s\n", ref->name);
+               packet_buf_write(&preamble, "%s %s\n",
+                                sha1_to_hex(ref->old_sha1), ref->name);
        }
        packet_buf_flush(&preamble);
 
@@ -766,7 +791,7 @@ static void parse_fetch(struct strbuf *buf)
        int alloc_heads = 0, nr_heads = 0;
 
        do {
-               if (!prefixcmp(buf->buf, "fetch ")) {
+               if (starts_with(buf->buf, "fetch ")) {
                        char *p = buf->buf + strlen("fetch ");
                        char *name;
                        struct ref *ref;
@@ -889,7 +914,7 @@ static void parse_push(struct strbuf *buf)
        int alloc_spec = 0, nr_spec = 0, i, ret;
 
        do {
-               if (!prefixcmp(buf->buf, "push ")) {
+               if (starts_with(buf->buf, "push ")) {
                        ALLOC_GROW(specs, nr_spec + 1, alloc_spec);
                        specs[nr_spec++] = xstrdup(buf->buf + 5);
                }
@@ -952,19 +977,19 @@ int main(int argc, const char **argv)
                }
                if (buf.len == 0)
                        break;
-               if (!prefixcmp(buf.buf, "fetch ")) {
+               if (starts_with(buf.buf, "fetch ")) {
                        if (nongit)
                                die("Fetch attempted without a local repo");
                        parse_fetch(&buf);
 
-               } else if (!strcmp(buf.buf, "list") || !prefixcmp(buf.buf, "list ")) {
+               } else if (!strcmp(buf.buf, "list") || starts_with(buf.buf, "list ")) {
                        int for_push = !!strstr(buf.buf + 4, "for-push");
                        output_refs(get_refs(for_push));
 
-               } else if (!prefixcmp(buf.buf, "push ")) {
+               } else if (starts_with(buf.buf, "push ")) {
                        parse_push(&buf);
 
-               } else if (!prefixcmp(buf.buf, "option ")) {
+               } else if (starts_with(buf.buf, "option ")) {
                        char *name = buf.buf + strlen("option ");
                        char *value = strchr(name, ' ');
                        int result;
index d7cd5d272fd23624223f37ec8bda7d4fa8c39bce..078f1ffa417092e1c783b16d782b2aa69417a591 100644 (file)
@@ -82,7 +82,7 @@ static int parse_rev_note(const char *msg, struct rev_note *res)
                len = end ? end - msg : strlen(msg);
 
                key = "Revision-number: ";
-               if (!prefixcmp(msg, key)) {
+               if (starts_with(msg, key)) {
                        long i;
                        char *end;
                        value = msg + strlen(key);
@@ -154,7 +154,7 @@ static void check_or_regenerate_marks(int latestrev)
        } else {
                strbuf_addf(&sb, ":%d ", latestrev);
                while (strbuf_getline(&line, marksfile, '\n') != EOF) {
-                       if (!prefixcmp(line.buf, sb.buf)) {
+                       if (starts_with(line.buf, sb.buf)) {
                                found++;
                                break;
                        }
@@ -264,7 +264,7 @@ static int do_command(struct strbuf *line)
                return 1;       /* end of command stream, quit */
        }
        if (batch_cmd) {
-               if (prefixcmp(batch_cmd->name, line->buf))
+               if (!starts_with(batch_cmd->name, line->buf))
                        die("Active %s batch interrupted by %s", batch_cmd->name, line->buf);
                /* buffer batch lines */
                string_list_append(&batchlines, line->buf);
@@ -272,7 +272,7 @@ static int do_command(struct strbuf *line)
        }
 
        for (p = input_command_list; p->name; p++) {
-               if (!prefixcmp(line->buf, p->name) && (strlen(p->name) == line->len ||
+               if (starts_with(line->buf, p->name) && (strlen(p->name) == line->len ||
                                line->buf[strlen(p->name)] == ' ')) {
                        if (p->batchable) {
                                batch_cmd = p;
@@ -304,7 +304,7 @@ int main(int argc, char **argv)
        remote = remote_get(argv[1]);
        url_in = (argc == 3) ? argv[2] : remote->url[0];
 
-       if (!prefixcmp(url_in, "file://")) {
+       if (starts_with(url_in, "file://")) {
                dump_from_file = 1;
                url = url_decode(url_in + sizeof("file://")-1);
        } else {
index 9f1a8aa2c499ae5d2eb8f76f8218eb62937eea51..e41251ee77af1426dd26d4b13ee61a39cffccdeb 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -76,7 +76,7 @@ static const char *alias_url(const char *url, struct rewrites *r)
                if (!r->rewrite[i])
                        continue;
                for (j = 0; j < r->rewrite[i]->instead_of_nr; j++) {
-                       if (!prefixcmp(url, r->rewrite[i]->instead_of[j].s) &&
+                       if (starts_with(url, r->rewrite[i]->instead_of[j].s) &&
                            (!longest ||
                             longest->len < r->rewrite[i]->instead_of[j].len)) {
                                longest = &(r->rewrite[i]->instead_of[j]);
@@ -239,13 +239,13 @@ static void read_remotes_file(struct remote *remote)
                int value_list;
                char *s, *p;
 
-               if (!prefixcmp(buffer, "URL:")) {
+               if (starts_with(buffer, "URL:")) {
                        value_list = 0;
                        s = buffer + 4;
-               } else if (!prefixcmp(buffer, "Push:")) {
+               } else if (starts_with(buffer, "Push:")) {
                        value_list = 1;
                        s = buffer + 5;
-               } else if (!prefixcmp(buffer, "Pull:")) {
+               } else if (starts_with(buffer, "Pull:")) {
                        value_list = 2;
                        s = buffer + 5;
                } else
@@ -337,7 +337,7 @@ static int handle_config(const char *key, const char *value, void *cb)
        const char *subkey;
        struct remote *remote;
        struct branch *branch;
-       if (!prefixcmp(key, "branch.")) {
+       if (starts_with(key, "branch.")) {
                name = key + 7;
                subkey = strrchr(name, '.');
                if (!subkey)
@@ -361,7 +361,7 @@ static int handle_config(const char *key, const char *value, void *cb)
                }
                return 0;
        }
-       if (!prefixcmp(key, "url.")) {
+       if (starts_with(key, "url.")) {
                struct rewrite *rewrite;
                name = key + 4;
                subkey = strrchr(name, '.');
@@ -380,7 +380,7 @@ static int handle_config(const char *key, const char *value, void *cb)
                }
        }
 
-       if (prefixcmp(key,  "remote."))
+       if (!starts_with(key,  "remote."))
                return 0;
        name = key + 7;
 
@@ -487,7 +487,7 @@ static void read_config(void)
        current_branch = NULL;
        head_ref = resolve_ref_unsafe("HEAD", sha1, 0, &flag);
        if (head_ref && (flag & REF_ISSYMREF) &&
-           !prefixcmp(head_ref, "refs/heads/")) {
+           starts_with(head_ref, "refs/heads/")) {
                current_branch =
                        make_branch(head_ref + strlen("refs/heads/"), 0);
        }
@@ -745,35 +745,66 @@ int for_each_remote(each_remote_fn fn, void *priv)
        return result;
 }
 
-void ref_remove_duplicates(struct ref *ref_map)
+static void handle_duplicate(struct ref *ref1, struct ref *ref2)
+{
+       if (strcmp(ref1->name, ref2->name)) {
+               if (ref1->fetch_head_status != FETCH_HEAD_IGNORE &&
+                   ref2->fetch_head_status != FETCH_HEAD_IGNORE) {
+                       die(_("Cannot fetch both %s and %s to %s"),
+                           ref1->name, ref2->name, ref2->peer_ref->name);
+               } else if (ref1->fetch_head_status != FETCH_HEAD_IGNORE &&
+                          ref2->fetch_head_status == FETCH_HEAD_IGNORE) {
+                       warning(_("%s usually tracks %s, not %s"),
+                               ref2->peer_ref->name, ref2->name, ref1->name);
+               } else if (ref1->fetch_head_status == FETCH_HEAD_IGNORE &&
+                          ref2->fetch_head_status == FETCH_HEAD_IGNORE) {
+                       die(_("%s tracks both %s and %s"),
+                           ref2->peer_ref->name, ref1->name, ref2->name);
+               } else {
+                       /*
+                        * This last possibility doesn't occur because
+                        * FETCH_HEAD_IGNORE entries always appear at
+                        * the end of the list.
+                        */
+                       die(_("Internal error"));
+               }
+       }
+       free(ref2->peer_ref);
+       free(ref2);
+}
+
+struct ref *ref_remove_duplicates(struct ref *ref_map)
 {
        struct string_list refs = STRING_LIST_INIT_NODUP;
-       struct string_list_item *item = NULL;
-       struct ref *prev = NULL, *next = NULL;
-       for (; ref_map; prev = ref_map, ref_map = next) {
-               next = ref_map->next;
-               if (!ref_map->peer_ref)
-                       continue;
+       struct ref *retval = NULL;
+       struct ref **p = &retval;
 
-               item = string_list_lookup(&refs, ref_map->peer_ref->name);
-               if (item) {
-                       if (strcmp(((struct ref *)item->util)->name,
-                                  ref_map->name))
-                               die("%s tracks both %s and %s",
-                                   ref_map->peer_ref->name,
-                                   ((struct ref *)item->util)->name,
-                                   ref_map->name);
-                       prev->next = ref_map->next;
-                       free(ref_map->peer_ref);
-                       free(ref_map);
-                       ref_map = prev; /* skip this; we freed it */
-                       continue;
-               }
+       while (ref_map) {
+               struct ref *ref = ref_map;
+
+               ref_map = ref_map->next;
+               ref->next = NULL;
 
-               item = string_list_insert(&refs, ref_map->peer_ref->name);
-               item->util = ref_map;
+               if (!ref->peer_ref) {
+                       *p = ref;
+                       p = &ref->next;
+               } else {
+                       struct string_list_item *item =
+                               string_list_insert(&refs, ref->peer_ref->name);
+
+                       if (item->util) {
+                               /* Entry already existed */
+                               handle_duplicate((struct ref *)item->util, ref);
+                       } else {
+                               *p = ref;
+                               p = &ref->next;
+                               item->util = ref;
+                       }
+               }
        }
+
        string_list_clear(&refs, 0);
+       return retval;
 }
 
 int remote_has_url(struct remote *remote, const char *url)
@@ -821,10 +852,12 @@ static int match_name_with_pattern(const char *key, const char *name,
        return ret;
 }
 
-static int query_refspecs(struct refspec *refs, int ref_count, struct refspec *query)
+int query_refspecs(struct refspec *refs, int ref_count, struct refspec *query)
 {
        int i;
        int find_src = !query->src;
+       const char *needle = find_src ? query->dst : query->src;
+       char **result = find_src ? &query->src : &query->dst;
 
        if (find_src && !query->dst)
                return error("query_refspecs: need either src or dst");
@@ -833,8 +866,6 @@ static int query_refspecs(struct refspec *refs, int ref_count, struct refspec *q
                struct refspec *refspec = &refs[i];
                const char *key = find_src ? refspec->dst : refspec->src;
                const char *value = find_src ? refspec->src : refspec->dst;
-               const char *needle = find_src ? query->dst : query->src;
-               char **result = find_src ? &query->src : &query->dst;
 
                if (!refspec->dst)
                        continue;
@@ -955,9 +986,9 @@ void sort_ref_list(struct ref **l, int (*cmp)(const void *, const void *))
        *l = llist_mergesort(*l, ref_list_get_next, ref_list_set_next, cmp);
 }
 
-static int count_refspec_match(const char *pattern,
-                              struct ref *refs,
-                              struct ref **matched_ref)
+int count_refspec_match(const char *pattern,
+                       struct ref *refs,
+                       struct ref **matched_ref)
 {
        int patlen = strlen(pattern);
        struct ref *matched_weak = NULL;
@@ -969,7 +1000,7 @@ static int count_refspec_match(const char *pattern,
                char *name = refs->name;
                int namelen = strlen(name);
 
-               if (!refname_match(pattern, name, ref_rev_parse_rules))
+               if (!refname_match(pattern, name))
                        continue;
 
                /* A match is "weak" if it is with refs outside
@@ -982,8 +1013,8 @@ static int count_refspec_match(const char *pattern,
                 */
                if (namelen != patlen &&
                    patlen != namelen - 5 &&
-                   prefixcmp(name, "refs/heads/") &&
-                   prefixcmp(name, "refs/tags/")) {
+                   !starts_with(name, "refs/heads/") &&
+                   !starts_with(name, "refs/tags/")) {
                        /* We want to catch the case where only weak
                         * matches are found and there are multiple
                         * matches, and where more than one strong
@@ -1054,9 +1085,9 @@ static char *guess_ref(const char *name, struct ref *peer)
        if (!r)
                return NULL;
 
-       if (!prefixcmp(r, "refs/heads/"))
+       if (starts_with(r, "refs/heads/"))
                strbuf_addstr(&buf, "refs/heads/");
-       else if (!prefixcmp(r, "refs/tags/"))
+       else if (starts_with(r, "refs/tags/"))
                strbuf_addstr(&buf, "refs/tags/");
        else
                return NULL;
@@ -1104,7 +1135,7 @@ static int match_explicit(struct ref *src, struct ref *dst,
                dst_value = resolve_ref_unsafe(matched_src->name, sha1, 1, &flag);
                if (!dst_value ||
                    ((flag & REF_ISSYMREF) &&
-                    prefixcmp(dst_value, "refs/heads/")))
+                    !starts_with(dst_value, "refs/heads/")))
                        die("%s cannot be resolved to branch.",
                            matched_src->name);
        }
@@ -1193,7 +1224,7 @@ static char *get_ref_match(const struct refspec *rs, int rs_nr, const struct ref
                 * including refs outside refs/heads/ hierarchy, but
                 * that does not make much sense these days.
                 */
-               if (!send_mirror && prefixcmp(ref->name, "refs/heads/"))
+               if (!send_mirror && !starts_with(ref->name, "refs/heads/"))
                        return NULL;
                name = xstrdup(ref->name);
        }
@@ -1248,7 +1279,7 @@ static void add_missing_tags(struct ref *src, struct ref **dst, struct ref ***ds
                        add_to_tips(&sent_tips, ref->peer_ref->new_sha1);
                else
                        add_to_tips(&sent_tips, ref->old_sha1);
-               if (!prefixcmp(ref->name, "refs/tags/"))
+               if (starts_with(ref->name, "refs/tags/"))
                        string_list_append(&dst_tag, ref->name);
        }
        clear_commit_marks_many(sent_tips.nr, sent_tips.tip, TMP_MARK);
@@ -1257,7 +1288,7 @@ static void add_missing_tags(struct ref *src, struct ref **dst, struct ref ***ds
 
        /* Collect tags they do not have. */
        for (ref = src; ref; ref = ref->next) {
-               if (prefixcmp(ref->name, "refs/tags/"))
+               if (!starts_with(ref->name, "refs/tags/"))
                        continue; /* not a tag */
                if (string_list_has_string(&dst_tag, ref->name))
                        continue; /* they already have it */
@@ -1481,7 +1512,7 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
                 */
 
                else if (!ref->deletion && !is_null_sha1(ref->old_sha1)) {
-                       if (!prefixcmp(ref->name, "refs/tags/"))
+                       if (starts_with(ref->name, "refs/tags/"))
                                reject_reason = REF_STATUS_REJECT_ALREADY_EXISTS;
                        else if (!has_sha1_file(ref->old_sha1))
                                reject_reason = REF_STATUS_REJECT_FETCH_FIRST;
@@ -1540,7 +1571,7 @@ int branch_merge_matches(struct branch *branch,
 {
        if (!branch || i < 0 || i >= branch->merge_nr)
                return 0;
-       return refname_match(branch->merge[i]->src, refname, ref_fetch_rules);
+       return refname_match(branch->merge[i]->src, refname);
 }
 
 static int ignore_symref_update(const char *refname)
@@ -1553,6 +1584,13 @@ static int ignore_symref_update(const char *refname)
        return (flag & REF_ISSYMREF);
 }
 
+/*
+ * Create and return a list of (struct ref) consisting of copies of
+ * each remote_ref that matches refspec.  refspec must be a pattern.
+ * Fill in the copies' peer_ref to describe the local tracking refs to
+ * which they map.  Omit any references that would map to an existing
+ * local symbolic ref.
+ */
 static struct ref *get_expanded_map(const struct ref *remote_refs,
                                    const struct refspec *refspec)
 {
@@ -1560,9 +1598,9 @@ static struct ref *get_expanded_map(const struct ref *remote_refs,
        struct ref *ret = NULL;
        struct ref **tail = &ret;
 
-       char *expn_name;
-
        for (ref = remote_refs; ref; ref = ref->next) {
+               char *expn_name = NULL;
+
                if (strchr(ref->name, '^'))
                        continue; /* a dereference item */
                if (match_name_with_pattern(refspec->src, ref->name,
@@ -1571,12 +1609,12 @@ static struct ref *get_expanded_map(const struct ref *remote_refs,
                        struct ref *cpy = copy_ref(ref);
 
                        cpy->peer_ref = alloc_ref(expn_name);
-                       free(expn_name);
                        if (refspec->force)
                                cpy->peer_ref->force = 1;
                        *tail = cpy;
                        tail = &cpy->next;
                }
+               free(expn_name);
        }
 
        return ret;
@@ -1586,7 +1624,7 @@ static const struct ref *find_ref_by_name_abbrev(const struct ref *refs, const c
 {
        const struct ref *ref;
        for (ref = refs; ref; ref = ref->next) {
-               if (refname_match(name, ref->name, ref_fetch_rules))
+               if (refname_match(name, ref->name))
                        return ref;
        }
        return NULL;
@@ -1607,12 +1645,12 @@ static struct ref *get_local_ref(const char *name)
        if (!name || name[0] == '\0')
                return NULL;
 
-       if (!prefixcmp(name, "refs/"))
+       if (starts_with(name, "refs/"))
                return alloc_ref(name);
 
-       if (!prefixcmp(name, "heads/") ||
-           !prefixcmp(name, "tags/") ||
-           !prefixcmp(name, "remotes/"))
+       if (starts_with(name, "heads/") ||
+           starts_with(name, "tags/") ||
+           starts_with(name, "remotes/"))
                return alloc_ref_with_prefix("refs/", 5, name);
 
        return alloc_ref_with_prefix("refs/heads/", 11, name);
@@ -1647,7 +1685,7 @@ int get_fetch_map(const struct ref *remote_refs,
 
        for (rmp = &ref_map; *rmp; ) {
                if ((*rmp)->peer_ref) {
-                       if (prefixcmp((*rmp)->peer_ref->name, "refs/") ||
+                       if (!starts_with((*rmp)->peer_ref->name, "refs/") ||
                            check_refname_format((*rmp)->peer_ref->name, 0)) {
                                struct ref *ignore = *rmp;
                                error("* Ignoring funny ref '%s' locally",
@@ -1931,7 +1969,7 @@ struct ref *guess_remote_head(const struct ref *head,
        /* Look for another ref that points there */
        for (r = refs; r; r = r->next) {
                if (r != head &&
-                   !prefixcmp(r->name, "refs/heads/") &&
+                   starts_with(r->name, "refs/heads/") &&
                    !hashcmp(r->old_sha1, head->old_sha1)) {
                        *tail = copy_ref(r);
                        tail = &((*tail)->next);
@@ -2083,7 +2121,7 @@ static void apply_cas(struct push_cas_option *cas,
        /* Find an explicit --<option>=<name>[:<value>] entry */
        for (i = 0; i < cas->nr; i++) {
                struct push_cas *entry = &cas->entry[i];
-               if (!refname_match(entry->refname, ref->name, ref_rev_parse_rules))
+               if (!refname_match(entry->refname, ref->name))
                        continue;
                ref->expect_old_sha1 = 1;
                if (!entry->use_tracking)
index 131130a611b55c9f79649037e6dde916b621f578..fb7647fab92aef651fbf2eb4118232a550450a55 100644 (file)
--- a/remote.h
+++ b/remote.h
@@ -109,6 +109,7 @@ struct ref {
                REF_STATUS_REJECT_FETCH_FIRST,
                REF_STATUS_REJECT_NEEDS_FORCE,
                REF_STATUS_REJECT_STALE,
+               REF_STATUS_REJECT_SHALLOW,
                REF_STATUS_UPTODATE,
                REF_STATUS_REMOTE_REJECT,
                REF_STATUS_EXPECTING_REPORT
@@ -128,6 +129,7 @@ struct ref *alloc_ref(const char *name);
 struct ref *copy_ref(const struct ref *ref);
 struct ref *copy_ref_list(const struct ref *ref);
 void sort_ref_list(struct ref **, int (*cmp)(const void *, const void *));
+extern int count_refspec_match(const char *, struct ref *refs, struct ref **matched_ref);
 int ref_compare_name(const void *, const void *);
 
 int check_ref_type(const struct ref *ref, int flags);
@@ -137,27 +139,30 @@ int check_ref_type(const struct ref *ref, int flags);
  */
 void free_refs(struct ref *ref);
 
-struct extra_have_objects {
-       int nr, alloc;
-       unsigned char (*array)[20];
-};
+struct sha1_array;
 extern struct ref **get_remote_heads(int in, char *src_buf, size_t src_len,
                                     struct ref **list, unsigned int flags,
-                                    struct extra_have_objects *);
+                                    struct sha1_array *extra_have,
+                                    struct sha1_array *shallow);
 
 int resolve_remote_symref(struct ref *ref, struct ref *list);
 int ref_newer(const unsigned char *new_sha1, const unsigned char *old_sha1);
 
 /*
- * Removes and frees any duplicate refs in the map.
+ * Remove and free all but the first of any entries in the input list
+ * that map the same remote reference to the same local reference.  If
+ * there are two entries that map different remote references to the
+ * same local reference, emit an error message and die.  Return a
+ * pointer to the head of the resulting list.
  */
-void ref_remove_duplicates(struct ref *ref_map);
+struct ref *ref_remove_duplicates(struct ref *ref_map);
 
 int valid_fetch_refspec(const char *refspec);
 struct refspec *parse_fetch_refspec(int nr_refspec, const char **refspec);
 
 void free_refspec(int nr_refspec, struct refspec *refspec);
 
+extern int query_refspecs(struct refspec *specs, int nr, struct refspec *query);
 char *apply_refspecs(struct refspec *refspecs, int nr_refspec,
                     const char *name);
 
index d0b1548726e9d2362d27c6947eb61a0647d0eac2..cdcaf8cbe2f4e6c7a5b334fc2c43da6855df4654 100644 (file)
@@ -97,9 +97,6 @@ const unsigned char *do_lookup_replace_object(const unsigned char *sha1)
        int pos, depth = MAXREPLACEDEPTH;
        const unsigned char *cur = sha1;
 
-       if (!read_replace_refs)
-               return sha1;
-
        prepare_replace_object();
 
        /* Try to recursively replace the object */
index 05d2d7763a3c9a2bd555cb19ef9af01c5767adb8..a0df72f32c100a68e5d99b5b00e597d43f3802a5 100644 (file)
@@ -104,17 +104,12 @@ static void mark_blob_uninteresting(struct blob *blob)
        blob->object.flags |= UNINTERESTING;
 }
 
-void mark_tree_uninteresting(struct tree *tree)
+static void mark_tree_contents_uninteresting(struct tree *tree)
 {
        struct tree_desc desc;
        struct name_entry entry;
        struct object *obj = &tree->object;
 
-       if (!tree)
-               return;
-       if (obj->flags & UNINTERESTING)
-               return;
-       obj->flags |= UNINTERESTING;
        if (!has_sha1_file(obj->sha1))
                return;
        if (parse_tree(tree) < 0)
@@ -142,6 +137,18 @@ void mark_tree_uninteresting(struct tree *tree)
        free_tree_buffer(tree);
 }
 
+void mark_tree_uninteresting(struct tree *tree)
+{
+       struct object *obj = &tree->object;
+
+       if (!tree)
+               return;
+       if (obj->flags & UNINTERESTING)
+               return;
+       obj->flags |= UNINTERESTING;
+       mark_tree_contents_uninteresting(tree);
+}
+
 void mark_parents_uninteresting(struct commit *commit)
 {
        struct commit_list *parents = NULL, *l;
@@ -276,6 +283,7 @@ static struct commit *handle_commit(struct rev_info *revs,
                                return NULL;
                        die("bad object %s", sha1_to_hex(tag->tagged->sha1));
                }
+               object->flags |= flags;
        }
 
        /*
@@ -287,7 +295,6 @@ static struct commit *handle_commit(struct rev_info *revs,
                if (parse_commit(commit) < 0)
                        die("unable to parse commit %s", name);
                if (flags & UNINTERESTING) {
-                       commit->object.flags |= UNINTERESTING;
                        mark_parents_uninteresting(commit);
                        revs->limited = 1;
                }
@@ -305,7 +312,7 @@ static struct commit *handle_commit(struct rev_info *revs,
                if (!revs->tree_objects)
                        return NULL;
                if (flags & UNINTERESTING) {
-                       mark_tree_uninteresting(tree);
+                       mark_tree_contents_uninteresting(tree);
                        return NULL;
                }
                add_pending_object(revs, object, "");
@@ -316,13 +323,10 @@ static struct commit *handle_commit(struct rev_info *revs,
         * Blob object? You know the drill by now..
         */
        if (object->type == OBJ_BLOB) {
-               struct blob *blob = (struct blob *)object;
                if (!revs->blob_objects)
                        return NULL;
-               if (flags & UNINTERESTING) {
-                       mark_blob_uninteresting(blob);
+               if (flags & UNINTERESTING)
                        return NULL;
-               }
                add_pending_object(revs, object, "");
                return NULL;
        }
@@ -1627,9 +1631,9 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
            !strcmp(arg, "--tags") || !strcmp(arg, "--remotes") ||
            !strcmp(arg, "--reflog") || !strcmp(arg, "--not") ||
            !strcmp(arg, "--no-walk") || !strcmp(arg, "--do-walk") ||
-           !strcmp(arg, "--bisect") || !prefixcmp(arg, "--glob=") ||
-           !prefixcmp(arg, "--branches=") || !prefixcmp(arg, "--tags=") ||
-           !prefixcmp(arg, "--remotes=") || !prefixcmp(arg, "--no-walk="))
+           !strcmp(arg, "--bisect") || starts_with(arg, "--glob=") ||
+           starts_with(arg, "--branches=") || starts_with(arg, "--tags=") ||
+           starts_with(arg, "--remotes=") || starts_with(arg, "--no-walk="))
        {
                unkv[(*unkc)++] = arg;
                return 1;
@@ -1652,7 +1656,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                revs->max_count = atoi(argv[1]);
                revs->no_walk = 0;
                return 2;
-       } else if (!prefixcmp(arg, "-n")) {
+       } else if (starts_with(arg, "-n")) {
                revs->max_count = atoi(arg + 2);
                revs->no_walk = 0;
        } else if ((argcount = parse_long_opt("max-age", argv, &optarg))) {
@@ -1712,7 +1716,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
        } else if (!strcmp(arg, "--author-date-order")) {
                revs->sort_order = REV_SORT_BY_AUTHOR_DATE;
                revs->topo_order = 1;
-       } else if (!prefixcmp(arg, "--early-output")) {
+       } else if (starts_with(arg, "--early-output")) {
                int count = 100;
                switch (arg[14]) {
                case '=':
@@ -1737,13 +1741,13 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                revs->min_parents = 2;
        } else if (!strcmp(arg, "--no-merges")) {
                revs->max_parents = 1;
-       } else if (!prefixcmp(arg, "--min-parents=")) {
+       } else if (starts_with(arg, "--min-parents=")) {
                revs->min_parents = atoi(arg+14);
-       } else if (!prefixcmp(arg, "--no-min-parents")) {
+       } else if (starts_with(arg, "--no-min-parents")) {
                revs->min_parents = 0;
-       } else if (!prefixcmp(arg, "--max-parents=")) {
+       } else if (starts_with(arg, "--max-parents=")) {
                revs->max_parents = atoi(arg+14);
-       } else if (!prefixcmp(arg, "--no-max-parents")) {
+       } else if (starts_with(arg, "--no-max-parents")) {
                revs->max_parents = -1;
        } else if (!strcmp(arg, "--boundary")) {
                revs->boundary = 1;
@@ -1793,7 +1797,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                revs->verify_objects = 1;
        } else if (!strcmp(arg, "--unpacked")) {
                revs->unpacked = 1;
-       } else if (!prefixcmp(arg, "--unpacked=")) {
+       } else if (starts_with(arg, "--unpacked=")) {
                die("--unpacked=<packfile> no longer supported.");
        } else if (!strcmp(arg, "-r")) {
                revs->diff = 1;
@@ -1818,7 +1822,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                revs->verbose_header = 1;
                revs->pretty_given = 1;
                get_commit_format(arg+8, revs);
-       } else if (!prefixcmp(arg, "--pretty=") || !prefixcmp(arg, "--format=")) {
+       } else if (starts_with(arg, "--pretty=") || starts_with(arg, "--format=")) {
                /*
                 * Detached form ("--pretty X" as opposed to "--pretty=X")
                 * not allowed, since the argument is optional.
@@ -1832,12 +1836,12 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                revs->notes_opt.use_default_notes = 1;
        } else if (!strcmp(arg, "--show-signature")) {
                revs->show_signature = 1;
-       } else if (!prefixcmp(arg, "--show-notes=") ||
-                  !prefixcmp(arg, "--notes=")) {
+       } else if (starts_with(arg, "--show-notes=") ||
+                  starts_with(arg, "--notes=")) {
                struct strbuf buf = STRBUF_INIT;
                revs->show_notes = 1;
                revs->show_notes_given = 1;
-               if (!prefixcmp(arg, "--show-notes")) {
+               if (starts_with(arg, "--show-notes")) {
                        if (revs->notes_opt.use_default_notes < 0)
                                revs->notes_opt.use_default_notes = 1;
                        strbuf_addstr(&buf, arg+13);
@@ -1880,7 +1884,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                revs->abbrev = 0;
        } else if (!strcmp(arg, "--abbrev")) {
                revs->abbrev = DEFAULT_ABBREV;
-       } else if (!prefixcmp(arg, "--abbrev=")) {
+       } else if (starts_with(arg, "--abbrev=")) {
                revs->abbrev = strtoul(arg + 9, NULL, 10);
                if (revs->abbrev < MINIMUM_ABBREV)
                        revs->abbrev = MINIMUM_ABBREV;
@@ -2027,17 +2031,17 @@ static int handle_revision_pseudo_opt(const char *submodule,
        } else if ((argcount = parse_long_opt("exclude", argv, &optarg))) {
                add_ref_exclusion(&revs->ref_excludes, optarg);
                return argcount;
-       } else if (!prefixcmp(arg, "--branches=")) {
+       } else if (starts_with(arg, "--branches=")) {
                struct all_refs_cb cb;
                init_all_refs_cb(&cb, revs, *flags);
                for_each_glob_ref_in(handle_one_ref, arg + 11, "refs/heads/", &cb);
                clear_ref_exclusion(&revs->ref_excludes);
-       } else if (!prefixcmp(arg, "--tags=")) {
+       } else if (starts_with(arg, "--tags=")) {
                struct all_refs_cb cb;
                init_all_refs_cb(&cb, revs, *flags);
                for_each_glob_ref_in(handle_one_ref, arg + 7, "refs/tags/", &cb);
                clear_ref_exclusion(&revs->ref_excludes);
-       } else if (!prefixcmp(arg, "--remotes=")) {
+       } else if (starts_with(arg, "--remotes=")) {
                struct all_refs_cb cb;
                init_all_refs_cb(&cb, revs, *flags);
                for_each_glob_ref_in(handle_one_ref, arg + 10, "refs/remotes/", &cb);
@@ -2048,7 +2052,7 @@ static int handle_revision_pseudo_opt(const char *submodule,
                *flags ^= UNINTERESTING | BOTTOM;
        } else if (!strcmp(arg, "--no-walk")) {
                revs->no_walk = REVISION_WALK_NO_WALK_SORTED;
-       } else if (!prefixcmp(arg, "--no-walk=")) {
+       } else if (starts_with(arg, "--no-walk=")) {
                /*
                 * Detached form ("--no-walk X" as opposed to "--no-walk=X")
                 * not allowed, since the argument is optional.
index fab62e3da05913b5a4f76db7ed1c17e5831384d2..6129b0fd8e8e1961ab4c74de4f421c41c7786b42 100644 (file)
@@ -10,6 +10,7 @@
 #include "quote.h"
 #include "transport.h"
 #include "version.h"
+#include "sha1-array.h"
 
 static int feed_object(const unsigned char *sha1, int fd, int negative)
 {
@@ -28,7 +29,7 @@ static int feed_object(const unsigned char *sha1, int fd, int negative)
 /*
  * Make a pack stream and spit it out into file descriptor fd
  */
-static int pack_objects(int fd, struct ref *refs, struct extra_have_objects *extra, struct send_pack_args *args)
+static int pack_objects(int fd, struct ref *refs, struct sha1_array *extra, struct send_pack_args *args)
 {
        /*
         * The child becomes pack-objects --revs; we feed
@@ -71,7 +72,7 @@ static int pack_objects(int fd, struct ref *refs, struct extra_have_objects *ext
         * parameters by writing to the pipe.
         */
        for (i = 0; i < extra->nr; i++)
-               if (!feed_object(extra->array[i], po.in, 1))
+               if (!feed_object(extra->sha1[i], po.in, 1))
                        break;
 
        while (refs) {
@@ -109,7 +110,7 @@ static int receive_status(int in, struct ref *refs)
        struct ref *hint;
        int ret = 0;
        char *line = packet_read_line(in, NULL);
-       if (prefixcmp(line, "unpack "))
+       if (!starts_with(line, "unpack "))
                return error("did not receive remote status");
        if (strcmp(line, "unpack ok")) {
                error("unpack failed: %s", line + 7);
@@ -122,7 +123,7 @@ static int receive_status(int in, struct ref *refs)
                line = packet_read_line(in, NULL);
                if (!line)
                        break;
-               if (prefixcmp(line, "ok ") && prefixcmp(line, "ng ")) {
+               if (!starts_with(line, "ok ") && !starts_with(line, "ng ")) {
                        error("invalid ref status from remote: %s", line);
                        ret = -1;
                        break;
@@ -174,10 +175,25 @@ static int sideband_demux(int in, int out, void *data)
        return ret;
 }
 
+static int advertise_shallow_grafts_cb(const struct commit_graft *graft, void *cb)
+{
+       struct strbuf *sb = cb;
+       if (graft->nr_parent == -1)
+               packet_buf_write(sb, "shallow %s\n", sha1_to_hex(graft->sha1));
+       return 0;
+}
+
+static void advertise_shallow_grafts_buf(struct strbuf *sb)
+{
+       if (!is_repository_shallow())
+               return;
+       for_each_commit_graft(advertise_shallow_grafts_cb, sb);
+}
+
 int send_pack(struct send_pack_args *args,
              int fd[], struct child_process *conn,
              struct ref *remote_refs,
-             struct extra_have_objects *extra_have)
+             struct sha1_array *extra_have)
 {
        int in = fd[0];
        int out = fd[1];
@@ -206,6 +222,8 @@ int send_pack(struct send_pack_args *args,
                quiet_supported = 1;
        if (server_supports("agent"))
                agent_supported = 1;
+       if (server_supports("no-thin"))
+               args->use_thin_pack = 0;
 
        if (!remote_refs) {
                fprintf(stderr, "No refs in common and none specified; doing nothing.\n"
@@ -213,6 +231,9 @@ int send_pack(struct send_pack_args *args,
                return 0;
        }
 
+       if (!args->dry_run)
+               advertise_shallow_grafts_buf(&req_buf);
+
        /*
         * Finally, tell the other end!
         */
@@ -272,7 +293,7 @@ int send_pack(struct send_pack_args *args,
        }
 
        if (args->stateless_rpc) {
-               if (!args->dry_run && cmds_sent) {
+               if (!args->dry_run && (cmds_sent || is_repository_shallow())) {
                        packet_buf_flush(&req_buf);
                        send_sideband(out, -1, req_buf.buf, req_buf.len, LARGE_PACKET_MAX);
                }
index 05d7ab118b3e1473cdf559d3f6337b26bb9aac81..8e843924cf5beba6ee680149910cd357cffc73e3 100644 (file)
@@ -16,6 +16,6 @@ struct send_pack_args {
 
 int send_pack(struct send_pack_args *args,
              int fd[], struct child_process *conn,
-             struct ref *remote_refs, struct extra_have_objects *extra_have);
+             struct ref *remote_refs, struct sha1_array *extra_have);
 
 #endif
index 06e52b4c83ffc0ce2b9414afe3fb1b4ee91d4335..90cac7b02eef1e9f681f5dc58bc26becaaf5ac12 100644 (file)
@@ -41,7 +41,7 @@ static int is_cherry_picked_from_line(const char *buf, int len)
         * We only care that it looks roughly like (cherry picked from ...)
         */
        return len > strlen(cherry_picked_prefix) + 1 &&
-               !prefixcmp(buf, cherry_picked_prefix) && buf[len - 1] == ')';
+               starts_with(buf, cherry_picked_prefix) && buf[len - 1] == ')';
 }
 
 /*
@@ -180,7 +180,7 @@ static char *get_encoding(const char *message)
        while (*p && *p != '\n') {
                for (eol = p + 1; *eol && *eol != '\n'; eol++)
                        ; /* do nothing */
-               if (!prefixcmp(p, "encoding ")) {
+               if (starts_with(p, "encoding ")) {
                        char *result = xmalloc(eol - 8 - p);
                        strlcpy(result, p + 9, eol - 8 - p);
                        return result;
@@ -705,10 +705,10 @@ static struct commit *parse_insn_line(char *bol, char *eol, struct replay_opts *
        char *end_of_object_name;
        int saved, status, padding;
 
-       if (!prefixcmp(bol, "pick")) {
+       if (starts_with(bol, "pick")) {
                action = REPLAY_PICK;
                bol += strlen("pick");
-       } else if (!prefixcmp(bol, "revert")) {
+       } else if (starts_with(bol, "revert")) {
                action = REPLAY_REVERT;
                bol += strlen("revert");
        } else
diff --git a/setup.c b/setup.c
index 5432a31b62800bfba4d5a19226946ea30c5446a4..6c3f85ff7a7935ed6f05ab5ab376a2d6c5ca2d46 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -82,7 +82,7 @@ int check_filename(const char *prefix, const char *arg)
        const char *name;
        struct stat st;
 
-       if (!prefixcmp(arg, ":/")) {
+       if (starts_with(arg, ":/")) {
                if (arg[2] == '\0') /* ":/" is root dir, always exists */
                        return 1;
                name = arg + 2;
@@ -304,7 +304,7 @@ const char *read_gitfile(const char *path)
        if (len != st.st_size)
                die("Error reading %s", path);
        buf[len] = '\0';
-       if (prefixcmp(buf, "gitdir: "))
+       if (!starts_with(buf, "gitdir: "))
                die("Invalid gitfile format: %s", path);
        while (buf[len - 1] == '\n' || buf[len - 1] == '\r')
                len--;
index daacc0cfb0132e98a98ba9645991f3d3f228d1cd..6e8c05d10825ee93d8248f749d4bef513d772be4 100644 (file)
@@ -105,50 +105,63 @@ int mkdir_in_gitdir(const char *path)
        return adjust_shared_perm(path);
 }
 
-int safe_create_leading_directories(char *path)
+enum scld_error safe_create_leading_directories(char *path)
 {
-       char *pos = path + offset_1st_component(path);
-       struct stat st;
+       char *next_component = path + offset_1st_component(path);
+       enum scld_error ret = SCLD_OK;
+
+       while (ret == SCLD_OK && next_component) {
+               struct stat st;
+               char *slash = next_component, slash_character;
 
-       while (pos) {
-               pos = strchr(pos, '/');
-               if (!pos)
+               while (*slash && !is_dir_sep(*slash))
+                       slash++;
+
+               if (!*slash)
                        break;
-               while (*++pos == '/')
-                       ;
-               if (!*pos)
+
+               next_component = slash + 1;
+               while (is_dir_sep(*next_component))
+                       next_component++;
+               if (!*next_component)
                        break;
-               *--pos = '\0';
+
+               slash_character = *slash;
+               *slash = '\0';
                if (!stat(path, &st)) {
                        /* path exists */
-                       if (!S_ISDIR(st.st_mode)) {
-                               *pos = '/';
-                               return -3;
-                       }
-               }
-               else if (mkdir(path, 0777)) {
+                       if (!S_ISDIR(st.st_mode))
+                               ret = SCLD_EXISTS;
+               } else if (mkdir(path, 0777)) {
                        if (errno == EEXIST &&
-                           !stat(path, &st) && S_ISDIR(st.st_mode)) {
+                           !stat(path, &st) && S_ISDIR(st.st_mode))
                                ; /* somebody created it since we checked */
-                       } else {
-                               *pos = '/';
-                               return -1;
-                       }
-               }
-               else if (adjust_shared_perm(path)) {
-                       *pos = '/';
-                       return -2;
+                       else if (errno == ENOENT)
+                               /*
+                                * Either mkdir() failed because
+                                * somebody just pruned the containing
+                                * directory, or stat() failed because
+                                * the file that was in our way was
+                                * just removed.  Either way, inform
+                                * the caller that it might be worth
+                                * trying again:
+                                */
+                               ret = SCLD_VANISHED;
+                       else
+                               ret = SCLD_FAILED;
+               } else if (adjust_shared_perm(path)) {
+                       ret = SCLD_PERMS;
                }
-               *pos++ = '/';
+               *slash = slash_character;
        }
-       return 0;
+       return ret;
 }
 
-int safe_create_leading_directories_const(const char *path)
+enum scld_error safe_create_leading_directories_const(const char *path)
 {
        /* path points to cache entries, so xstrdup before messing with it */
        char *buf = xstrdup(path);
-       int result = safe_create_leading_directories(buf);
+       enum scld_error result = safe_create_leading_directories(buf);
        free(buf);
        return result;
 }
@@ -807,15 +820,38 @@ void free_pack_by_name(const char *pack_name)
 static unsigned int get_max_fd_limit(void)
 {
 #ifdef RLIMIT_NOFILE
-       struct rlimit lim;
+       {
+               struct rlimit lim;
 
-       if (getrlimit(RLIMIT_NOFILE, &lim))
-               die_errno("cannot get RLIMIT_NOFILE");
+               if (!getrlimit(RLIMIT_NOFILE, &lim))
+                       return lim.rlim_cur;
+       }
+#endif
 
-       return lim.rlim_cur;
-#elif defined(_SC_OPEN_MAX)
-       return sysconf(_SC_OPEN_MAX);
-#elif defined(OPEN_MAX)
+#ifdef _SC_OPEN_MAX
+       {
+               long open_max = sysconf(_SC_OPEN_MAX);
+               if (0 < open_max)
+                       return open_max;
+               /*
+                * Otherwise, we got -1 for one of the two
+                * reasons:
+                *
+                * (1) sysconf() did not understand _SC_OPEN_MAX
+                *     and signaled an error with -1; or
+                * (2) sysconf() said there is no limit.
+                *
+                * We _could_ clear errno before calling sysconf() to
+                * tell these two cases apart and return a huge number
+                * in the latter case to let the caller cap it to a
+                * value that is not so selfish, but letting the
+                * fallback OPEN_MAX codepath take care of these cases
+                * is a lot simpler.
+                */
+       }
+#endif
+
+#ifdef OPEN_MAX
        return OPEN_MAX;
 #else
        return 1; /* see the caller ;-) */
@@ -1667,6 +1703,38 @@ static off_t get_delta_base(struct packed_git *p,
        return base_offset;
 }
 
+/*
+ * Like get_delta_base above, but we return the sha1 instead of the pack
+ * offset. This means it is cheaper for REF deltas (we do not have to do
+ * the final object lookup), but more expensive for OFS deltas (we
+ * have to load the revidx to convert the offset back into a sha1).
+ */
+static const unsigned char *get_delta_base_sha1(struct packed_git *p,
+                                               struct pack_window **w_curs,
+                                               off_t curpos,
+                                               enum object_type type,
+                                               off_t delta_obj_offset)
+{
+       if (type == OBJ_REF_DELTA) {
+               unsigned char *base = use_pack(p, w_curs, curpos, NULL);
+               return base;
+       } else if (type == OBJ_OFS_DELTA) {
+               struct revindex_entry *revidx;
+               off_t base_offset = get_delta_base(p, w_curs, &curpos,
+                                                  type, delta_obj_offset);
+
+               if (!base_offset)
+                       return NULL;
+
+               revidx = find_pack_revindex(p, base_offset);
+               if (!revidx)
+                       return NULL;
+
+               return nth_packed_object_sha1(p, revidx->nr);
+       } else
+               return NULL;
+}
+
 int unpack_object_header(struct packed_git *p,
                         struct pack_window **w_curs,
                         off_t *curpos,
@@ -1824,6 +1892,22 @@ static int packed_object_info(struct packed_git *p, off_t obj_offset,
                }
        }
 
+       if (oi->delta_base_sha1) {
+               if (type == OBJ_OFS_DELTA || type == OBJ_REF_DELTA) {
+                       const unsigned char *base;
+
+                       base = get_delta_base_sha1(p, &w_curs, curpos,
+                                                  type, obj_offset);
+                       if (!base) {
+                               type = OBJ_BAD;
+                               goto out;
+                       }
+
+                       hashcpy(oi->delta_base_sha1, base);
+               } else
+                       hashclr(oi->delta_base_sha1);
+       }
+
 out:
        unuse_pack(&w_curs);
        return type;
@@ -2407,6 +2491,9 @@ static int sha1_loose_object_info(const unsigned char *sha1,
        git_zstream stream;
        char hdr[32];
 
+       if (oi->delta_base_sha1)
+               hashclr(oi->delta_base_sha1);
+
        /*
         * If we don't care about type or size, then we don't
         * need to look inside the object at all. Note that we
@@ -2443,13 +2530,14 @@ static int sha1_loose_object_info(const unsigned char *sha1,
        return 0;
 }
 
-int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi)
+int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi, unsigned flags)
 {
        struct cached_object *co;
        struct pack_entry e;
        int rtype;
+       const unsigned char *real = lookup_replace_object_extended(sha1, flags);
 
-       co = find_cached_object(sha1);
+       co = find_cached_object(real);
        if (co) {
                if (oi->typep)
                        *(oi->typep) = co->type;
@@ -2457,27 +2545,29 @@ int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi)
                        *(oi->sizep) = co->size;
                if (oi->disk_sizep)
                        *(oi->disk_sizep) = 0;
+               if (oi->delta_base_sha1)
+                       hashclr(oi->delta_base_sha1);
                oi->whence = OI_CACHED;
                return 0;
        }
 
-       if (!find_pack_entry(sha1, &e)) {
+       if (!find_pack_entry(real, &e)) {
                /* Most likely it's a loose object. */
-               if (!sha1_loose_object_info(sha1, oi)) {
+               if (!sha1_loose_object_info(real, oi)) {
                        oi->whence = OI_LOOSE;
                        return 0;
                }
 
                /* Not a loose object; someone else may have just packed it. */
                reprepare_packed_git();
-               if (!find_pack_entry(sha1, &e))
+               if (!find_pack_entry(real, &e))
                        return -1;
        }
 
        rtype = packed_object_info(e.p, e.offset, oi);
        if (rtype < 0) {
-               mark_bad_packed_object(e.p, sha1);
-               return sha1_object_info_extended(sha1, oi);
+               mark_bad_packed_object(e.p, real);
+               return sha1_object_info_extended(real, oi, 0);
        } else if (in_delta_base_cache(e.p, e.offset)) {
                oi->whence = OI_DBCACHED;
        } else {
@@ -2499,7 +2589,7 @@ int sha1_object_info(const unsigned char *sha1, unsigned long *sizep)
 
        oi.typep = &type;
        oi.sizep = sizep;
-       if (sha1_object_info_extended(sha1, &oi) < 0)
+       if (sha1_object_info_extended(sha1, &oi, LOOKUP_REPLACE_OBJECT) < 0)
                return -1;
        return type;
 }
@@ -2591,8 +2681,7 @@ void *read_sha1_file_extended(const unsigned char *sha1,
        void *data;
        char *path;
        const struct packed_git *p;
-       const unsigned char *repl = (flag & READ_SHA1_FILE_REPLACE)
-               ? lookup_replace_object(sha1) : sha1;
+       const unsigned char *repl = lookup_replace_object_extended(sha1, flag);
 
        errno = 0;
        data = read_object(repl, type, size);
index 2f37488b96c0a8485e90d87befa997d75ba4b7b1..6fca8692d2dd8875281fcb8379cc93805e647cea 100644 (file)
@@ -430,7 +430,7 @@ static inline int upstream_mark(const char *string, int len)
 }
 
 static int get_sha1_1(const char *name, int len, unsigned char *sha1, unsigned lookup_flags);
-static int interpret_nth_prior_checkout(const char *name, struct strbuf *buf);
+static int interpret_nth_prior_checkout(const char *name, int namelen, struct strbuf *buf);
 
 static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
 {
@@ -451,9 +451,9 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
        int at, reflog_len, nth_prior = 0;
 
        if (len == 40 && !get_sha1_hex(str, sha1)) {
-               if (warn_on_object_refname_ambiguity) {
+               if (warn_ambiguous_refs && warn_on_object_refname_ambiguity) {
                        refs_found = dwim_ref(str, len, tmp_sha1, &real_ref);
-                       if (refs_found > 0 && warn_ambiguous_refs) {
+                       if (refs_found > 0) {
                                warning(warn_msg, len, str);
                                if (advice_object_name_warning)
                                        fprintf(stderr, "%s\n", _(object_name_msg));
@@ -492,7 +492,7 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
                struct strbuf buf = STRBUF_INIT;
                int detached;
 
-               if (interpret_nth_prior_checkout(str, &buf) > 0) {
+               if (interpret_nth_prior_checkout(str, len, &buf) > 0) {
                        detached = (buf.len == 40 && !get_sha1_hex(buf.buf, sha1));
                        strbuf_release(&buf);
                        if (detached)
@@ -546,7 +546,7 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
                if (read_ref_at(real_ref, at_time, nth, sha1, NULL,
                                &co_time, &co_tz, &co_cnt)) {
                        if (!len) {
-                               if (!prefixcmp(real_ref, "refs/heads/")) {
+                               if (starts_with(real_ref, "refs/heads/")) {
                                        str = real_ref + 11;
                                        len = strlen(real_ref + 11);
                                } else {
@@ -674,15 +674,15 @@ static int peel_onion(const char *name, int len, unsigned char *sha1)
                return -1;
 
        sp++; /* beginning of type name, or closing brace for empty */
-       if (!prefixcmp(sp, "commit}"))
+       if (starts_with(sp, "commit}"))
                expected_type = OBJ_COMMIT;
-       else if (!prefixcmp(sp, "tag}"))
+       else if (starts_with(sp, "tag}"))
                expected_type = OBJ_TAG;
-       else if (!prefixcmp(sp, "tree}"))
+       else if (starts_with(sp, "tree}"))
                expected_type = OBJ_TREE;
-       else if (!prefixcmp(sp, "blob}"))
+       else if (starts_with(sp, "blob}"))
                expected_type = OBJ_BLOB;
-       else if (!prefixcmp(sp, "object}"))
+       else if (starts_with(sp, "object}"))
                expected_type = OBJ_ANY;
        else if (sp[0] == '}')
                expected_type = OBJ_NONE;
@@ -909,7 +909,7 @@ static int grab_nth_branch_switch(unsigned char *osha1, unsigned char *nsha1,
        const char *match = NULL, *target = NULL;
        size_t len;
 
-       if (!prefixcmp(message, "checkout: moving from ")) {
+       if (starts_with(message, "checkout: moving from ")) {
                match = message + strlen("checkout: moving from ");
                target = strstr(match, " to ");
        }
@@ -929,7 +929,8 @@ static int grab_nth_branch_switch(unsigned char *osha1, unsigned char *nsha1,
  * Parse @{-N} syntax, return the number of characters parsed
  * if successful; otherwise signal an error with negative value.
  */
-static int interpret_nth_prior_checkout(const char *name, struct strbuf *buf)
+static int interpret_nth_prior_checkout(const char *name, int namelen,
+                                       struct strbuf *buf)
 {
        long nth;
        int retval;
@@ -937,9 +938,11 @@ static int interpret_nth_prior_checkout(const char *name, struct strbuf *buf)
        const char *brace;
        char *num_end;
 
+       if (namelen < 4)
+               return -1;
        if (name[0] != '@' || name[1] != '{' || name[2] != '-')
                return -1;
-       brace = strchr(name, '}');
+       brace = memchr(name, '}', namelen);
        if (!brace)
                return -1;
        nth = strtol(name + 3, &num_end, 10);
@@ -1012,7 +1015,7 @@ static int interpret_empty_at(const char *name, int namelen, int len, struct str
                return -1;
 
        /* make sure it's a single @, or @@{.*}, not @foo */
-       next = strchr(name + len + 1, '@');
+       next = memchr(name + len + 1, '@', namelen - len - 1);
        if (next && next[1] != '{')
                return -1;
        if (!next)
@@ -1046,6 +1049,57 @@ static int reinterpret(const char *name, int namelen, int len, struct strbuf *bu
        return ret - used + len;
 }
 
+static void set_shortened_ref(struct strbuf *buf, const char *ref)
+{
+       char *s = shorten_unambiguous_ref(ref, 0);
+       strbuf_reset(buf);
+       strbuf_addstr(buf, s);
+       free(s);
+}
+
+static const char *get_upstream_branch(const char *branch_buf, int len)
+{
+       char *branch = xstrndup(branch_buf, len);
+       struct branch *upstream = branch_get(*branch ? branch : NULL);
+
+       /*
+        * Upstream can be NULL only if branch refers to HEAD and HEAD
+        * points to something different than a branch.
+        */
+       if (!upstream)
+               die(_("HEAD does not point to a branch"));
+       if (!upstream->merge || !upstream->merge[0]->dst) {
+               if (!ref_exists(upstream->refname))
+                       die(_("No such branch: '%s'"), branch);
+               if (!upstream->merge) {
+                       die(_("No upstream configured for branch '%s'"),
+                               upstream->name);
+               }
+               die(
+                       _("Upstream branch '%s' not stored as a remote-tracking branch"),
+                       upstream->merge[0]->src);
+       }
+       free(branch);
+
+       return upstream->merge[0]->dst;
+}
+
+static int interpret_upstream_mark(const char *name, int namelen,
+                                  int at, struct strbuf *buf)
+{
+       int len;
+
+       len = upstream_mark(name + at, namelen - at);
+       if (!len)
+               return -1;
+
+       if (memchr(name, ':', at))
+               return -1;
+
+       set_shortened_ref(buf, get_upstream_branch(name, at));
+       return len + at;
+}
+
 /*
  * This reads short-hand syntax that not only evaluates to a commit
  * object name, but also can act as if the end user spelled the name
@@ -1069,10 +1123,9 @@ static int reinterpret(const char *name, int namelen, int len, struct strbuf *bu
  */
 int interpret_branch_name(const char *name, int namelen, struct strbuf *buf)
 {
-       char *cp;
-       struct branch *upstream;
-       int len = interpret_nth_prior_checkout(name, buf);
-       int tmp_len;
+       char *at;
+       const char *start;
+       int len = interpret_nth_prior_checkout(name, namelen, buf);
 
        if (!namelen)
                namelen = strlen(name);
@@ -1086,44 +1139,20 @@ int interpret_branch_name(const char *name, int namelen, struct strbuf *buf)
                        return reinterpret(name, namelen, len, buf);
        }
 
-       cp = strchr(name, '@');
-       if (!cp)
-               return -1;
-
-       len = interpret_empty_at(name, namelen, cp - name, buf);
-       if (len > 0)
-               return reinterpret(name, namelen, len, buf);
+       for (start = name;
+            (at = memchr(start, '@', namelen - (start - name)));
+            start = at + 1) {
 
-       tmp_len = upstream_mark(cp, namelen - (cp - name));
-       if (!tmp_len)
-               return -1;
+               len = interpret_empty_at(name, namelen, at - name, buf);
+               if (len > 0)
+                       return reinterpret(name, namelen, len, buf);
 
-       len = cp + tmp_len - name;
-       cp = xstrndup(name, cp - name);
-       upstream = branch_get(*cp ? cp : NULL);
-       /*
-        * Upstream can be NULL only if cp refers to HEAD and HEAD
-        * points to something different than a branch.
-        */
-       if (!upstream)
-               die(_("HEAD does not point to a branch"));
-       if (!upstream->merge || !upstream->merge[0]->dst) {
-               if (!ref_exists(upstream->refname))
-                       die(_("No such branch: '%s'"), cp);
-               if (!upstream->merge) {
-                       die(_("No upstream configured for branch '%s'"),
-                               upstream->name);
-               }
-               die(
-                       _("Upstream branch '%s' not stored as a remote-tracking branch"),
-                       upstream->merge[0]->src);
+               len = interpret_upstream_mark(name, namelen, at - name, buf);
+               if (len > 0)
+                       return len;
        }
-       free(cp);
-       cp = shorten_unambiguous_ref(upstream->merge[0]->dst, 0);
-       strbuf_reset(buf);
-       strbuf_addstr(buf, cp);
-       free(cp);
-       return len;
+
+       return -1;
 }
 
 int strbuf_branchname(struct strbuf *sb, const char *name)
@@ -1302,7 +1331,7 @@ static void diagnose_invalid_index_path(int stage,
 
 static char *resolve_relative_path(const char *rel)
 {
-       if (prefixcmp(rel, "./") && prefixcmp(rel, "../"))
+       if (!starts_with(rel, "./") && !starts_with(rel, "../"))
                return NULL;
 
        if (!startup_info)
index 961cf6f02480e04d1baebb8f508aa17c5fea6355..bbc98b55c07969474b0afb01d9c4da70455f1903 100644 (file)
--- a/shallow.c
+++ b/shallow.c
@@ -2,15 +2,23 @@
 #include "commit.h"
 #include "tag.h"
 #include "pkt-line.h"
+#include "remote.h"
+#include "refs.h"
+#include "sha1-array.h"
+#include "diff.h"
+#include "revision.h"
+#include "commit-slab.h"
 
 static int is_shallow = -1;
 static struct stat shallow_stat;
 static char *alternate_shallow_file;
 
-void set_alternate_shallow_file(const char *path)
+void set_alternate_shallow_file(const char *path, int override)
 {
        if (is_shallow != -1)
                die("BUG: is_repository_shallow must not be called before set_alternate_shallow_file");
+       if (alternate_shallow_file && !override)
+               return;
        free(alternate_shallow_file);
        alternate_shallow_file = path ? xstrdup(path) : NULL;
 }
@@ -69,6 +77,7 @@ struct commit_list *get_shallow_commits(struct object_array *heads, int depth,
        struct commit_list *result = NULL;
        struct object_array stack = OBJECT_ARRAY_INIT;
        struct commit *commit = NULL;
+       struct commit_graft *graft;
 
        while (commit || i < heads->nr || stack.nr) {
                struct commit_list *p;
@@ -92,7 +101,10 @@ struct commit_list *get_shallow_commits(struct object_array *heads, int depth,
                }
                parse_commit_or_die(commit);
                cur_depth++;
-               if (cur_depth >= depth) {
+               if ((depth != INFINITE_DEPTH && cur_depth >= depth) ||
+                   (is_repository_shallow() && !commit->parents &&
+                    (graft = lookup_commit_graft(commit->object.sha1)) != NULL &&
+                    graft->nr_parent < 0)) {
                        commit_list_insert(commit, &result);
                        commit->object.flags |= shallow_flag;
                        commit = NULL;
@@ -142,10 +154,14 @@ void check_shallow_file_for_update(void)
                die("shallow file was changed during fetch");
 }
 
+#define SEEN_ONLY 1
+#define VERBOSE   2
+
 struct write_shallow_data {
        struct strbuf *out;
        int use_pack_protocol;
        int count;
+       unsigned flags;
 };
 
 static int write_one_shallow(const struct commit_graft *graft, void *cb_data)
@@ -154,6 +170,15 @@ static int write_one_shallow(const struct commit_graft *graft, void *cb_data)
        const char *hex = sha1_to_hex(graft->sha1);
        if (graft->nr_parent != -1)
                return 0;
+       if (data->flags & SEEN_ONLY) {
+               struct commit *c = lookup_commit(graft->sha1);
+               if (!c || !(c->object.flags & SEEN)) {
+                       if (data->flags & VERBOSE)
+                               printf("Removing %s from .git/shallow\n",
+                                      sha1_to_hex(c->object.sha1));
+                       return 0;
+               }
+       }
        data->count++;
        if (data->use_pack_protocol)
                packet_buf_write(data->out, "shallow %s", hex);
@@ -164,22 +189,39 @@ static int write_one_shallow(const struct commit_graft *graft, void *cb_data)
        return 0;
 }
 
-int write_shallow_commits(struct strbuf *out, int use_pack_protocol)
+static int write_shallow_commits_1(struct strbuf *out, int use_pack_protocol,
+                                  const struct sha1_array *extra,
+                                  unsigned flags)
 {
        struct write_shallow_data data;
+       int i;
        data.out = out;
        data.use_pack_protocol = use_pack_protocol;
        data.count = 0;
+       data.flags = flags;
        for_each_commit_graft(write_one_shallow, &data);
+       if (!extra)
+               return data.count;
+       for (i = 0; i < extra->nr; i++) {
+               strbuf_addstr(out, sha1_to_hex(extra->sha1[i]));
+               strbuf_addch(out, '\n');
+               data.count++;
+       }
        return data.count;
 }
 
-char *setup_temporary_shallow(void)
+int write_shallow_commits(struct strbuf *out, int use_pack_protocol,
+                         const struct sha1_array *extra)
+{
+       return write_shallow_commits_1(out, use_pack_protocol, extra, 0);
+}
+
+char *setup_temporary_shallow(const struct sha1_array *extra)
 {
        struct strbuf sb = STRBUF_INIT;
        int fd;
 
-       if (write_shallow_commits(&sb, 0)) {
+       if (write_shallow_commits(&sb, 0, extra)) {
                struct strbuf path = STRBUF_INIT;
                strbuf_addstr(&path, git_path("shallow_XXXXXX"));
                fd = xmkstemp(path.buf);
@@ -198,7 +240,8 @@ char *setup_temporary_shallow(void)
 }
 
 void setup_alternate_shallow(struct lock_file *shallow_lock,
-                            const char **alternate_shallow_file)
+                            const char **alternate_shallow_file,
+                            const struct sha1_array *extra)
 {
        struct strbuf sb = STRBUF_INIT;
        int fd;
@@ -206,7 +249,7 @@ void setup_alternate_shallow(struct lock_file *shallow_lock,
        check_shallow_file_for_update();
        fd = hold_lock_file_for_update(shallow_lock, git_path("shallow"),
                                       LOCK_DIE_ON_ERROR);
-       if (write_shallow_commits(&sb, 0)) {
+       if (write_shallow_commits(&sb, 0, extra)) {
                if (write_in_full(fd, sb.buf, sb.len) != sb.len)
                        die_errno("failed to write to %s",
                                  shallow_lock->filename);
@@ -219,3 +262,415 @@ void setup_alternate_shallow(struct lock_file *shallow_lock,
                *alternate_shallow_file = "";
        strbuf_release(&sb);
 }
+
+static int advertise_shallow_grafts_cb(const struct commit_graft *graft, void *cb)
+{
+       int fd = *(int *)cb;
+       if (graft->nr_parent == -1)
+               packet_write(fd, "shallow %s\n", sha1_to_hex(graft->sha1));
+       return 0;
+}
+
+void advertise_shallow_grafts(int fd)
+{
+       if (!is_repository_shallow())
+               return;
+       for_each_commit_graft(advertise_shallow_grafts_cb, &fd);
+}
+
+/*
+ * mark_reachable_objects() should have been run prior to this and all
+ * reachable commits marked as "SEEN".
+ */
+void prune_shallow(int show_only)
+{
+       static struct lock_file shallow_lock;
+       struct strbuf sb = STRBUF_INIT;
+       int fd;
+
+       if (show_only) {
+               write_shallow_commits_1(&sb, 0, NULL, SEEN_ONLY | VERBOSE);
+               strbuf_release(&sb);
+               return;
+       }
+       check_shallow_file_for_update();
+       fd = hold_lock_file_for_update(&shallow_lock, git_path("shallow"),
+                                      LOCK_DIE_ON_ERROR);
+       if (write_shallow_commits_1(&sb, 0, NULL, SEEN_ONLY)) {
+               if (write_in_full(fd, sb.buf, sb.len) != sb.len)
+                       die_errno("failed to write to %s",
+                                 shallow_lock.filename);
+               commit_lock_file(&shallow_lock);
+       } else {
+               unlink(git_path("shallow"));
+               rollback_lock_file(&shallow_lock);
+       }
+       strbuf_release(&sb);
+}
+
+#define TRACE_KEY "GIT_TRACE_SHALLOW"
+
+/*
+ * Step 1, split sender shallow commits into "ours" and "theirs"
+ * Step 2, clean "ours" based on .git/shallow
+ */
+void prepare_shallow_info(struct shallow_info *info, struct sha1_array *sa)
+{
+       int i;
+       trace_printf_key(TRACE_KEY, "shallow: prepare_shallow_info\n");
+       memset(info, 0, sizeof(*info));
+       info->shallow = sa;
+       if (!sa)
+               return;
+       info->ours = xmalloc(sizeof(*info->ours) * sa->nr);
+       info->theirs = xmalloc(sizeof(*info->theirs) * sa->nr);
+       for (i = 0; i < sa->nr; i++) {
+               if (has_sha1_file(sa->sha1[i])) {
+                       struct commit_graft *graft;
+                       graft = lookup_commit_graft(sa->sha1[i]);
+                       if (graft && graft->nr_parent < 0)
+                               continue;
+                       info->ours[info->nr_ours++] = i;
+               } else
+                       info->theirs[info->nr_theirs++] = i;
+       }
+}
+
+void clear_shallow_info(struct shallow_info *info)
+{
+       free(info->ours);
+       free(info->theirs);
+}
+
+/* Step 4, remove non-existent ones in "theirs" after getting the pack */
+
+void remove_nonexistent_theirs_shallow(struct shallow_info *info)
+{
+       unsigned char (*sha1)[20] = info->shallow->sha1;
+       int i, dst;
+       trace_printf_key(TRACE_KEY, "shallow: remove_nonexistent_theirs_shallow\n");
+       for (i = dst = 0; i < info->nr_theirs; i++) {
+               if (i != dst)
+                       info->theirs[dst] = info->theirs[i];
+               if (has_sha1_file(sha1[info->theirs[i]]))
+                       dst++;
+       }
+       info->nr_theirs = dst;
+}
+
+define_commit_slab(ref_bitmap, uint32_t *);
+
+struct paint_info {
+       struct ref_bitmap ref_bitmap;
+       unsigned nr_bits;
+       char **slab;
+       char *free, *end;
+       unsigned slab_count;
+};
+
+static uint32_t *paint_alloc(struct paint_info *info)
+{
+       unsigned nr = (info->nr_bits + 31) / 32;
+       unsigned size = nr * sizeof(uint32_t);
+       void *p;
+       if (!info->slab_count || info->free + size > info->end) {
+               info->slab_count++;
+               info->slab = xrealloc(info->slab,
+                                     info->slab_count * sizeof(*info->slab));
+               info->free = xmalloc(COMMIT_SLAB_SIZE);
+               info->slab[info->slab_count - 1] = info->free;
+               info->end = info->free + COMMIT_SLAB_SIZE;
+       }
+       p = info->free;
+       info->free += size;
+       return p;
+}
+
+/*
+ * Given a commit SHA-1, walk down to parents until either SEEN,
+ * UNINTERESTING or BOTTOM is hit. Set the id-th bit in ref_bitmap for
+ * all walked commits.
+ */
+static void paint_down(struct paint_info *info, const unsigned char *sha1,
+                      int id)
+{
+       unsigned int i, nr;
+       struct commit_list *head = NULL;
+       int bitmap_nr = (info->nr_bits + 31) / 32;
+       int bitmap_size = bitmap_nr * sizeof(uint32_t);
+       uint32_t *tmp = xmalloc(bitmap_size); /* to be freed before return */
+       uint32_t *bitmap = paint_alloc(info);
+       struct commit *c = lookup_commit_reference_gently(sha1, 1);
+       if (!c)
+               return;
+       memset(bitmap, 0, bitmap_size);
+       bitmap[id / 32] |= (1 << (id % 32));
+       commit_list_insert(c, &head);
+       while (head) {
+               struct commit_list *p;
+               struct commit *c = head->item;
+               uint32_t **refs = ref_bitmap_at(&info->ref_bitmap, c);
+
+               p = head;
+               head = head->next;
+               free(p);
+
+               /* XXX check "UNINTERESTING" from pack bitmaps if available */
+               if (c->object.flags & (SEEN | UNINTERESTING))
+                       continue;
+               else
+                       c->object.flags |= SEEN;
+
+               if (*refs == NULL)
+                       *refs = bitmap;
+               else {
+                       memcpy(tmp, *refs, bitmap_size);
+                       for (i = 0; i < bitmap_nr; i++)
+                               tmp[i] |= bitmap[i];
+                       if (memcmp(tmp, *refs, bitmap_size)) {
+                               *refs = paint_alloc(info);
+                               memcpy(*refs, tmp, bitmap_size);
+                       }
+               }
+
+               if (c->object.flags & BOTTOM)
+                       continue;
+
+               if (parse_commit(c))
+                       die("unable to parse commit %s",
+                           sha1_to_hex(c->object.sha1));
+
+               for (p = c->parents; p; p = p->next) {
+                       uint32_t **p_refs = ref_bitmap_at(&info->ref_bitmap,
+                                                         p->item);
+                       if (p->item->object.flags & SEEN)
+                               continue;
+                       if (*p_refs == NULL || *p_refs == *refs)
+                               *p_refs = *refs;
+                       commit_list_insert(p->item, &head);
+               }
+       }
+
+       nr = get_max_object_index();
+       for (i = 0; i < nr; i++) {
+               struct object *o = get_indexed_object(i);
+               if (o && o->type == OBJ_COMMIT)
+                       o->flags &= ~SEEN;
+       }
+
+       free(tmp);
+}
+
+static int mark_uninteresting(const char *refname,
+                             const unsigned char *sha1,
+                             int flags, void *cb_data)
+{
+       struct commit *commit = lookup_commit_reference_gently(sha1, 1);
+       if (!commit)
+               return 0;
+       commit->object.flags |= UNINTERESTING;
+       mark_parents_uninteresting(commit);
+       return 0;
+}
+
+static void post_assign_shallow(struct shallow_info *info,
+                               struct ref_bitmap *ref_bitmap,
+                               int *ref_status);
+/*
+ * Step 6(+7), associate shallow commits with new refs
+ *
+ * info->ref must be initialized before calling this function.
+ *
+ * If used is not NULL, it's an array of info->shallow->nr
+ * bitmaps. The n-th bit set in the m-th bitmap if ref[n] needs the
+ * m-th shallow commit from info->shallow.
+ *
+ * If used is NULL, "ours" and "theirs" are updated. And if ref_status
+ * is not NULL it's an array of ref->nr ints. ref_status[i] is true if
+ * the ref needs some shallow commits from either info->ours or
+ * info->theirs.
+ */
+void assign_shallow_commits_to_refs(struct shallow_info *info,
+                                   uint32_t **used, int *ref_status)
+{
+       unsigned char (*sha1)[20] = info->shallow->sha1;
+       struct sha1_array *ref = info->ref;
+       unsigned int i, nr;
+       int *shallow, nr_shallow = 0;
+       struct paint_info pi;
+
+       trace_printf_key(TRACE_KEY, "shallow: assign_shallow_commits_to_refs\n");
+       shallow = xmalloc(sizeof(*shallow) * (info->nr_ours + info->nr_theirs));
+       for (i = 0; i < info->nr_ours; i++)
+               shallow[nr_shallow++] = info->ours[i];
+       for (i = 0; i < info->nr_theirs; i++)
+               shallow[nr_shallow++] = info->theirs[i];
+
+       /*
+        * Prepare the commit graph to track what refs can reach what
+        * (new) shallow commits.
+        */
+       nr = get_max_object_index();
+       for (i = 0; i < nr; i++) {
+               struct object *o = get_indexed_object(i);
+               if (!o || o->type != OBJ_COMMIT)
+                       continue;
+
+               o->flags &= ~(UNINTERESTING | BOTTOM | SEEN);
+       }
+
+       memset(&pi, 0, sizeof(pi));
+       init_ref_bitmap(&pi.ref_bitmap);
+       pi.nr_bits = ref->nr;
+
+       /*
+        * "--not --all" to cut short the traversal if new refs
+        * connect to old refs. If not (e.g. force ref updates) it'll
+        * have to go down to the current shallow commits.
+        */
+       head_ref(mark_uninteresting, NULL);
+       for_each_ref(mark_uninteresting, NULL);
+
+       /* Mark potential bottoms so we won't go out of bound */
+       for (i = 0; i < nr_shallow; i++) {
+               struct commit *c = lookup_commit(sha1[shallow[i]]);
+               c->object.flags |= BOTTOM;
+       }
+
+       for (i = 0; i < ref->nr; i++)
+               paint_down(&pi, ref->sha1[i], i);
+
+       if (used) {
+               int bitmap_size = ((pi.nr_bits + 31) / 32) * sizeof(uint32_t);
+               memset(used, 0, sizeof(*used) * info->shallow->nr);
+               for (i = 0; i < nr_shallow; i++) {
+                       const struct commit *c = lookup_commit(sha1[shallow[i]]);
+                       uint32_t **map = ref_bitmap_at(&pi.ref_bitmap, c);
+                       if (*map)
+                               used[shallow[i]] = xmemdupz(*map, bitmap_size);
+               }
+               /*
+                * unreachable shallow commits are not removed from
+                * "ours" and "theirs". The user is supposed to run
+                * step 7 on every ref separately and not trust "ours"
+                * and "theirs" any more.
+                */
+       } else
+               post_assign_shallow(info, &pi.ref_bitmap, ref_status);
+
+       clear_ref_bitmap(&pi.ref_bitmap);
+       for (i = 0; i < pi.slab_count; i++)
+               free(pi.slab[i]);
+       free(pi.slab);
+       free(shallow);
+}
+
+struct commit_array {
+       struct commit **commits;
+       int nr, alloc;
+};
+
+static int add_ref(const char *refname,
+                  const unsigned char *sha1, int flags, void *cb_data)
+{
+       struct commit_array *ca = cb_data;
+       ALLOC_GROW(ca->commits, ca->nr + 1, ca->alloc);
+       ca->commits[ca->nr] = lookup_commit_reference_gently(sha1, 1);
+       if (ca->commits[ca->nr])
+               ca->nr++;
+       return 0;
+}
+
+static void update_refstatus(int *ref_status, int nr, uint32_t *bitmap)
+{
+       int i;
+       if (!ref_status)
+               return;
+       for (i = 0; i < nr; i++)
+               if (bitmap[i / 32] & (1 << (i % 32)))
+                       ref_status[i]++;
+}
+
+/*
+ * Step 7, reachability test on "ours" at commit level
+ */
+static void post_assign_shallow(struct shallow_info *info,
+                               struct ref_bitmap *ref_bitmap,
+                               int *ref_status)
+{
+       unsigned char (*sha1)[20] = info->shallow->sha1;
+       struct commit *c;
+       uint32_t **bitmap;
+       int dst, i, j;
+       int bitmap_nr = (info->ref->nr + 31) / 32;
+       struct commit_array ca;
+
+       trace_printf_key(TRACE_KEY, "shallow: post_assign_shallow\n");
+       if (ref_status)
+               memset(ref_status, 0, sizeof(*ref_status) * info->ref->nr);
+
+       /* Remove unreachable shallow commits from "theirs" */
+       for (i = dst = 0; i < info->nr_theirs; i++) {
+               if (i != dst)
+                       info->theirs[dst] = info->theirs[i];
+               c = lookup_commit(sha1[info->theirs[i]]);
+               bitmap = ref_bitmap_at(ref_bitmap, c);
+               if (!*bitmap)
+                       continue;
+               for (j = 0; j < bitmap_nr; j++)
+                       if (bitmap[0][j]) {
+                               update_refstatus(ref_status, info->ref->nr, *bitmap);
+                               dst++;
+                               break;
+                       }
+       }
+       info->nr_theirs = dst;
+
+       memset(&ca, 0, sizeof(ca));
+       head_ref(add_ref, &ca);
+       for_each_ref(add_ref, &ca);
+
+       /* Remove unreachable shallow commits from "ours" */
+       for (i = dst = 0; i < info->nr_ours; i++) {
+               if (i != dst)
+                       info->ours[dst] = info->ours[i];
+               c = lookup_commit(sha1[info->ours[i]]);
+               bitmap = ref_bitmap_at(ref_bitmap, c);
+               if (!*bitmap)
+                       continue;
+               for (j = 0; j < bitmap_nr; j++)
+                       if (bitmap[0][j] &&
+                           /* Step 7, reachability test at commit level */
+                           !in_merge_bases_many(c, ca.nr, ca.commits)) {
+                               update_refstatus(ref_status, info->ref->nr, *bitmap);
+                               dst++;
+                               break;
+                       }
+       }
+       info->nr_ours = dst;
+
+       free(ca.commits);
+}
+
+/* (Delayed) step 7, reachability test at commit level */
+int delayed_reachability_test(struct shallow_info *si, int c)
+{
+       if (si->need_reachability_test[c]) {
+               struct commit *commit = lookup_commit(si->shallow->sha1[c]);
+
+               if (!si->commits) {
+                       struct commit_array ca;
+                       memset(&ca, 0, sizeof(ca));
+                       head_ref(add_ref, &ca);
+                       for_each_ref(add_ref, &ca);
+                       si->commits = ca.commits;
+                       si->nr_commits = ca.nr;
+               }
+
+               si->reachable[c] = in_merge_bases_many(commit,
+                                                      si->nr_commits,
+                                                      si->commits);
+               si->need_reachability_test[c] = 0;
+       }
+       return si->reachable[c];
+}
diff --git a/shell.c b/shell.c
index 66350b220cc74abd383e10770f8325ba4e658e87..5c0d47a5cc1ae85a03e40321e97ddfa859ff7e1a 100644 (file)
--- a/shell.c
+++ b/shell.c
@@ -15,7 +15,7 @@ static int do_generic_cmd(const char *me, char *arg)
        setup_path();
        if (!arg || !(arg = sq_dequote(arg)))
                die("bad argument");
-       if (prefixcmp(me, "git-"))
+       if (!starts_with(me, "git-"))
                die("bad command");
 
        my_argv[0] = me + 4;
index 1170d01c4322b494cd900853279766edd030de32..83caf4a9147e6788107fb5fd23e6d9e1d6f6267f 100644 (file)
--- a/strbuf.c
+++ b/strbuf.c
@@ -1,6 +1,15 @@
 #include "cache.h"
 #include "refs.h"
 
+int starts_with(const char *str, const char *prefix)
+{
+       for (; ; str++, prefix++)
+               if (!*prefix)
+                       return 1;
+               else if (*str != *prefix)
+                       return 0;
+}
+
 int prefixcmp(const char *str, const char *prefix)
 {
        for (; ; str++, prefix++)
@@ -10,6 +19,15 @@ int prefixcmp(const char *str, const char *prefix)
                        return (unsigned char)*prefix - (unsigned char)*str;
 }
 
+int ends_with(const char *str, const char *suffix)
+{
+       int len = strlen(str), suflen = strlen(suffix);
+       if (len < suflen)
+               return 0;
+       else
+               return !strcmp(str + len - suflen, suffix);
+}
+
 int suffixcmp(const char *str, const char *suffix)
 {
        int len = strlen(str), suflen = strlen(suffix);
index debe904523252ae4fd2e18784641575c3f828dd2..d7c9f32f0ce98112ddd4d4a8499b227b181e09e6 100644 (file)
@@ -113,7 +113,7 @@ static enum input_source istream_source(const unsigned char *sha1,
 
        oi->typep = type;
        oi->sizep = &size;
-       status = sha1_object_info_extended(sha1, oi);
+       status = sha1_object_info_extended(sha1, oi, 0);
        if (status < 0)
                return stream_error;
 
@@ -538,7 +538,7 @@ int stream_blob_to_fd(int fd, unsigned const char *sha1, struct stream_filter *f
                        goto close_and_exit;
        }
        if (kept && (lseek(fd, kept - 1, SEEK_CUR) == (off_t) -1 ||
-                    write(fd, "", 1) != 1))
+                    xwrite(fd, "", 1) != 1))
                goto close_and_exit;
        result = 0;
 
index 1905d75b2b09ee7e1503b35bf072710a659ea1ef..613857e400f826f3d4ea28c87025b2c2df42408a 100644 (file)
@@ -201,7 +201,7 @@ void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt,
 
 int submodule_config(const char *var, const char *value, void *cb)
 {
-       if (!prefixcmp(var, "submodule."))
+       if (starts_with(var, "submodule."))
                return parse_submodule_config_option(var, value);
        else if (!strcmp(var, "fetch.recursesubmodules")) {
                config_fetch_recurse_submodules = parse_fetch_recurse_submodules_arg(var, value);
old mode 100644 (file)
new mode 100755 (executable)
old mode 100644 (file)
new mode 100755 (executable)
old mode 100644 (file)
new mode 100755 (executable)
old mode 100644 (file)
new mode 100755 (executable)
old mode 100644 (file)
new mode 100755 (executable)
old mode 100644 (file)
new mode 100755 (executable)
old mode 100644 (file)
new mode 100755 (executable)
index c9d105d70725b9a97d5a9518e83c615821e91c23..304c7b7d8714f9f985cddcc91ba3006f668de127 100644 (file)
@@ -116,6 +116,27 @@ test_expect_success 'blame evil merge' '
        check_count A 2 B 1 B1 2 B2 1 "A U Thor" 1
 '
 
+test_expect_success 'blame huge graft' '
+       test_when_finished "git checkout branch2" &&
+       test_when_finished "rm -f .git/info/grafts" &&
+       graft= &&
+       for i in 0 1 2
+       do
+               for j in 0 1 2 3 4 5 6 7 8 9
+               do
+                       git checkout --orphan "$i$j" &&
+                       printf "%s\n" "$i" "$j" >file &&
+                       test_tick &&
+                       GIT_AUTHOR_NAME=$i$j GIT_AUTHOR_EMAIL=$i$j@test.git \
+                       git commit -a -m "$i$j" &&
+                       commit=$(git rev-parse --verify HEAD) &&
+                       graft="$graft$commit "
+               done
+       done &&
+       printf "%s " $graft >.git/info/grafts &&
+       check_count -h 00 01 1 10 1
+'
+
 test_expect_success 'setup incomplete line' '
        echo "incomplete" | tr -d "\\012" >>file &&
        GIT_AUTHOR_NAME="C" GIT_AUTHOR_EMAIL="C@test.git" \
index 8cf909a6c5521729b24064c8273209ec2338d29c..d5dab5a94f1d881b8b103f6290ba91e4ec596ec6 100644 (file)
@@ -1,4 +1,5 @@
-#!/bin/sh
+# Initialization and helpers for Gitweb tests, which source this
+# shell library instead of test-lib.sh.
 #
 # Copyright (c) 2007 Jakub Narebski
 #
index 11397f747b9658146a133f5fa65923cc4edfb11f..2be955fafba60e9dbf30a3ab7f0bc1dfae270140 100644 (file)
@@ -1,7 +1,6 @@
-#!/bin/sh
-#
-# Ensures that tests are run under Bash; primarily intended for running tests
-# of the completion script.
+# Shell library sourced instead of ./test-lib.sh by tests that need
+# to run under Bash; primarily intended for tests of the completion
+# script.
 
 if test -n "$BASH" && test -z "$POSIXLY_CORRECT"; then
        # we are in full-on bash mode
index 44263ade2533b848220c234441ab67813ca2b0b4..5076718916388d6abf483220cd35f9f61394abc4 100644 (file)
@@ -1,4 +1,4 @@
-#!/bin/sh
+# Shell library sourced instead of ./test-lib.sh by cvsimport tests.
 
 . ./test-lib.sh
 
index 75ffd9174fbfb7fc6b14d4777f6613d9f052d64f..8b4dbf22d2d140bef7ca0e106a389cb0ca1d4681 100644 (file)
@@ -1,4 +1,5 @@
-#!/bin/sh
+# Helpers shared by the test scripts for diff algorithms (patience,
+# histogram, etc).
 
 test_diff_frobnitz() {
        cat >file1 <<\EOF
index ae8883a07514c47b0568c9323698e879b38703e5..eec757f104708df3a00935cef89dd7dad537afb9 100644 (file)
@@ -1,4 +1,5 @@
-#!/bin/sh
+# Initialization and Icelandic locale for basic git i18n tests,
+# which source this scriptlet instead of ./test-lib.sh.
 #
 # Copyright (c) 2010 Ævar Arnfjörð Bjarmason
 #
index 87f0ad8f4182b13903b9649308e36e34a6122494..394b06b32f838463221e9c2f49380fd9eb08bb9d 100644 (file)
@@ -1,4 +1,20 @@
-#!/bin/sh
+# Shell library to run git-daemon in tests.  Ends the test early if
+# GIT_TEST_GIT_DAEMON is not set.
+#
+# Usage:
+#
+#      . ./test-lib.sh
+#      . "$TEST_DIRECTORY"/lib-git-daemon.sh
+#      start_git_daemon
+#
+#      test_expect_success '...' '
+#              ...
+#      '
+#
+#      test_expect_success ...
+#
+#      stop_git_daemon
+#      test_done
 
 if test -z "$GIT_TEST_GIT_DAEMON"
 then
index ccd918e79ef4fa7962984c5ed4890ec07e92f03a..5aa8adcf9c8a53b8a643a37b9474886448f6a45b 100644 (file)
@@ -47,15 +47,22 @@ P4DPORT=$((10669 + ($testid - $git_p4_test_start)))
 
 P4PORT=localhost:$P4DPORT
 P4CLIENT=client
-P4EDITOR=:
+P4USER=author
+P4EDITOR=true
 unset P4CHARSET
-export P4PORT P4CLIENT P4EDITOR P4CHARSET
+export P4PORT P4CLIENT P4USER P4EDITOR P4CHARSET
 
 db="$TRASH_DIRECTORY/db"
 cli="$TRASH_DIRECTORY/cli"
 git="$TRASH_DIRECTORY/git"
 pidfile="$TRASH_DIRECTORY/p4d.pid"
 
+# git p4 submit generates a temp file, which will
+# not get cleaned up if the submission fails.  Don't
+# clutter up /tmp on the test machine.
+TMPDIR="$TRASH_DIRECTORY"
+export TMPDIR
+
 start_p4d() {
        mkdir -p "$db" "$cli" "$git" &&
        rm -f "$pidfile" &&
@@ -96,12 +103,24 @@ start_p4d() {
                return 1
        fi
 
+       # build a p4 user so author@example.com has an entry
+       p4_add_user author
+
        # build a client
        client_view "//depot/... //client/..." &&
 
        return 0
 }
 
+p4_add_user() {
+       name=$1 &&
+       p4 user -f -i <<-EOF
+       User: $name
+       Email: $name@example.com
+       FullName: Dr. $name
+       EOF
+}
+
 kill_p4d() {
        pid=$(cat "$pidfile")
        # it had better exist for the first kill
index ad8f1ef71efcd7e449d2cad8edd3cf80c98ca188..bfdff2a8c93091c8e2db8e03a74eae5c5a53991c 100644 (file)
@@ -1,4 +1,31 @@
-#!/bin/sh
+# Shell library to run an HTTP server for use in tests.
+# Ends the test early if httpd tests should not be run,
+# for example because the user has not enabled them.
+#
+# Usage:
+#
+#      . ./test-lib.sh
+#      . "$TEST_DIRECTORY"/lib-httpd.sh
+#      start_httpd
+#
+#      test_expect_success '...' '
+#              ...
+#      '
+#
+#      test_expect_success ...
+#
+#      stop_httpd
+#      test_done
+#
+# Can be configured using the following variables.
+#
+#    GIT_TEST_HTTPD              enable HTTPD tests
+#    LIB_HTTPD_PATH              web server path
+#    LIB_HTTPD_MODULE_PATH       web server modules path
+#    LIB_HTTPD_PORT              listening port
+#    LIB_HTTPD_DAV               enable DAV
+#    LIB_HTTPD_SVN               enable SVN
+#    LIB_HTTPD_SSL               enable SSL
 #
 # Copyright (c) 2008 Clemens Buchacher <drizzd@aon.at>
 #
@@ -102,7 +129,7 @@ prepare_httpd() {
        HTTPD_DEST=127.0.0.1:$LIB_HTTPD_PORT
        HTTPD_URL=$HTTPD_PROTO://$HTTPD_DEST
        HTTPD_URL_USER=$HTTPD_PROTO://user%40host@$HTTPD_DEST
-       HTTPD_URL_USER_PASS=$HTTPD_PROTO://user%40host:user%40host@$HTTPD_DEST
+       HTTPD_URL_USER_PASS=$HTTPD_PROTO://user%40host:pass%40host@$HTTPD_DEST
 
        if test -n "$LIB_HTTPD_DAV" -o -n "$LIB_HTTPD_SVN"
        then
@@ -190,7 +217,15 @@ setup_askpass_helper() {
        test_expect_success 'setup askpass helper' '
                write_script "$TRASH_DIRECTORY/askpass" <<-\EOF &&
                echo >>"$TRASH_DIRECTORY/askpass-query" "askpass: $*" &&
-               cat "$TRASH_DIRECTORY/askpass-response"
+               case "$*" in
+               *Username*)
+                       what=user
+                       ;;
+               *Password*)
+                       what=pass
+                       ;;
+               esac &&
+               cat "$TRASH_DIRECTORY/askpass-$what"
                EOF
                GIT_ASKPASS="$TRASH_DIRECTORY/askpass" &&
                export GIT_ASKPASS &&
@@ -200,7 +235,8 @@ setup_askpass_helper() {
 
 set_askpass() {
        >"$TRASH_DIRECTORY/askpass-query" &&
-       echo "$*" >"$TRASH_DIRECTORY/askpass-response"
+       echo "$1" >"$TRASH_DIRECTORY/askpass-user" &&
+       echo "$2" >"$TRASH_DIRECTORY/askpass-pass"
 }
 
 expect_askpass() {
index f2fbcad33e28adcbd30a539371e5d659251ba3a6..99a34d648742f669e7b501298a92fab401bc1971 100644 (file)
@@ -1 +1 @@
-user@host:nKpa8pZUHx/ic
+user@host:xb4E8pqD81KQs
index b96e1254dd36821b4a390726c7aefb5286c243f3..75098465716512a3373e64d51a5cdf848e9f1101 100644 (file)
@@ -1,5 +1,3 @@
-#!/bin/sh
-#
 # Support routines for hand-crafting weird or malicious packs.
 #
 # You can make a complete pack like:
index ba03eab14fc29934f095673fa82af658b06bf865..3aa7a3ffd8b0103752987872c84e75ea2b299b9e 100644 (file)
@@ -1,4 +1,4 @@
-#!/bin/sh
+# Helpers for tests of git's choice of pager.
 
 test_expect_success 'determine default pager' '
        test_might_fail git config --unset core.pager &&
diff --git a/t/lib-prereq-FILEMODE.sh b/t/lib-prereq-FILEMODE.sh
deleted file mode 100644 (file)
index bce5a4c..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2010 Ævar Arnfjörð Bjarmason
-#
-
-if test "$(git config --bool core.filemode)" = false
-then
-       say 'filemode disabled on the filesystem'
-else
-       test_set_prereq FILEMODE
-fi
index ef079afc4638a4f02fa0c12f1d54ad622047ed56..b95f4856061da723cbe36bd6955c552bee7a29aa 100644 (file)
@@ -1,5 +1,3 @@
-#!/bin/sh
-#
 # Helper functions to check if read-tree would succeed/fail as expected with
 # and without the dry-run option. They also test that the dry-run does not
 # write the index and that together with -u it doesn't touch the work tree.
index 8ff87fb3f9bee1c6955f84b66027037100ebbbd3..6bd252212a7eb901dded2a460eebe92aa3b6212b 100644 (file)
@@ -1,4 +1,4 @@
-#!/bin/sh
+# Helper functions used by interactive rebase tests.
 
 # After setting the fake editor with this function, you can
 #
index 737df289a1450b80aed58b52a59cf00eb2d77da3..9a2dca506a8d6c15f52e6ded3aa22a0f08f507a1 100644 (file)
@@ -1,4 +1,4 @@
-#!/bin/sh
+# Helpers for terminal output tests.
 
 test_expect_success PERL 'set up terminal for tests' '
        # Reading from the pty master seems to get stuck _sometimes_
index 4f71a63b0a80d7a625d246cf7b98b66c473adc90..16359d51ae5a58ac629391b27d6b42acfc2ddb5e 100755 (executable)
@@ -14,4 +14,16 @@ test_perf 'rev-list --all --objects' '
        git rev-list --all --objects >/dev/null
 '
 
+test_expect_success 'create new unreferenced commit' '
+       commit=$(git commit-tree HEAD^{tree} -p HEAD)
+'
+
+test_perf 'rev-list $commit --not --all' '
+       git rev-list $commit --not --all >/dev/null
+'
+
+test_perf 'rev-list --objects $commit --not --all' '
+       git rev-list --objects $commit --not --all >/dev/null
+'
+
 test_done
diff --git a/t/perf/p4001-diff-no-index.sh b/t/perf/p4001-diff-no-index.sh
new file mode 100755 (executable)
index 0000000..683be69
--- /dev/null
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+test_description="Test diff --no-index performance"
+
+. ./perf-lib.sh
+
+test_perf_large_repo
+test_checkout_worktree
+
+file1=$(git ls-files | tail -n 2 | head -1)
+file2=$(git ls-files | tail -n 1 | head -1)
+
+test_expect_success "empty files, so they take no time to diff" "
+       echo >$file1 &&
+       echo >$file2
+"
+
+test_perf "diff --no-index" "
+       git diff --no-index $file1 $file2 >/dev/null
+"
+
+test_done
index f4eecaa17110c2974cfdedab90db4604b6923443..a8c9574291b87d9bb1906a9d61faa9b157178b76 100644 (file)
@@ -1,4 +1,6 @@
-#!/bin/sh
+# Performance testing framework.  Each perf script starts much like
+# a normal test script, except it sources this library instead of
+# test-lib.sh.  See t/perf/README for documentation.
 #
 # Copyright (c) 2011 Thomas Rast
 #
index 10be52beedb4058318555e096d51517f1c30ad9f..a2bb63ce8e5e5586691affb51f9cff1f4d1e327a 100755 (executable)
@@ -41,20 +41,17 @@ test_expect_success '.git/objects should have 3 subdirectories' '
 test_expect_success 'success is reported like this' '
        :
 '
-test_expect_failure 'pretend we have a known breakage' '
-       false
-'
 
 run_sub_test_lib_test () {
        name="$1" descr="$2" # stdin is the body of the test code
        shift 2
        mkdir "$name" &&
        (
-               # Pretend we're a test harness.  This prevents
-               # test-lib from writing the counts to a file that will
-               # later be summarized, showing spurious "failed" tests
-               HARNESS_ACTIVE=t &&
-               export HARNESS_ACTIVE &&
+               # Pretend we're not running under a test harness, whether we
+               # are or not. The test-lib output depends on the setting of
+               # this variable, so we need a stable setting under which to run
+               # the sub-test.
+               sane_unset HARNESS_ACTIVE &&
                cd "$name" &&
                cat >"$name.sh" <<-EOF &&
                #!$SHELL_PATH
@@ -71,6 +68,8 @@ run_sub_test_lib_test () {
                cat >>"$name.sh" &&
                chmod +x "$name.sh" &&
                export TEST_DIRECTORY &&
+               TEST_OUTPUT_DIRECTORY=$(pwd) &&
+               export TEST_OUTPUT_DIRECTORY &&
                ./"$name.sh" "$@" >out 2>err
        )
 }
@@ -233,16 +232,13 @@ test_expect_success 'test --verbose' '
        grep -v "^Initialized empty" test-verbose/out+ >test-verbose/out &&
        check_sub_test_lib_test test-verbose <<-\EOF
        > expecting success: true
-       > Z
        > ok 1 - passing test
        > Z
        > expecting success: echo foo
        > foo
-       > Z
        > ok 2 - test with output
        > Z
        > expecting success: false
-       > Z
        > not ok 3 - failing test
        > #     false
        > Z
@@ -265,7 +261,6 @@ test_expect_success 'test --verbose-only' '
        > Z
        > expecting success: echo foo
        > foo
-       > Z
        > ok 2 - test with output
        > Z
        > not ok 3 - failing test
index cb144258ccc3f32025c4b7beba6ac02c427612d8..37e9396e5dae8957ba73507018d5c334ee65e30d 100755 (executable)
@@ -7,7 +7,7 @@ Verify that plumbing commands work when .git is a file
 . ./test-lib.sh
 
 objpath() {
-    echo "$1" | sed -e 's|\(..\)|\1/|'
+       echo "$1" | sed -e 's|\(..\)|\1/|'
 }
 
 objck() {
@@ -19,7 +19,6 @@ objck() {
        fi
 }
 
-
 test_expect_success 'initial setup' '
        REAL="$(pwd)/.real" &&
        mv .git "$REAL"
index 0b98b6f8d090ecc3dfb48fe6fda5889dfbc5e018..b9d79476e20b4ddf0f9e85c672df2061fb910f9d 100755 (executable)
@@ -13,7 +13,6 @@ attr_check () {
        test_line_count = 0 err
 }
 
-
 test_expect_success 'setup' '
        mkdir -p a/b/d a/c b &&
        (
old mode 100644 (file)
new mode 100755 (executable)
index 8a1bc5c53281eb6f44c15d68dd0ae737bc0125f3..a72e700ae434b07b08b48a68d4c0ebaa8173002b 100755 (executable)
@@ -85,6 +85,28 @@ $content"
                git cat-file --batch-check="%(objecttype) %(rest)" >actual &&
        test_cmp expect actual
     '
+
+    test -z "$content" ||
+    test_expect_success "--batch without type ($type)" '
+       {
+               echo "$size" &&
+               maybe_remove_timestamp "$content" $no_ts
+       } >expect &&
+       echo $sha1 | git cat-file --batch="%(objectsize)" >actual.full &&
+       maybe_remove_timestamp "$(cat actual.full)" $no_ts >actual &&
+       test_cmp expect actual
+    '
+
+    test -z "$content" ||
+    test_expect_success "--batch without size ($type)" '
+       {
+               echo "$type" &&
+               maybe_remove_timestamp "$content" $no_ts
+       } >expect &&
+       echo $sha1 | git cat-file --batch="%(objecttype)" >actual.full &&
+       maybe_remove_timestamp "$(cat actual.full)" $no_ts >actual &&
+       test_cmp expect actual
+    '
 }
 
 hello_content="Hello World"
@@ -240,4 +262,38 @@ test_expect_success "--batch-check with multiple sha1s gives correct format" '
     "$(echo_without_newline "$batch_check_input" | git cat-file --batch-check)"
 '
 
+test_expect_success 'setup blobs which are likely to delta' '
+       test-genrandom foo 10240 >foo &&
+       { cat foo; echo plus; } >foo-plus &&
+       git add foo foo-plus &&
+       git commit -m foo &&
+       cat >blobs <<-\EOF
+       HEAD:foo
+       HEAD:foo-plus
+       EOF
+'
+
+test_expect_success 'confirm that neither loose blob is a delta' '
+       cat >expect <<-EOF
+       $_z40
+       $_z40
+       EOF
+       git cat-file --batch-check="%(deltabase)" <blobs >actual &&
+       test_cmp expect actual
+'
+
+# To avoid relying too much on the current delta heuristics,
+# we will check only that one of the two objects is a delta
+# against the other, but not the order. We can do so by just
+# asking for the base of both, and checking whether either
+# sha1 appears in the output.
+test_expect_success '%(deltabase) reports packed delta bases' '
+       git repack -ad &&
+       git cat-file --batch-check="%(deltabase)" <blobs >actual &&
+       {
+               grep "$(git rev-parse HEAD:foo)" actual ||
+               grep "$(git rev-parse HEAD:foo-plus)" actual
+       }
+'
+
 test_done
index 46103a1591680c9803588d24a135abf79fe64ae9..3a2c81968cf7d539410358de1c8af6cf1d2500c9 100755 (executable)
@@ -3,17 +3,28 @@
 test_description='Test wacky input to git config'
 . ./test-lib.sh
 
+# Leaving off the newline is intentional!
 setup() {
        (printf "[section]\n" &&
        printf "  key = foo") >.git/config
 }
 
+# 'check section.key value' verifies that the entry for section.key is
+# 'value'
 check() {
        echo "$2" >expected
        git config --get "$1" >actual 2>&1
        test_cmp actual expected
 }
 
+# 'check section.key regex value' verifies that the entry for
+# section.key *that matches 'regex'* is 'value'
+check_regex() {
+       echo "$3" >expected
+       git config --get "$1" "$2" >actual 2>&1
+       test_cmp actual expected
+}
+
 test_expect_success 'modify same key' '
        setup &&
        git config section.key bar &&
@@ -47,4 +58,57 @@ test_expect_success 'do not crash on special long config line' '
        check section.key "$LONG_VALUE"
 '
 
+setup_many() {
+       setup &&
+       # This time we want the newline so that we can tack on more
+       # entries.
+       echo >>.git/config &&
+       # Semi-efficient way of concatenating 5^5 = 3125 lines. Note
+       # that because 'setup' already put one line, this means 3126
+       # entries for section.key in the config file.
+       cat >5to1 <<-\EOF &&
+         key = foo
+         key = foo
+         key = foo
+         key = foo
+         key = foo
+       EOF
+       cat 5to1 5to1 5to1 5to1 5to1 >5to2 &&      # 25
+       cat 5to2 5to2 5to2 5to2 5to2 >5to3 &&      # 125
+       cat 5to3 5to3 5to3 5to3 5to3 >5to4 &&      # 635
+       cat 5to4 5to4 5to4 5to4 5to4 >>.git/config # 3125
+}
+
+test_expect_success 'get many entries' '
+       setup_many &&
+       git config --get-all section.key >actual &&
+       test_line_count = 3126 actual
+'
+
+test_expect_success 'get many entries by regex' '
+       setup_many &&
+       git config --get-regexp "sec.*ke." >actual &&
+       test_line_count = 3126 actual
+'
+
+test_expect_success 'add and replace one of many entries' '
+       setup_many &&
+       git config --add section.key bar &&
+       check_regex section.key "b.*r" bar &&
+       git config section.key beer "b.*r" &&
+       check_regex section.key "b.*r" beer
+'
+
+test_expect_success 'replace many entries' '
+       setup_many &&
+       git config --replace-all section.key bar &&
+       check section.key bar
+'
+
+test_expect_success 'unset many entries' '
+       setup_many &&
+       git config --unset-all section.key &&
+       test_must_fail git config section.key
+'
+
 test_done
index f950c101287c2f05bbd70c1faffbcf2cda1edbee..613d9bfe1bbb153d1c2e647aee26a60e07a6af86 100755 (executable)
@@ -196,4 +196,28 @@ test_expect_success 'dotdot is not an empty set' '
        test_cmp expect actual
 '
 
+test_expect_success 'arg before dashdash must be a revision (missing)' '
+       test_must_fail git rev-parse foobar -- 2>stderr &&
+       test_i18ngrep "bad revision" stderr
+'
+
+test_expect_success 'arg before dashdash must be a revision (file)' '
+       >foobar &&
+       test_must_fail git rev-parse foobar -- 2>stderr &&
+       test_i18ngrep "bad revision" stderr
+'
+
+test_expect_success 'arg before dashdash must be a revision (ambiguous)' '
+       >foobar &&
+       git update-ref refs/heads/foobar HEAD &&
+       {
+               # we do not want to use rev-parse here, because
+               # we are testing it
+               cat .git/refs/heads/foobar &&
+               printf "%s\n" --
+       } >expect &&
+       git rev-parse foobar -- >actual &&
+       test_cmp expect actual
+'
+
 test_done
index 2a19e797eb7cffba28deb4e99b3708c5b26602ae..178694ee63937a9ec1f8d182d54441c2d20bd4a3 100755 (executable)
@@ -17,6 +17,9 @@ test_expect_success 'setup' '
         test_commit 4 &&
         git branch --track my-side origin/side &&
         git branch --track local-master master &&
+        git branch --track fun@ny origin/side &&
+        git branch --track @funny origin/side &&
+        git branch --track funny@ origin/side &&
         git remote add -t master master-only .. &&
         git fetch master-only &&
         git branch bad-upstream &&
@@ -54,6 +57,24 @@ test_expect_success 'my-side@{upstream} resolves to correct full name' '
        test refs/remotes/origin/side = "$(full_name my-side@{u})"
 '
 
+test_expect_success 'upstream of branch with @ in middle' '
+       full_name fun@ny@{u} >actual &&
+       echo refs/remotes/origin/side >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success 'upstream of branch with @ at start' '
+       full_name @funny@{u} >actual &&
+       echo refs/remotes/origin/side >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success 'upstream of branch with @ at end' '
+       full_name funny@@{u} >actual &&
+       echo refs/remotes/origin/side >expect &&
+       test_cmp expect actual
+'
+
 test_expect_success 'refs/heads/my-side@{upstream} does not resolve to my-side{upstream}' '
        test_must_fail full_name refs/heads/my-side@{upstream}
 '
@@ -210,4 +231,20 @@ test_expect_success 'log -g other@{u}@{now}' '
        test_cmp expect actual
 '
 
+test_expect_success '@{reflog}-parsing does not look beyond colon' '
+       echo content >@{yesterday} &&
+       git add @{yesterday} &&
+       git commit -m "funny reflog file" &&
+       git hash-object @{yesterday} >expect &&
+       git rev-parse HEAD:@{yesterday} >actual
+'
+
+test_expect_success '@{upstream}-parsing does not look beyond colon' '
+       echo content >@{upstream} &&
+       git add @{upstream} &&
+       git commit -m "funny upstream file" &&
+       git hash-object @{upstream} >expect &&
+       git rev-parse HEAD:@{upstream} >actual
+'
+
 test_done
index ceb844985f97e9472ffcc3fe2f375912d126a4e8..078e1195dfa1625796fdbbf1b054ebf8a1ba2621 100755 (executable)
@@ -9,8 +9,11 @@ check() {
                if test '$2' = 'commit'
                then
                        git log -1 --format=%s '$1' >actual
-               else
+               elif test '$2' = 'ref'
+               then
                        git rev-parse --symbolic-full-name '$1' >actual
+               else
+                       git cat-file -p '$1' >actual
                fi &&
                test_cmp expect actual
        "
@@ -82,4 +85,14 @@ check HEAD ref refs/heads/old-branch
 check "HEAD@{1}" commit new-two
 check "@{1}" commit old-one
 
+test_expect_success 'create path with @' '
+       echo content >normal &&
+       echo content >fun@ny &&
+       git add normal fun@ny &&
+       git commit -m "funny path"
+'
+
+check "@:normal" blob content
+check "@:fun@ny" blob content
+
 test_done
index ebf93b0695dfac37fc276e1f07377d858eb3e1da..6d94b1fcd94e9f724c93a389c50066c92c46e1b9 100755 (executable)
@@ -134,12 +134,22 @@ test_expect_success 'fail when upstream arg is missing and not configured' '
        test_must_fail git rebase
 '
 
-test_expect_success 'default to @{upstream} when upstream arg is missing' '
+test_expect_success 'default to common base in @{upstream}s reflog if no upstream arg' '
+       git checkout -b default-base master &&
        git checkout -b default topic &&
        git config branch.default.remote . &&
-       git config branch.default.merge refs/heads/master &&
+       git config branch.default.merge refs/heads/default-base &&
        git rebase &&
-       test "$(git rev-parse default~1)" = "$(git rev-parse master)"
+       git rev-parse --verify default-base >expect &&
+       git rev-parse default~1 >actual &&
+       test_cmp expect actual &&
+       git checkout default-base &&
+       git reset --hard HEAD^ &&
+       git checkout default &&
+       git rebase &&
+       git rev-parse --verify default-base >expect &&
+       git rev-parse default~1 >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success 'rebase -q is quiet' '
index 540c49bab618ffb75dd789b098ef982634648f7c..3d305814b9d755a827d27ed9036ebb2f8d48abe6 100755 (executable)
@@ -705,6 +705,22 @@ test_expect_success 'rm of a populated nested submodule with a nested .git direc
        rm -rf submod
 '
 
+test_expect_success 'checking out a commit after submodule removal needs manual updates' '
+       git commit -m "submodule removal" submod &&
+       git checkout HEAD^ &&
+       git submodule update &&
+       git checkout -q HEAD^ 2>actual &&
+       git checkout -q master 2>actual &&
+       echo "warning: unable to rmdir submod: Directory not empty" >expected &&
+       test_i18ncmp expected actual &&
+       git status -s submod >actual &&
+       echo "?? submod/" >expected &&
+       test_cmp expected actual &&
+       rm -rf submod &&
+       git status -s -uno --ignore-submodules=none > actual &&
+       ! test -s actual
+'
+
 test_expect_success 'rm of d/f when d has become a non-directory' '
        rm -rf d &&
        mkdir d &&
index aab86e838b806f9bb2289f536f4ba9324d75da0a..fe274e2fb14869314dfb21d563fd2c3f367365c8 100755 (executable)
@@ -272,6 +272,25 @@ test_expect_success '"add non-existent" should fail' '
        ! (git ls-files | grep "non-existent")
 '
 
+test_expect_success 'git add -A on empty repo does not error out' '
+       rm -fr empty &&
+       git init empty &&
+       (
+               cd empty &&
+               git add -A . &&
+               git add -A
+       )
+'
+
+test_expect_success '"git add ." in empty repo' '
+       rm -fr empty &&
+       git init empty &&
+       (
+               cd empty &&
+               git add .
+       )
+'
+
 test_expect_success 'git add --dry-run of existing changed file' "
        echo new >>track-this &&
        git add --dry-run track-this >actual 2>&1 &&
index 9dc91d09d7ef3c66c39bcf930bb486312cf291af..24ddd8a704b22e32322b8ef52bbd0d2e6195e5e1 100755 (executable)
@@ -2,7 +2,6 @@
 
 test_description='add -i basic tests'
 . ./test-lib.sh
-. "$TEST_DIRECTORY"/lib-prereq-FILEMODE.sh
 
 if ! test_have_prereq PERL
 then
index debda7a678277e1198ba6620210b66984caf03a2..5b79b216e2e3bb837e58828a2e9a292cb2d7cc53 100755 (executable)
@@ -673,4 +673,16 @@ test_expect_success 'store updates stash ref and reflog' '
        grep quux bazzy
 '
 
+test_expect_success 'handle stash specification with spaces' '
+       git stash clear &&
+       echo pig >file &&
+       git stash &&
+       stamp=$(git log -g --format="%cd" -1 refs/stash) &&
+       test_tick &&
+       echo cow >file &&
+       git stash &&
+       git stash apply "stash@{$stamp}" &&
+       grep pig file
+'
+
 test_done
index 8a309795c9c9654446d82c2e474e1ca08486e081..bcae35ac1c93aaf79032db5ee09fd2d3400101f5 100755 (executable)
@@ -193,6 +193,19 @@ test_expect_success 'GIT_EXTERNAL_DIFF with more than one changed files' '
        GIT_EXTERNAL_DIFF=echo git diff
 '
 
+test_expect_success 'GIT_EXTERNAL_DIFF path counter/total' '
+       write_script external-diff.sh <<-\EOF &&
+       echo $GIT_DIFF_PATH_COUNTER of $GIT_DIFF_PATH_TOTAL >>counter.txt
+       EOF
+       >counter.txt &&
+       cat >expect <<-\EOF &&
+       1 of 2
+       2 of 2
+       EOF
+       GIT_EXTERNAL_DIFF=./external-diff.sh git diff &&
+       test_cmp expect counter.txt
+'
+
 test_expect_success 'GIT_EXTERNAL_DIFF generates pretty paths' '
        touch file.ext &&
        git add file.ext &&
index 979e98398bebc21fe664ca4a19770e5b251bfc97..2ab3c487340569d7f2f8164776907ef30df2d415 100755 (executable)
@@ -29,4 +29,30 @@ test_expect_success 'git diff --no-index relative path outside repo' '
        )
 '
 
+test_expect_success 'git diff --no-index with broken index' '
+       (
+               cd repo &&
+               echo broken >.git/index &&
+               git diff --no-index a ../non/git/a
+       )
+'
+
+test_expect_success 'git diff outside repo with broken index' '
+       (
+               cd repo &&
+               git diff ../non/git/a ../non/git/b
+       )
+'
+
+test_expect_success 'git diff --no-index executed outside repo gives correct error message' '
+       (
+               GIT_CEILING_DIRECTORIES=$TRASH_DIRECTORY/non &&
+               export GIT_CEILING_DIRECTORIES &&
+               cd non/git &&
+               test_must_fail git diff --no-index a 2>actual.err &&
+               echo "usage: git diff --no-index <path> <path>" >expect.err &&
+               test_cmp expect.err actual.err
+       )
+'
+
 test_done
diff --git a/t/t4056-diff-order.sh b/t/t4056-diff-order.sh
new file mode 100755 (executable)
index 0000000..9e2b29e
--- /dev/null
@@ -0,0 +1,100 @@
+#!/bin/sh
+
+test_description='diff order'
+
+. ./test-lib.sh
+
+create_files () {
+       echo "$1" >a.h &&
+       echo "$1" >b.c &&
+       echo "$1" >c/Makefile &&
+       echo "$1" >d.txt &&
+       git add a.h b.c c/Makefile d.txt &&
+       git commit -m"$1"
+}
+
+test_expect_success 'setup' '
+       mkdir c &&
+       create_files 1 &&
+       create_files 2 &&
+
+       cat >order_file_1 <<-\EOF &&
+       *Makefile
+       *.txt
+       *.h
+       EOF
+
+       cat >order_file_2 <<-\EOF &&
+       *Makefile
+       *.h
+       *.c
+       EOF
+
+       cat >expect_none <<-\EOF &&
+       a.h
+       b.c
+       c/Makefile
+       d.txt
+       EOF
+
+       cat >expect_1 <<-\EOF &&
+       c/Makefile
+       d.txt
+       a.h
+       b.c
+       EOF
+
+       cat >expect_2 <<-\EOF
+       c/Makefile
+       a.h
+       b.c
+       d.txt
+       EOF
+'
+
+test_expect_success "no order (=tree object order)" '
+       git diff --name-only HEAD^..HEAD >actual &&
+       test_cmp expect_none actual
+'
+
+test_expect_success 'missing orderfile' '
+       rm -f bogus_file &&
+       test_must_fail git diff -Obogus_file --name-only HEAD^..HEAD
+'
+
+test_expect_success POSIXPERM,SANITY 'unreadable orderfile' '
+       >unreadable_file &&
+       chmod -r unreadable_file &&
+       test_must_fail git diff -Ounreadable_file --name-only HEAD^..HEAD
+'
+
+for i in 1 2
+do
+       test_expect_success "orderfile using option ($i)" '
+               git diff -Oorder_file_$i --name-only HEAD^..HEAD >actual &&
+               test_cmp expect_$i actual
+       '
+
+       test_expect_success PIPE "orderfile is fifo ($i)" '
+               rm -f order_fifo &&
+               mkfifo order_fifo &&
+               {
+                       cat order_file_$i >order_fifo &
+               } &&
+               git diff -O order_fifo --name-only HEAD^..HEAD >actual &&
+               wait &&
+               test_cmp expect_$i actual
+       '
+
+       test_expect_success "orderfile using config ($i)" '
+               git -c diff.orderfile=order_file_$i diff --name-only HEAD^..HEAD >actual &&
+               test_cmp expect_$i actual
+       '
+
+       test_expect_success "cancelling configured orderfile ($i)" '
+               git -c diff.orderfile=order_file_$i diff -O/dev/null --name-only HEAD^..HEAD >actual &&
+               test_cmp expect_none actual
+       '
+done
+
+test_done
index e3ea3d5114b8f6a09b79c9a1323c563da0c7b170..49e2d6c34983c7e4620739a52ecc03cf158efe3a 100755 (executable)
@@ -7,7 +7,6 @@ test_description='git apply handling copy/rename patch.
 
 '
 . ./test-lib.sh
-. "$TEST_DIRECTORY"/lib-prereq-FILEMODE.sh
 
 # setup
 
index 2298ece8019d79ef718ef658bdac74493d265b92..1e4d4380bf01c081aeb1df44cef40319063f2faa 100755 (executable)
@@ -48,12 +48,12 @@ test_expect_success 'apply in reverse' '
 
 test_expect_success 'setup separate repository lacking postimage' '
 
-       git tar-tree initial initial | $TAR xf - &&
+       git archive --format=tar --prefix=initial/ initial | $TAR xf - &&
        (
                cd initial && git init && git add .
        ) &&
 
-       git tar-tree second second | $TAR xf - &&
+       git archive --format=tar --prefix=second/ second | $TAR xf - &&
        (
                cd second && git init && git add .
        )
index c5fecdfed43dc5682307dd0fdb66ddd78dece056..497b62868d4aa0f81c62178ee9b1a5a108b89c0d 100755 (executable)
@@ -6,7 +6,6 @@
 test_description='git apply -p handling.'
 
 . ./test-lib.sh
-. "$TEST_DIRECTORY"/lib-prereq-FILEMODE.sh
 
 test_expect_success setup '
        mkdir sub &&
index 0d36ebdc8653d7d22ab5831c1f154db452e4558e..c268298eaf5672c649ca3e4815dc4f43d71efece 100755 (executable)
@@ -3,7 +3,6 @@
 test_description='applying patch with mode bits'
 
 . ./test-lib.sh
-. "$TEST_DIRECTORY"/lib-prereq-FILEMODE.sh
 
 test_expect_success setup '
        echo original >file &&
index fb000411395d50639334c3c0395b2b975aa4bc12..2a6278bb333d2843ee617ff69227feae115aefea 100755 (executable)
@@ -310,4 +310,19 @@ EOF
        test_cmp expected actual
 '
 
+test_expect_success 'log decoration properly follows tag chain' '
+       git tag -a tag1 -m tag1 &&
+       git tag -a tag2 -m tag2 tag1 &&
+       git tag -d tag1 &&
+       git commit --amend -m shorter &&
+       git log --no-walk --tags --pretty="%H %d" --decorate=full >actual &&
+       cat <<EOF >expected &&
+6a908c10688b2503073c39c9ba26322c73902bb5  (tag: refs/tags/tag2)
+9f716384d92283fb915a4eee5073f030638e05f9  (tag: refs/tags/message-one)
+b87e4cccdb77336ea79d89224737be7ea8e95367  (tag: refs/tags/message-two)
+EOF
+       sort actual >actual1 &&
+       test_cmp expected actual1
+'
+
 test_done
index c2023b1a3d78a7f100207a76678957d2659f8a8d..05f011d38eeaf16e2be8caace5bca697b7a9daf3 100755 (executable)
@@ -3,7 +3,7 @@
 # Copyright (C) 2005 Rene Scharfe
 #
 
-test_description='git tar-tree and git get-tar-commit-id test
+test_description='git archive and git get-tar-commit-id test
 
 This test covers the topics of file contents, commit date handling and
 commit id embedding:
@@ -13,11 +13,11 @@ commit id embedding:
   binary file (/bin/sh).  Only paths shorter than 99 characters are
   used.
 
-  git tar-tree applies the commit date to every file in the archive it
+  git archive applies the commit date to every file in the archive it
   creates.  The test sets the commit date to a specific value and checks
   if the tar archive contains that value.
 
-  When giving git tar-tree a commit id (in contrast to a tree id) it
+  When giving git archive a commit id (in contrast to a tree id) it
   embeds this commit id into the tar archive as a comment.  The test
   checks the ability of git get-tar-commit-id to figure it out from the
   tar file.
@@ -25,8 +25,6 @@ commit id embedding:
 '
 
 . ./test-lib.sh
-GZIP=${GZIP:-gzip}
-GUNZIP=${GUNZIP:-gzip -d}
 
 SUBSTFORMAT=%H%n
 
@@ -39,6 +37,8 @@ test_lazy_prereq TAR_NEEDS_PAX_FALLBACK '
        )
 '
 
+test_lazy_prereq GZIP 'gzip --version'
+
 get_pax_header() {
        file=$1
        header=$2=
@@ -196,16 +196,6 @@ test_expect_success \
     'git get-tar-commit-id <b.tar >b.commitid &&
      test_cmp .git/$(git symbolic-ref HEAD) b.commitid'
 
-test_expect_success 'git tar-tree' '
-       git tar-tree HEAD >tar-tree.tar &&
-       test_cmp b.tar tar-tree.tar
-'
-
-test_expect_success 'git tar-tree with prefix' '
-       git tar-tree HEAD prefix >tar-tree_with_prefix.tar &&
-       test_cmp with_prefix.tar tar-tree_with_prefix.tar
-'
-
 test_expect_success 'git archive with --output, override inferred format' '
        git archive --format=tar --output=d4.zip HEAD &&
        test_cmp b.tar d4.zip
@@ -275,12 +265,6 @@ test_expect_success 'only enabled filters are available remotely' '
        test_cmp remote.bar config.bar
 '
 
-if $GZIP --version >/dev/null 2>&1; then
-       test_set_prereq GZIP
-else
-       say "Skipping some tar.gz tests because gzip not found"
-fi
-
 test_expect_success GZIP 'git archive --format=tgz' '
        git archive --format=tgz HEAD >j.tgz
 '
@@ -300,14 +284,8 @@ test_expect_success GZIP 'infer tgz from .tar.gz filename' '
        test_cmp j.tgz j3.tar.gz
 '
 
-if $GUNZIP --version >/dev/null 2>&1; then
-       test_set_prereq GUNZIP
-else
-       say "Skipping some tar.gz tests because gunzip was not found"
-fi
-
-test_expect_success GZIP,GUNZIP 'extract tgz file' '
-       $GUNZIP -c <j.tgz >j.tar &&
+test_expect_success GZIP 'extract tgz file' '
+       gzip -d -c <j.tgz >j.tar &&
        test_cmp b.tar j.tar
 '
 
index f47d8717fdd93cf8ebf356c2675511567782335e..51dedab29b682749990fb29a3f11134a2629f941 100755 (executable)
@@ -87,14 +87,4 @@ test_expect_success 'export-subst' '
        test_cmp substfile2 archive/substfile2
 '
 
-test_expect_success 'git tar-tree vs. git archive with worktree attributes' '
-       git tar-tree HEAD >tar-tree.tar &&
-       test_cmp worktree.tar tar-tree.tar
-'
-
-test_expect_success 'git tar-tree vs. git archive with worktree attrs, bare' '
-       (cd bare && git tar-tree HEAD) >bare-tar-tree.tar &&
-       test_cmp bare-worktree.tar bare-tar-tree.tar
-'
-
 test_done
index fe82025d4a0dbae0cee3d0c3943617ede5b5c7a5..4bbb718751738c3e913c25160d0fff52d8350bba 100755 (executable)
@@ -174,11 +174,11 @@ test_expect_success \
 test_expect_success \
     '[index v1] 5) pack-objects happily reuses corrupted data' \
     'pack4=$(git pack-objects test-4 <obj-list) &&
-     test -f "test-4-${pack1}.pack"'
+     test -f "test-4-${pack4}.pack"'
 
 test_expect_success \
     '[index v1] 6) newly created pack is BAD !' \
-    'test_must_fail git verify-pack -v "test-4-${pack1}.pack"'
+    'test_must_fail git verify-pack -v "test-4-${pack4}.pack"'
 
 test_expect_success \
     '[index v2] 1) stream pack to repository' \
index e4bb3a14570780b41ce4ebea9e47870d4cbcb127..66c9a41739e5d75633a239c55fd516341f8a98e3 100755 (executable)
@@ -221,4 +221,14 @@ EOF
        test_cmp expected actual
 '
 
+test_expect_success 'prune .git/shallow' '
+       SHA1=`echo hi|git commit-tree HEAD^{tree}` &&
+       echo $SHA1 >.git/shallow &&
+       git prune --dry-run >out &&
+       grep $SHA1 .git/shallow &&
+       grep $SHA1 out &&
+       git prune &&
+       ! test -f .git/shallow
+'
+
 test_done
index d87ddf73b7127bc624ca739653db9389831b817e..5b2b1c2c130387332778f6e49043b739dd2d7d21 100755 (executable)
@@ -531,5 +531,92 @@ test_expect_success 'shallow fetch with tags does not break the repository' '
                git fsck
        )
 '
+check_prot_path () {
+       cat >expected <<-EOF &&
+       Diag: url=$1
+       Diag: protocol=$2
+       Diag: path=$3
+       EOF
+       git fetch-pack --diag-url "$1" | grep -v hostandport= >actual &&
+       test_cmp expected actual
+}
+
+check_prot_host_path () {
+       cat >expected <<-EOF &&
+       Diag: url=$1
+       Diag: protocol=$2
+       Diag: hostandport=$3
+       Diag: path=$4
+       EOF
+       git fetch-pack --diag-url "$1" >actual &&
+       test_cmp expected actual
+}
+
+for r in repo re:po re/po
+do
+       # git or ssh with scheme
+       for p in "ssh+git" "git+ssh" git ssh
+       do
+               for h in host host:12 [::1] [::1]:23
+               do
+                       case "$p" in
+                       *ssh*)
+                               pp=ssh
+                               ;;
+                       *)
+                               pp=$p
+                       ;;
+                       esac
+                       test_expect_success "fetch-pack --diag-url $p://$h/$r" '
+                               check_prot_host_path $p://$h/$r $pp "$h" "/$r"
+                       '
+                       # "/~" -> "~" conversion
+                       test_expect_success "fetch-pack --diag-url $p://$h/~$r" '
+                               check_prot_host_path $p://$h/~$r $pp "$h" "~$r"
+                       '
+               done
+       done
+       # file with scheme
+       for p in file
+       do
+               test_expect_success "fetch-pack --diag-url $p://$h/$r" '
+                       check_prot_path $p://$h/$r $p "/$r"
+               '
+               # No "/~" -> "~" conversion for file
+               test_expect_success "fetch-pack --diag-url $p://$h/~$r" '
+                       check_prot_path $p://$h/~$r $p "/~$r"
+               '
+       done
+       # file without scheme
+       for h in nohost nohost:12 [::1] [::1]:23 [ [:aa
+       do
+               test_expect_success "fetch-pack --diag-url ./$h:$r" '
+                       check_prot_path ./$h:$r $p "./$h:$r"
+               '
+               # No "/~" -> "~" conversion for file
+               test_expect_success "fetch-pack --diag-url ./$p:$h/~$r" '
+               check_prot_path ./$p:$h/~$r $p "./$p:$h/~$r"
+               '
+       done
+       #ssh without scheme
+       p=ssh
+       for h in host [::1]
+       do
+               test_expect_success "fetch-pack --diag-url $h:$r" '
+                       check_prot_path $h:$r $p "$r"
+               '
+               # Do "/~" -> "~" conversion
+               test_expect_success "fetch-pack --diag-url $h:/~$r" '
+                       check_prot_host_path $h:/~$r $p "$h" "~$r"
+               '
+       done
+done
+
+test_expect_success MINGW 'fetch-pack --diag-url file://c:/repo' '
+       check_prot_path file://c:/repo file c:/repo
+'
+test_expect_success MINGW 'fetch-pack --diag-url c:repo' '
+       check_prot_path c:repo file c:repo
+'
 
 test_done
index 1f0f8e6827773b1bb9a419a822de2d1ebde8e37a..ab28594c62dd7cddc69d3afdb075b48c34091c45 100755 (executable)
@@ -88,7 +88,7 @@ test_expect_success 'fetch --prune on its own works as expected' '
        cd "$D" &&
        git clone . prune &&
        cd prune &&
-       git fetch origin refs/heads/master:refs/remotes/origin/extrabranch &&
+       git update-ref refs/remotes/origin/extrabranch master &&
 
        git fetch --prune origin &&
        test_must_fail git rev-parse origin/extrabranch
@@ -98,7 +98,7 @@ test_expect_success 'fetch --prune with a branch name keeps branches' '
        cd "$D" &&
        git clone . prune-branch &&
        cd prune-branch &&
-       git fetch origin refs/heads/master:refs/remotes/origin/extrabranch &&
+       git update-ref refs/remotes/origin/extrabranch master &&
 
        git fetch --prune origin master &&
        git rev-parse origin/extrabranch
@@ -113,25 +113,45 @@ test_expect_success 'fetch --prune with a namespace keeps other namespaces' '
        git rev-parse origin/master
 '
 
-test_expect_success 'fetch --prune --tags does not delete the remote-tracking branches' '
+test_expect_success 'fetch --prune --tags prunes branches but not tags' '
        cd "$D" &&
        git clone . prune-tags &&
        cd prune-tags &&
-       git fetch origin refs/heads/master:refs/tags/sometag &&
+       git tag sometag master &&
+       # Create what looks like a remote-tracking branch from an earlier
+       # fetch that has since been deleted from the remote:
+       git update-ref refs/remotes/origin/fake-remote master &&
 
        git fetch --prune --tags origin &&
        git rev-parse origin/master &&
-       test_must_fail git rev-parse somebranch
+       test_must_fail git rev-parse origin/fake-remote &&
+       git rev-parse sometag
 '
 
-test_expect_success 'fetch --prune --tags with branch does not delete other remote-tracking branches' '
+test_expect_success 'fetch --prune --tags with branch does not prune other things' '
        cd "$D" &&
        git clone . prune-tags-branch &&
        cd prune-tags-branch &&
-       git fetch origin refs/heads/master:refs/remotes/origin/extrabranch &&
+       git tag sometag master &&
+       git update-ref refs/remotes/origin/extrabranch master &&
 
        git fetch --prune --tags origin master &&
-       git rev-parse origin/extrabranch
+       git rev-parse origin/extrabranch &&
+       git rev-parse sometag
+'
+
+test_expect_success 'fetch --prune --tags with refspec prunes based on refspec' '
+       cd "$D" &&
+       git clone . prune-tags-refspec &&
+       cd prune-tags-refspec &&
+       git tag sometag master &&
+       git update-ref refs/remotes/origin/foo/otherbranch master &&
+       git update-ref refs/remotes/origin/extrabranch master &&
+
+       git fetch --prune --tags origin refs/heads/foo/*:refs/remotes/origin/foo/* &&
+       test_must_fail git rev-parse refs/remotes/origin/foo/otherbranch &&
+       git rev-parse origin/extrabranch &&
+       git rev-parse sometag
 '
 
 test_expect_success 'fetch tags when there is no tags' '
@@ -594,4 +614,41 @@ test_expect_success 'all boundary commits are excluded' '
        test_bundle_object_count .git/objects/pack/pack-${pack##pack    }.pack 3
 '
 
+test_expect_success 'fetch --prune prints the remotes url' '
+       git branch goodbye &&
+       git clone . only-prunes &&
+       git branch -D goodbye &&
+       (
+               cd only-prunes &&
+               git fetch --prune origin 2>&1 | head -n1 >../actual
+       ) &&
+       echo "From ${D}/." >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success 'branchname D/F conflict resolved by --prune' '
+       git branch dir/file &&
+       git clone . prune-df-conflict &&
+       git branch -D dir/file &&
+       git branch dir &&
+       (
+               cd prune-df-conflict &&
+               git fetch --prune &&
+               git rev-parse origin/dir >../actual
+       ) &&
+       git rev-parse dir >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success 'fetching a one-level ref works' '
+       test_commit extra &&
+       git reset --hard HEAD^ &&
+       git update-ref refs/foo extra &&
+       git init one-level &&
+       (
+               cd one-level &&
+               git fetch .. HEAD refs/foo
+       )
+'
+
 test_done
index 1669cc4af0e8b3d179bcff9dd850f2f363278005..0f70f66c705dd6af9f9861ae2e33223d1b944d3a 100644 (file)
@@ -1,4 +1,5 @@
 # br-unconfig --tags ../.git
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b               ../
 6c9dec2b923228c9ff994c6cfe4ae16c12408dc5       not-for-merge   tag 'tag-master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689       not-for-merge   tag 'tag-one' of ../
 22feea448b023a2d864ef94b013735af34d238ba       not-for-merge   tag 'tag-one-tree' of ../
index 8a7493537b99ac9592fd548300d5332e4163ab77..ab473a6e1f76ddf0f10a78358097d5e3bf16bc02 100644 (file)
@@ -1,4 +1,5 @@
 # master --tags ../.git
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b               ../
 6c9dec2b923228c9ff994c6cfe4ae16c12408dc5       not-for-merge   tag 'tag-master' of ../
 8e32a6d901327a23ef831511badce7bf3bf46689       not-for-merge   tag 'tag-one' of ../
 22feea448b023a2d864ef94b013735af34d238ba       not-for-merge   tag 'tag-one-tree' of ../
index 99c32d75391bb02fae779ad831879903abca5d25..926e7f6b979d4d1c69ecc9d280cd6de365627012 100755 (executable)
@@ -1126,6 +1126,81 @@ test_expect_success 'fetch follows tags by default' '
        test_cmp expect actual
 '
 
+test_expect_success 'pushing a specific ref applies remote.$name.push as refmap' '
+       mk_test testrepo heads/master &&
+       rm -fr src dst &&
+       git init src &&
+       git init --bare dst &&
+       (
+               cd src &&
+               git pull ../testrepo master &&
+               git branch next &&
+               git config remote.dst.url ../dst &&
+               git config remote.dst.push "+refs/heads/*:refs/remotes/src/*" &&
+               git push dst master &&
+               git show-ref refs/heads/master |
+               sed -e "s|refs/heads/|refs/remotes/src/|" >../dst/expect
+       ) &&
+       (
+               cd dst &&
+               test_must_fail git show-ref refs/heads/next &&
+               test_must_fail git show-ref refs/heads/master &&
+               git show-ref refs/remotes/src/master >actual
+       ) &&
+       test_cmp dst/expect dst/actual
+'
+
+test_expect_success 'with no remote.$name.push, it is not used as refmap' '
+       mk_test testrepo heads/master &&
+       rm -fr src dst &&
+       git init src &&
+       git init --bare dst &&
+       (
+               cd src &&
+               git pull ../testrepo master &&
+               git branch next &&
+               git config remote.dst.url ../dst &&
+               git config push.default matching &&
+               git push dst master &&
+               git show-ref refs/heads/master >../dst/expect
+       ) &&
+       (
+               cd dst &&
+               test_must_fail git show-ref refs/heads/next &&
+               git show-ref refs/heads/master >actual
+       ) &&
+       test_cmp dst/expect dst/actual
+'
+
+test_expect_success 'with no remote.$name.push, upstream mapping is used' '
+       mk_test testrepo heads/master &&
+       rm -fr src dst &&
+       git init src &&
+       git init --bare dst &&
+       (
+               cd src &&
+               git pull ../testrepo master &&
+               git branch next &&
+               git config remote.dst.url ../dst &&
+               git config remote.dst.fetch "+refs/heads/*:refs/remotes/dst/*" &&
+               git config push.default upstream &&
+
+               git config branch.master.merge refs/heads/trunk &&
+               git config branch.master.remote dst &&
+
+               git push dst master &&
+               git show-ref refs/heads/master |
+               sed -e "s|refs/heads/master|refs/heads/trunk|" >../dst/expect
+       ) &&
+       (
+               cd dst &&
+               test_must_fail git show-ref refs/heads/master &&
+               test_must_fail git show-ref refs/heads/next &&
+               git show-ref refs/heads/trunk >actual
+       ) &&
+       test_cmp dst/expect dst/actual
+'
+
 test_expect_success 'push does not follow tags by default' '
        mk_test testrepo heads/master &&
        rm -fr src dst &&
index 4fbf7a120f3fd4133e09cb7cbe5c8e4597bfa27c..45815f737850b61684f648af9d984ac16ea0944e 100755 (executable)
@@ -8,7 +8,8 @@ setup_clone () {
        git clone --mirror . $1 &&
        git remote add remote_$1 $1 &&
        (cd $1 &&
-       git tag tag_$1)
+       git tag tag_$1 &&
+       git branch branch_$1)
 }
 
 test_expect_success setup '
@@ -21,21 +22,33 @@ test_expect_success setup '
 
 test_expect_success "fetch with tagopt=--no-tags does not get tag" '
        git fetch remote_one &&
-       test_must_fail git show-ref tag_one
+       test_must_fail git show-ref tag_one &&
+       git show-ref remote_one/branch_one
        '
 
 test_expect_success "fetch --tags with tagopt=--no-tags gets tag" '
+       (
+               cd one &&
+               git branch second_branch_one
+       ) &&
        git fetch --tags remote_one &&
-       git show-ref tag_one
+       git show-ref tag_one &&
+       git show-ref remote_one/second_branch_one
        '
 
 test_expect_success "fetch --no-tags with tagopt=--tags does not get tag" '
        git fetch --no-tags remote_two &&
-       test_must_fail git show-ref tag_two
+       test_must_fail git show-ref tag_two &&
+       git show-ref remote_two/branch_two
        '
 
 test_expect_success "fetch with tagopt=--tags gets tag" '
+       (
+               cd two &&
+               git branch second_branch_two
+       ) &&
        git fetch remote_two &&
-       git show-ref tag_two
+       git show-ref tag_two &&
+       git show-ref remote_two/second_branch_two
        '
 test_done
index 8c16e045a0c585957a0be4e31b5628d1d5b6cb23..445bb5fe26a4d8edc29223b1216625d484cd01bf 100755 (executable)
@@ -12,6 +12,7 @@ test_expect_success setup '
        (
                cd work &&
                git init &&
+               git config push.default matching &&
                mkdir -p gar/bage &&
                (
                        cd gar/bage &&
diff --git a/t/t5536-fetch-conflicts.sh b/t/t5536-fetch-conflicts.sh
new file mode 100755 (executable)
index 0000000..6c5d3a4
--- /dev/null
@@ -0,0 +1,100 @@
+#!/bin/sh
+
+test_description='fetch handles conflicting refspecs correctly'
+
+. ./test-lib.sh
+
+D=$(pwd)
+
+setup_repository () {
+       git init "$1" && (
+               cd "$1" &&
+               git config remote.origin.url "$D" &&
+               shift &&
+               for refspec in "$@"
+               do
+                       git config --add remote.origin.fetch "$refspec"
+               done
+       )
+}
+
+verify_stderr () {
+       cat >expected &&
+       # We're not interested in the error
+       # "fatal: The remote end hung up unexpectedly":
+       grep -E '^(fatal|warning):' <error | grep -v 'hung up' >actual | sort &&
+       test_cmp expected actual
+}
+
+test_expect_success 'setup' '
+       git commit --allow-empty -m "Initial" &&
+       git branch branch1 &&
+       git tag tag1 &&
+       git commit --allow-empty -m "First" &&
+       git branch branch2 &&
+       git tag tag2
+'
+
+test_expect_success 'fetch with no conflict' '
+       setup_repository ok "+refs/heads/*:refs/remotes/origin/*" && (
+               cd ok &&
+               git fetch origin
+       )
+'
+
+test_expect_success 'fetch conflict: config vs. config' '
+       setup_repository ccc \
+               "+refs/heads/branch1:refs/remotes/origin/branch1" \
+               "+refs/heads/branch2:refs/remotes/origin/branch1" && (
+               cd ccc &&
+               test_must_fail git fetch origin 2>error &&
+               verify_stderr <<-\EOF
+               fatal: Cannot fetch both refs/heads/branch1 and refs/heads/branch2 to refs/remotes/origin/branch1
+               EOF
+       )
+'
+
+test_expect_success 'fetch duplicate: config vs. config' '
+       setup_repository dcc \
+               "+refs/heads/*:refs/remotes/origin/*" \
+               "+refs/heads/branch1:refs/remotes/origin/branch1" && (
+               cd dcc &&
+               git fetch origin
+       )
+'
+
+test_expect_success 'fetch conflict: arg overrides config' '
+       setup_repository aoc \
+               "+refs/heads/*:refs/remotes/origin/*" && (
+               cd aoc &&
+               git fetch origin refs/heads/branch2:refs/remotes/origin/branch1
+       )
+'
+
+test_expect_success 'fetch conflict: arg vs. arg' '
+       setup_repository caa && (
+               cd caa &&
+               test_must_fail git fetch origin \
+                       refs/heads/*:refs/remotes/origin/* \
+                       refs/heads/branch2:refs/remotes/origin/branch1 2>error &&
+               verify_stderr <<-\EOF
+               fatal: Cannot fetch both refs/heads/branch1 and refs/heads/branch2 to refs/remotes/origin/branch1
+               EOF
+       )
+'
+
+test_expect_success 'fetch conflict: criss-cross args' '
+       setup_repository xaa \
+               "+refs/heads/*:refs/remotes/origin/*" && (
+               cd xaa &&
+               git fetch origin \
+                       refs/heads/branch1:refs/remotes/origin/branch2 \
+                       refs/heads/branch2:refs/remotes/origin/branch1 2>error &&
+               verify_stderr <<-\EOF
+               warning: refs/remotes/origin/branch1 usually tracks refs/heads/branch1, not refs/heads/branch2
+               warning: refs/remotes/origin/branch2 usually tracks refs/heads/branch2, not refs/heads/branch1
+               EOF
+       )
+'
+
+test_done
diff --git a/t/t5537-fetch-shallow.sh b/t/t5537-fetch-shallow.sh
new file mode 100755 (executable)
index 0000000..b0fa738
--- /dev/null
@@ -0,0 +1,204 @@
+#!/bin/sh
+
+test_description='fetch/clone from a shallow clone'
+
+. ./test-lib.sh
+
+commit() {
+       echo "$1" >tracked &&
+       git add tracked &&
+       git commit -m "$1"
+}
+
+test_expect_success 'setup' '
+       commit 1 &&
+       commit 2 &&
+       commit 3 &&
+       commit 4 &&
+       git config --global transfer.fsckObjects true
+'
+
+test_expect_success 'setup shallow clone' '
+       git clone --no-local --depth=2 .git shallow &&
+       git --git-dir=shallow/.git log --format=%s >actual &&
+       cat <<EOF >expect &&
+4
+3
+EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'clone from shallow clone' '
+       git clone --no-local shallow shallow2 &&
+       (
+       cd shallow2 &&
+       git fsck &&
+       git log --format=%s >actual &&
+       cat <<EOF >expect &&
+4
+3
+EOF
+       test_cmp expect actual
+       )
+'
+
+test_expect_success 'fetch from shallow clone' '
+       (
+       cd shallow &&
+       commit 5
+       ) &&
+       (
+       cd shallow2 &&
+       git fetch &&
+       git fsck &&
+       git log --format=%s origin/master >actual &&
+       cat <<EOF >expect &&
+5
+4
+3
+EOF
+       test_cmp expect actual
+       )
+'
+
+test_expect_success 'fetch --depth from shallow clone' '
+       (
+       cd shallow &&
+       commit 6
+       ) &&
+       (
+       cd shallow2 &&
+       git fetch --depth=2 &&
+       git fsck &&
+       git log --format=%s origin/master >actual &&
+       cat <<EOF >expect &&
+6
+5
+EOF
+       test_cmp expect actual
+       )
+'
+
+test_expect_success 'fetch --unshallow from shallow clone' '
+       (
+       cd shallow2 &&
+       git fetch --unshallow &&
+       git fsck &&
+       git log --format=%s origin/master >actual &&
+       cat <<EOF >expect &&
+6
+5
+4
+3
+EOF
+       test_cmp expect actual
+       )
+'
+
+test_expect_success 'fetch something upstream has but hidden by clients shallow boundaries' '
+       # the blob "1" is available in .git but hidden by the
+       # shallow2/.git/shallow and it should be resent
+       ! git --git-dir=shallow2/.git cat-file blob `echo 1|git hash-object --stdin` >/dev/null &&
+       echo 1 >1.t &&
+       git add 1.t &&
+       git commit -m add-1-back &&
+       (
+       cd shallow2 &&
+       git fetch ../.git +refs/heads/master:refs/remotes/top/master &&
+       git fsck &&
+       git log --format=%s top/master >actual &&
+       cat <<EOF >expect &&
+add-1-back
+4
+3
+EOF
+       test_cmp expect actual
+       ) &&
+       git --git-dir=shallow2/.git cat-file blob `echo 1|git hash-object --stdin` >/dev/null
+
+'
+
+test_expect_success 'fetch that requires changes in .git/shallow is filtered' '
+       (
+       cd shallow &&
+       git checkout --orphan no-shallow &&
+       commit no-shallow
+       ) &&
+       git init notshallow &&
+       (
+       cd notshallow &&
+       git fetch ../shallow/.git refs/heads/*:refs/remotes/shallow/*&&
+       git for-each-ref --format="%(refname)" >actual.refs &&
+       cat <<EOF >expect.refs &&
+refs/remotes/shallow/no-shallow
+EOF
+       test_cmp expect.refs actual.refs &&
+       git log --format=%s shallow/no-shallow >actual &&
+       cat <<EOF >expect &&
+no-shallow
+EOF
+       test_cmp expect actual
+       )
+'
+
+test_expect_success 'fetch --update-shallow' '
+       (
+       cd shallow &&
+       git checkout master &&
+       commit 7 &&
+       git tag -m foo heavy-tag HEAD^ &&
+       git tag light-tag HEAD^:tracked
+       ) &&
+       (
+       cd notshallow &&
+       git fetch --update-shallow ../shallow/.git refs/heads/*:refs/remotes/shallow/* &&
+       git fsck &&
+       git for-each-ref --sort=refname --format="%(refname)" >actual.refs &&
+       cat <<EOF >expect.refs &&
+refs/remotes/shallow/master
+refs/remotes/shallow/no-shallow
+refs/tags/heavy-tag
+refs/tags/light-tag
+EOF
+       test_cmp expect.refs actual.refs &&
+       git log --format=%s shallow/master >actual &&
+       cat <<EOF >expect &&
+7
+6
+5
+4
+3
+EOF
+       test_cmp expect actual
+       )
+'
+
+if test -n "$NO_CURL" -o -z "$GIT_TEST_HTTPD"; then
+       say 'skipping remaining tests, git built without http support'
+       test_done
+fi
+
+LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5537'}
+. "$TEST_DIRECTORY"/lib-httpd.sh
+start_httpd
+
+test_expect_success 'clone http repository' '
+       git clone --bare --no-local shallow "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+       git clone $HTTPD_URL/smart/repo.git clone &&
+       (
+       cd clone &&
+       git fsck &&
+       git log --format=%s origin/master >actual &&
+       cat <<EOF >expect &&
+7
+6
+5
+4
+3
+EOF
+       test_cmp expect actual
+       )
+'
+
+stop_httpd
+test_done
diff --git a/t/t5538-push-shallow.sh b/t/t5538-push-shallow.sh
new file mode 100755 (executable)
index 0000000..0a6e40f
--- /dev/null
@@ -0,0 +1,183 @@
+#!/bin/sh
+
+test_description='push from/to a shallow clone'
+
+. ./test-lib.sh
+
+commit() {
+       echo "$1" >tracked &&
+       git add tracked &&
+       git commit -m "$1"
+}
+
+test_expect_success 'setup' '
+       git config --global transfer.fsckObjects true &&
+       commit 1 &&
+       commit 2 &&
+       commit 3 &&
+       commit 4 &&
+       git clone . full &&
+       (
+       git init full-abc &&
+       cd full-abc &&
+       commit a &&
+       commit b &&
+       commit c
+       ) &&
+       git clone --no-local --depth=2 .git shallow &&
+       git --git-dir=shallow/.git log --format=%s >actual &&
+       cat <<EOF >expect &&
+4
+3
+EOF
+       test_cmp expect actual &&
+       git clone --no-local --depth=2 full-abc/.git shallow2 &&
+       git --git-dir=shallow2/.git log --format=%s >actual &&
+       cat <<EOF >expect &&
+c
+b
+EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'push from shallow clone' '
+       (
+       cd shallow &&
+       commit 5 &&
+       git push ../.git +master:refs/remotes/shallow/master
+       ) &&
+       git log --format=%s shallow/master >actual &&
+       git fsck &&
+       cat <<EOF >expect &&
+5
+4
+3
+2
+1
+EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'push from shallow clone, with grafted roots' '
+       (
+       cd shallow2 &&
+       test_must_fail git push ../.git +master:refs/remotes/shallow2/master 2>err &&
+       grep "shallow2/master.*shallow update not allowed" err
+       ) &&
+       test_must_fail git rev-parse shallow2/master &&
+       git fsck
+'
+
+test_expect_success 'add new shallow root with receive.updateshallow on' '
+       test_config receive.shallowupdate true &&
+       (
+       cd shallow2 &&
+       git push ../.git +master:refs/remotes/shallow2/master
+       ) &&
+       git log --format=%s shallow2/master >actual &&
+       git fsck &&
+       cat <<EOF >expect &&
+c
+b
+EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'push from shallow to shallow' '
+       (
+       cd shallow &&
+       git --git-dir=../shallow2/.git config receive.shallowupdate true &&
+       git push ../shallow2/.git +master:refs/remotes/shallow/master &&
+       git --git-dir=../shallow2/.git config receive.shallowupdate false
+       ) &&
+       (
+       cd shallow2 &&
+       git log --format=%s shallow/master >actual &&
+       git fsck &&
+       cat <<EOF >expect &&
+5
+4
+3
+EOF
+       test_cmp expect actual
+       )
+'
+
+test_expect_success 'push from full to shallow' '
+       ! git --git-dir=shallow2/.git cat-file blob `echo 1|git hash-object --stdin` &&
+       commit 1 &&
+       git push shallow2/.git +master:refs/remotes/top/master &&
+       (
+       cd shallow2 &&
+       git log --format=%s top/master >actual &&
+       git fsck &&
+       cat <<EOF >expect &&
+1
+4
+3
+EOF
+       test_cmp expect actual &&
+       git cat-file blob `echo 1|git hash-object --stdin` >/dev/null
+       )
+'
+
+if test -n "$NO_CURL" -o -z "$GIT_TEST_HTTPD"; then
+       say 'skipping remaining tests, git built without http support'
+       test_done
+fi
+
+LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5537'}
+. "$TEST_DIRECTORY"/lib-httpd.sh
+start_httpd
+
+test_expect_success 'push to shallow repo via http' '
+       git clone --bare --no-local shallow "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+       (
+       cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+       git config http.receivepack true
+       ) &&
+       (
+       cd full &&
+       commit 9 &&
+       git push $HTTPD_URL/smart/repo.git +master:refs/remotes/top/master
+       ) &&
+       (
+       cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+       git fsck &&
+       git log --format=%s top/master >actual &&
+       cat <<EOF >expect &&
+9
+4
+3
+EOF
+       test_cmp expect actual
+       )
+'
+
+test_expect_success 'push from shallow repo via http' '
+       mv "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" shallow-upstream.git &&
+       git clone --bare --no-local full "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+       (
+       cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+       git config http.receivepack true
+       ) &&
+       commit 10 &&
+       git push $HTTPD_URL/smart/repo.git +master:refs/remotes/top/master &&
+       (
+       cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+       git fsck &&
+       git log --format=%s top/master >actual &&
+       cat <<EOF >expect &&
+10
+1
+4
+3
+2
+1
+EOF
+       test_cmp expect actual
+       )
+'
+
+stop_httpd
+test_done
index 01d0d95b4d6476f691f650cc413fa7156caee88b..5b0198cbc88ad54ea4e6f102429697c2da2ed2ba 100755 (executable)
@@ -154,7 +154,7 @@ test_http_push_nonff "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git \
 
 test_expect_success 'push to password-protected repository (user in URL)' '
        test_commit pw-user &&
-       set_askpass user@host &&
+       set_askpass user@host pass@host &&
        git push "$HTTPD_URL_USER/auth/dumb/test_repo.git" HEAD &&
        git rev-parse --verify HEAD >expect &&
        git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/test_repo.git" \
@@ -168,7 +168,7 @@ test_expect_failure 'user was prompted only once for password' '
 
 test_expect_failure 'push to password-protected repository (no user in URL)' '
        test_commit pw-nouser &&
-       set_askpass user@host &&
+       set_askpass user@host pass@host &&
        git push "$HTTPD_URL/auth/dumb/test_repo.git" HEAD &&
        expect_askpass both user@host
        git rev-parse --verify HEAD >expect &&
index 470ac54295e8640feb4d23a81c5dc8ce362857d0..bfd241ea8ae4e51f69194f4a08685e9cd9d78b61 100755 (executable)
@@ -274,7 +274,7 @@ test_expect_success 'push over smart http with auth' '
        cd "$ROOT_PATH/test_repo_clone" &&
        echo push-auth-test >expect &&
        test_commit push-auth-test &&
-       set_askpass user@host &&
+       set_askpass user@host pass@host &&
        git push "$HTTPD_URL"/auth/smart/test_repo.git &&
        git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git" \
                log -1 --format=%s >actual &&
@@ -286,7 +286,7 @@ test_expect_success 'push to auth-only-for-push repo' '
        cd "$ROOT_PATH/test_repo_clone" &&
        echo push-half-auth >expect &&
        test_commit push-half-auth &&
-       set_askpass user@host &&
+       set_askpass user@host pass@host &&
        git push "$HTTPD_URL"/auth-push/smart/test_repo.git &&
        git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git" \
                log -1 --format=%s >actual &&
@@ -316,7 +316,7 @@ test_expect_success 'push into half-auth-complete requires password' '
        cd "$ROOT_PATH/half-auth-clone" &&
        echo two >expect &&
        test_commit two &&
-       set_askpass user@host &&
+       set_askpass user@host pass@host &&
        git push "$HTTPD_URL/half-auth-complete/smart/half-auth.git" &&
        git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/half-auth.git" \
                log -1 --format=%s >actual &&
index f7d0f146f0f69775dd3fa3ea06895e2bb1a74d55..83926247142458323298c844ec99a74c369107e7 100755 (executable)
@@ -62,13 +62,13 @@ test_expect_success 'http auth can use user/pass in URL' '
 '
 
 test_expect_success 'http auth can use just user in URL' '
-       set_askpass user@host &&
+       set_askpass wrong pass@host &&
        git clone "$HTTPD_URL_USER/auth/dumb/repo.git" clone-auth-pass &&
        expect_askpass pass user@host
 '
 
 test_expect_success 'http auth can request both user and pass' '
-       set_askpass user@host &&
+       set_askpass user@host pass@host &&
        git clone "$HTTPD_URL/auth/dumb/repo.git" clone-auth-both &&
        expect_askpass both user@host
 '
@@ -77,7 +77,7 @@ test_expect_success 'http auth respects credential helper config' '
        test_config_global credential.helper "!f() {
                cat >/dev/null
                echo username=user@host
-               echo password=user@host
+               echo password=pass@host
        }; f" &&
        set_askpass wrong &&
        git clone "$HTTPD_URL/auth/dumb/repo.git" clone-auth-helper &&
@@ -86,14 +86,14 @@ test_expect_success 'http auth respects credential helper config' '
 
 test_expect_success 'http auth can get username from config' '
        test_config_global "credential.$HTTPD_URL.username" user@host &&
-       set_askpass user@host &&
+       set_askpass wrong pass@host &&
        git clone "$HTTPD_URL/auth/dumb/repo.git" clone-auth-user &&
        expect_askpass pass user@host
 '
 
 test_expect_success 'configured username does not override URL' '
        test_config_global "credential.$HTTPD_URL.username" wrong &&
-       set_askpass user@host &&
+       set_askpass wrong pass@host &&
        git clone "$HTTPD_URL_USER/auth/dumb/repo.git" clone-auth-user2 &&
        expect_askpass pass user@host
 '
index afb439e09c2db4853c6eba88a1e630f319337cf8..a124efe11459905a11d91bfb32f8388bf4876c1d 100755 (executable)
@@ -119,7 +119,7 @@ test_expect_success 'redirects re-root further requests' '
 
 test_expect_success 'clone from password-protected repository' '
        echo two >expect &&
-       set_askpass user@host &&
+       set_askpass user@host pass@host &&
        git clone --bare "$HTTPD_URL/auth/smart/repo.git" smart-auth &&
        expect_askpass both user@host &&
        git --git-dir=smart-auth log -1 --format=%s >actual &&
@@ -137,7 +137,7 @@ test_expect_success 'clone from auth-only-for-push repository' '
 
 test_expect_success 'clone from auth-only-for-objects repository' '
        echo two >expect &&
-       set_askpass user@host &&
+       set_askpass user@host pass@host &&
        git clone --bare "$HTTPD_URL/auth-fetch/smart/repo.git" half-auth &&
        expect_askpass both user@host &&
        git --git-dir=half-auth log -1 --format=%s >actual &&
@@ -151,7 +151,7 @@ test_expect_success 'no-op half-auth fetch does not require a password' '
 '
 
 test_expect_success 'redirects send auth to new location' '
-       set_askpass user@host &&
+       set_askpass user@host pass@host &&
        git -c credential.useHttpPath=true \
          clone $HTTPD_URL/smart-redir-auth/repo.git repo-redir-auth &&
        expect_askpass both user@host auth/smart/repo.git
index 1d1c8755ead4e432744a82f0d7b8ed7dfc8a6510..5e67035be800b5cbec5a99dd9e3e458a343440df 100755 (executable)
@@ -280,25 +280,26 @@ test_expect_success 'clone checking out a tag' '
        test_cmp fetch.expected fetch.actual
 '
 
-test_expect_success 'setup ssh wrapper' '
-       write_script "$TRASH_DIRECTORY/ssh-wrapper" <<-\EOF &&
-       echo >>"$TRASH_DIRECTORY/ssh-output" "ssh: $*" &&
-       # throw away all but the last argument, which should be the
-       # command
-       while test $# -gt 1; do shift; done
-       eval "$1"
-       EOF
-
-       GIT_SSH="$TRASH_DIRECTORY/ssh-wrapper" &&
-       export GIT_SSH &&
-       export TRASH_DIRECTORY
-'
-
-clear_ssh () {
-       >"$TRASH_DIRECTORY/ssh-output"
+setup_ssh_wrapper () {
+       test_expect_success 'setup ssh wrapper' '
+               write_script "$TRASH_DIRECTORY/ssh-wrapper" <<-\EOF &&
+               echo >>"$TRASH_DIRECTORY/ssh-output" "ssh: $*" &&
+               # throw away all but the last argument, which should be the
+               # command
+               while test $# -gt 1; do shift; done
+               eval "$1"
+               EOF
+               GIT_SSH="$TRASH_DIRECTORY/ssh-wrapper" &&
+               export GIT_SSH &&
+               export TRASH_DIRECTORY &&
+               >"$TRASH_DIRECTORY"/ssh-output
+       '
 }
 
 expect_ssh () {
+       test_when_finished '
+               (cd "$TRASH_DIRECTORY" && rm -f ssh-expect && >ssh-output)
+       ' &&
        {
                case "$1" in
                none)
@@ -310,25 +311,114 @@ expect_ssh () {
        (cd "$TRASH_DIRECTORY" && test_cmp ssh-expect ssh-output)
 }
 
-test_expect_success 'cloning myhost:src uses ssh' '
-       clear_ssh &&
+setup_ssh_wrapper
+
+test_expect_success 'clone myhost:src uses ssh' '
        git clone myhost:src ssh-clone &&
        expect_ssh myhost src
 '
 
 test_expect_success NOT_MINGW,NOT_CYGWIN 'clone local path foo:bar' '
-       clear_ssh &&
        cp -R src "foo:bar" &&
-       git clone "./foo:bar" foobar &&
+       git clone "foo:bar" foobar &&
        expect_ssh none
 '
 
 test_expect_success 'bracketed hostnames are still ssh' '
-       clear_ssh &&
        git clone "[myhost:123]:src" ssh-bracket-clone &&
        expect_ssh myhost:123 src
 '
 
+counter=0
+# $1 url
+# $2 none|host
+# $3 path
+test_clone_url () {
+       counter=$(($counter + 1))
+       test_might_fail git clone "$1" tmp$counter &&
+       expect_ssh "$2" "$3"
+}
+
+test_expect_success NOT_MINGW 'clone c:temp is ssl' '
+       test_clone_url c:temp c temp
+'
+
+test_expect_success MINGW 'clone c:temp is dos drive' '
+       test_clone_url c:temp none
+'
+
+#ip v4
+for repo in rep rep/home/project 123
+do
+       test_expect_success "clone host:$repo" '
+               test_clone_url host:$repo host $repo
+       '
+done
+
+#ipv6
+for repo in rep rep/home/project 123
+do
+       test_expect_success "clone [::1]:$repo" '
+               test_clone_url [::1]:$repo ::1 $repo
+       '
+done
+#home directory
+test_expect_success "clone host:/~repo" '
+       test_clone_url host:/~repo host "~repo"
+'
+
+test_expect_success "clone [::1]:/~repo" '
+       test_clone_url [::1]:/~repo ::1 "~repo"
+'
+
+# Corner cases
+for url in foo/bar:baz [foo]bar/baz:qux [foo/bar]:baz
+do
+       test_expect_success "clone $url is not ssh" '
+               test_clone_url $url none
+       '
+done
+
+#with ssh:// scheme
+test_expect_success 'clone ssh://host.xz/home/user/repo' '
+       test_clone_url "ssh://host.xz/home/user/repo" host.xz "/home/user/repo"
+'
+
+# from home directory
+test_expect_success 'clone ssh://host.xz/~repo' '
+       test_clone_url "ssh://host.xz/~repo" host.xz "~repo"
+'
+
+# with port number
+test_expect_success 'clone ssh://host.xz:22/home/user/repo' '
+       test_clone_url "ssh://host.xz:22/home/user/repo" "-p 22 host.xz" "/home/user/repo"
+'
+
+# from home directory with port number
+test_expect_success 'clone ssh://host.xz:22/~repo' '
+       test_clone_url "ssh://host.xz:22/~repo" "-p 22 host.xz" "~repo"
+'
+
+#IPv6
+test_expect_success 'clone ssh://[::1]/home/user/repo' '
+       test_clone_url "ssh://[::1]/home/user/repo" "::1" "/home/user/repo"
+'
+
+#IPv6 from home directory
+test_expect_success 'clone ssh://[::1]/~repo' '
+       test_clone_url "ssh://[::1]/~repo" "::1" "~repo"
+'
+
+#IPv6 with port number
+test_expect_success 'clone ssh://[::1]:22/home/user/repo' '
+       test_clone_url "ssh://[::1]:22/home/user/repo" "-p 22 ::1" "/home/user/repo"
+'
+
+#IPv6 from home directory with port number
+test_expect_success 'clone ssh://[::1]:22/~repo' '
+       test_clone_url "ssh://[::1]:22/~repo" "-p 22 ::1" "~repo"
+'
+
 test_expect_success 'clone from a repository with two identical branches' '
 
        (
@@ -340,4 +430,11 @@ test_expect_success 'clone from a repository with two identical branches' '
 
 '
 
+test_expect_success 'shallow clone locally' '
+       git clone --depth=1 --no-local src ssrrcc &&
+       git clone ssrrcc ddsstt &&
+       test_cmp ssrrcc/.git/shallow ddsstt/.git/shallow &&
+       ( cd ddsstt && git fsck )
+'
+
 test_done
index 15e3d6476c78ba3447504aa133e9e1d7ffbfd87d..3794e4ceafc291be5621411f3cad7bf5ef764942 100755 (executable)
@@ -56,4 +56,21 @@ test_expect_success 'rev-list A..B and rev-list ^A B are the same' '
        test_cmp expect actual
 '
 
+test_expect_success 'propagate uninteresting flag down correctly' '
+       git rev-list --objects ^HEAD^{tree} HEAD^{tree} >actual &&
+       >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success 'symleft flag bit is propagated down from tag' '
+       git log --format="%m %s" --left-right v1.0...master >actual &&
+       cat >expect <<-\EOF &&
+       > two
+       > one
+       < another
+       < that
+       EOF
+       test_cmp expect actual
+'
+
 test_done
index 30a68335b3a69a8189648213606ddeb32b97d99e..39b3238da211957eeec36a9c7b911cd5942de6dd 100755 (executable)
@@ -258,4 +258,43 @@ test_expect_success 'using reflog to find the fork point' '
        test_cmp expect3 actual
 '
 
+test_expect_success 'merge-base --octopus --all for complex tree' '
+       # Best common ancestor for JE, JAA and JDD is JC
+       #             JE
+       #            / |
+       #           /  |
+       #          /   |
+       #  JAA    /    |
+       #   |\   /     |
+       #   | \  | JDD |
+       #   |  \ |/ |  |
+       #   |   JC JD  |
+       #   |    | /|  |
+       #   |    |/ |  |
+       #  JA    |  |  |
+       #   |\  /|  |  |
+       #   X JB |  X  X
+       #   \  \ | /   /
+       #    \__\|/___/
+       #        J
+       test_commit J &&
+       test_commit JB &&
+       git reset --hard J &&
+       test_commit JC &&
+       git reset --hard J &&
+       test_commit JTEMP1 &&
+       test_merge JA JB &&
+       test_merge JAA JC &&
+       git reset --hard J &&
+       test_commit JTEMP2 &&
+       test_merge JD JB &&
+       test_merge JDD JC &&
+       git reset --hard J &&
+       test_commit JTEMP3 &&
+       test_merge JE JC &&
+       git rev-parse JC >expected &&
+       git merge-base --all --octopus JAA JDD JE >actual &&
+       test_cmp expected actual
+'
+
 test_done
index 1cd649e245e49735afa539ab0e982036154c4797..a953f1b55cc26aae703d3c84f74df3265cc2ed57 100755 (executable)
@@ -2,7 +2,6 @@
 
 test_description='merge-recursive: handle file mode'
 . ./test-lib.sh
-. "$TEST_DIRECTORY"/lib-prereq-FILEMODE.sh
 
 test_expect_success 'mode change in one branch: keep changed version' '
        : >file1 &&
index 7d479843522e3b183fdb4d72d86909b53e0edbda..719a11673bfc93463870d7d601eb3a0634b59b48 100755 (executable)
@@ -276,6 +276,48 @@ test_expect_success '-f option bypasses the type check' '
        git replace -f HEAD^ $BLOB
 '
 
+test_expect_success 'git cat-file --batch works on replace objects' '
+       git replace | grep $PARA3 &&
+       echo $PARA3 | git cat-file --batch
+'
+
+test_expect_success 'test --format bogus' '
+       test_must_fail git replace --format bogus >/dev/null 2>&1
+'
+
+test_expect_success 'test --format short' '
+       git replace --format=short >actual &&
+       git replace >expected &&
+       test_cmp expected actual
+'
+
+test_expect_success 'test --format medium' '
+       H1=$(git --no-replace-objects rev-parse HEAD~1) &&
+       HT=$(git --no-replace-objects rev-parse HEAD^{tree}) &&
+       MYTAG=$(git --no-replace-objects rev-parse mytag) &&
+       {
+               echo "$H1 -> $BLOB" &&
+               echo "$BLOB -> $REPLACED" &&
+               echo "$HT -> $H1" &&
+               echo "$PARA3 -> $S" &&
+               echo "$MYTAG -> $HASH1"
+       } | sort >expected &&
+       git replace -l --format medium | sort > actual &&
+       test_cmp expected actual
+'
+
+test_expect_success 'test --format long' '
+       {
+               echo "$H1 (commit) -> $BLOB (blob)" &&
+               echo "$BLOB (blob) -> $REPLACED (blob)" &&
+               echo "$HT (tree) -> $H1 (commit)" &&
+               echo "$PARA3 (commit) -> $S (commit)" &&
+               echo "$MYTAG (tag) -> $HASH1 (commit)"
+       } | sort >expected &&
+       git replace --format=long | sort > actual &&
+       test_cmp expected actual
+'
+
 test_expect_success 'replace ref cleanup' '
        test -n "$(git replace)" &&
        git replace -d $(git replace) &&
index 7ea14ced313d3f0dd20d4afd2fc2a0d3309b938f..10b1452766b613932f66421f6c9bfafecc093177 100755 (executable)
@@ -20,7 +20,17 @@ test_expect_success 'setup' '
        test_commit start2 &&
        git checkout master &&
        git merge -m next start2 &&
-       test_commit final
+       test_commit final &&
+
+       test_seq 40 |
+       while read i
+       do
+               git checkout --orphan "b$i" &&
+               test_tick &&
+               git commit --allow-empty -m "$i" &&
+               commit=$(git rev-parse --verify HEAD) &&
+               printf "$commit " >>.git/info/grafts
+       done
 '
 
 test_expect_success 'start is valid' '
@@ -79,6 +89,10 @@ test_expect_success 'final^1^! = final^1 ^final^1^1 ^final^1^2' '
        test_cmp expect actual
 '
 
+test_expect_success 'large graft octopus' '
+       test_cmp_rev_output b31 "git rev-parse --verify b1^30"
+'
+
 test_expect_success 'repack for next test' '
        git repack -a -d
 '
index b90e985a48efbf8cdb16c6166356f6984ffbcea8..e3c8c2c1b8817acd85866470c6f76c4a4f8d17d7 100755 (executable)
@@ -70,6 +70,35 @@ test_expect_success \
 rm -f idontexist untracked1 untracked2 \
      path0/idontexist path0/untracked1 path0/untracked2 \
      .git/index.lock
+rmdir path1
+
+test_expect_success \
+    'moving to absent target with trailing slash' \
+    'test_must_fail git mv path0/COPYING no-such-dir/ &&
+     test_must_fail git mv path0/COPYING no-such-dir// &&
+     git mv path0/ no-such-dir/ &&
+     test_path_is_dir no-such-dir'
+
+test_expect_success \
+    'clean up' \
+    'git reset --hard'
+
+test_expect_success \
+    'moving to existing untracked target with trailing slash' \
+    'mkdir path1 &&
+     git mv path0/ path1/ &&
+     test_path_is_dir path1/path0/'
+
+test_expect_success \
+    'moving to existing tracked target with trailing slash' \
+    'mkdir path2 &&
+     >path2/file && git add path2/file &&
+     git mv path1/path0/ path2/ &&
+     test_path_is_dir path2/path0/'
+
+test_expect_success \
+    'clean up' \
+    'git reset --hard'
 
 test_expect_success \
     'adding another file' \
@@ -413,4 +442,25 @@ test_expect_success 'mv --dry-run does not touch the submodule or .gitmodules' '
        git diff-files --quiet -- sub .gitmodules
 '
 
+test_expect_success 'checking out a commit before submodule moved needs manual updates' '
+       git mv sub sub2 &&
+       git commit -m "moved sub to sub2" &&
+       git checkout -q HEAD^ 2>actual &&
+       echo "warning: unable to rmdir sub2: Directory not empty" >expected &&
+       test_i18ncmp expected actual &&
+       git status -s sub2 >actual &&
+       echo "?? sub2/" >expected &&
+       test_cmp expected actual &&
+       ! test -f sub/.git &&
+       test -f sub2/.git &&
+       git submodule update &&
+       test -f sub/.git &&
+       rm -rf sub2 &&
+       git diff-index --exit-code HEAD &&
+       git update-index --refresh &&
+       git diff-files --quiet -- sub .gitmodules &&
+       git status -s sub2 >actual &&
+       ! test -s actual
+'
+
 test_done
index ff2590849de960cf6ec751f83904f1792862f626..7fe3367b6b6b8f4373dfdb9ffc956f8e1670801a 100755 (executable)
@@ -37,6 +37,18 @@ test_expect_failure TTY 'pager runs from subdir' '
        test_cmp expected actual
 '
 
+test_expect_success TTY 'LESS and LV envvars are set for pagination' '
+       (
+               sane_unset LESS LV &&
+               PAGER="env >pager-env.out" &&
+               export PAGER &&
+
+               test_terminal git log
+       ) &&
+       grep ^LESS= pager-env.out &&
+       grep ^LV= pager-env.out
+'
+
 test_expect_success TTY 'some commands do not use a pager' '
        rm -f paginated.out &&
        test_terminal git rev-list HEAD &&
index da5bd3b5a585667dad97481df27049f0aa7415eb..2ddf28c984de99cb17884f76d68a708076f57e12 100755 (executable)
@@ -65,9 +65,35 @@ test_expect_success 'diff in message is retained without -v' '
        check_message diff
 '
 
-test_expect_failure 'diff in message is retained with -v' '
+test_expect_success 'diff in message is retained with -v' '
        git commit --amend -F diff -v &&
        check_message diff
 '
 
+test_expect_success 'submodule log is stripped out too with -v' '
+       git config diff.submodule log &&
+       git submodule add ./. sub &&
+       git commit -m "sub added" &&
+       (
+               cd sub &&
+               echo "more" >>file &&
+               git commit -a -m "submodule commit"
+       ) &&
+       (
+               GIT_EDITOR=cat &&
+               export GIT_EDITOR &&
+               test_must_fail git commit -a -v 2>err
+       ) &&
+       test_i18ngrep "Aborting commit due to empty commit message." err
+'
+
+test_expect_success 'verbose diff is stripped out with set core.commentChar' '
+       (
+               GIT_EDITOR=cat &&
+               export GIT_EDITOR &&
+               test_must_fail git -c core.commentchar=";" commit -a -v 2>err
+       ) &&
+       test_i18ngrep "Aborting commit due to empty commit message." err
+'
+
 test_done
old mode 100644 (file)
new mode 100755 (executable)
old mode 100644 (file)
new mode 100755 (executable)
index 3fb3368903686c8588c51e881beaee1f29808575..812c9cd462f58f6fd76e3d339e95137aee598801 100755 (executable)
@@ -5,7 +5,6 @@
 test_description='Test export of commits to CVS'
 
 . ./test-lib.sh
-. "$TEST_DIRECTORY"/lib-prereq-FILEMODE.sh
 
 if ! test_have_prereq PERL; then
        skip_all='skipping git cvsexportcommit tests, perl not available'
index a82744bab072114675dea66fa94f21f661aa8dbd..66d3fc91a739a7f04bb73694a2f5d75034045d10 100755 (executable)
@@ -250,6 +250,89 @@ test_expect_success 'ignore apple' '
        )
 '
 
+test_expect_success SYMLINKS 'create p4 symlink' '
+       cd "$cli" &&
+       ln -s symlink-target symlink &&
+       p4 add symlink &&
+       p4 submit -d "add symlink"
+'
+
+test_expect_success SYMLINKS 'ensure p4 symlink parsed correctly' '
+       test_when_finished cleanup_git &&
+       git p4 clone --dest="$git" //depot@all &&
+       (
+               cd "$git" &&
+               test -L symlink &&
+               test $(readlink symlink) = symlink-target
+       )
+'
+
+test_expect_success SYMLINKS 'empty symlink target' '
+       (
+               # first create the file as a file
+               cd "$cli" &&
+               >empty-symlink &&
+               p4 add empty-symlink &&
+               p4 submit -d "add empty-symlink as a file"
+       ) &&
+       (
+               # now change it to be a symlink to "target1"
+               cd "$cli" &&
+               p4 edit empty-symlink &&
+               p4 reopen -t symlink empty-symlink &&
+               rm empty-symlink &&
+               ln -s target1 empty-symlink &&
+               p4 add empty-symlink &&
+               p4 submit -d "make empty-symlink point to target1"
+       ) &&
+       (
+               # Hack the p4 depot to make the symlink point to nothing;
+               # this should not happen in reality, but shows up
+               # in p4 repos in the wild.
+               #
+               # The sed expression changes this:
+               #     @@
+               #     text
+               #     @target1
+               #     @
+               # to this:
+               #     @@
+               #     text
+               #     @@
+               #
+               cd "$db/depot" &&
+               sed "/@target1/{; s/target1/@/; n; d; }" \
+                   empty-symlink,v >empty-symlink,v.tmp &&
+               mv empty-symlink,v.tmp empty-symlink,v
+       ) &&
+       (
+               # Make sure symlink really is empty.  Asking
+               # p4 to sync here will make it generate errors.
+               cd "$cli" &&
+               p4 print -q //depot/empty-symlink#2 >out &&
+               test ! -s out
+       ) &&
+       test_when_finished cleanup_git &&
+
+       # make sure git p4 handles it without error
+       git p4 clone --dest="$git" //depot@all &&
+
+       # fix the symlink, make it point to "target2"
+       (
+               cd "$cli" &&
+               p4 open empty-symlink &&
+               rm empty-symlink &&
+               ln -s target2 empty-symlink &&
+               p4 submit -d "make empty-symlink point to target2"
+       ) &&
+       cleanup_git &&
+       git p4 clone --dest="$git" //depot@all &&
+       (
+               cd "$git" &&
+               test $(readlink empty-symlink) = target2
+       )
+'
+
 test_expect_success 'kill p4d' '
        kill_p4d
 '
index ff2cc79701cce5ff570ff3c11d1ad0b60459d9ef..89311886db168e6613fa98d9c7a340d460435585 100755 (executable)
@@ -17,7 +17,7 @@ test_expect_success 'init depot' '
        )
 '
 
-# this works because EDITOR is set to :
+# this works because P4EDITOR is set to true
 test_expect_success 'no config, unedited, say yes' '
        git p4 clone --dest="$git" //depot &&
        test_when_finished cleanup_git &&
@@ -90,7 +90,9 @@ test_expect_success 'no config, edited' '
                cd "$git" &&
                echo line >>file1 &&
                git commit -a -m "change 5" &&
-               P4EDITOR="" EDITOR="\"$TRASH_DIRECTORY/ed.sh\"" git p4 submit &&
+               P4EDITOR="$TRASH_DIRECTORY/ed.sh" &&
+               export P4EDITOR &&
+               git p4 submit &&
                p4 changes //depot/... >wc &&
                test_line_count = 5 wc
        )
index 1fb7bc7cf98bdbdf89491c687e6347cda673d720..4caf36e0066313f5402f61645c4330093ba23fcf 100755 (executable)
@@ -17,7 +17,7 @@ test_expect_success 'init depot' '
        )
 '
 
-test_expect_failure 'is_cli_file_writeable function' '
+test_expect_success 'is_cli_file_writeable function' '
        (
                cd "$cli" &&
                echo a >a &&
index 77f63492d90e61500e6665ba447fbbad7a8c7b3b..23a827fa77d0a5d527cef29a15a205ee14d68e27 100755 (executable)
@@ -76,28 +76,28 @@ test_expect_success 'init depot' '
 '
 
 # double % for printf
-test_expect_success 'unsupported view wildcard %%n' '
+test_expect_success 'view wildcard %%n' '
        client_view "//depot/%%%%1/sub/... //client/sub/%%%%1/..." &&
        test_when_finished cleanup_git &&
-       test_must_fail git p4 clone --use-client-spec --dest="$git" //depot
+       git p4 clone --use-client-spec --dest="$git" //depot
 '
 
-test_expect_success 'unsupported view wildcard *' '
+test_expect_success 'view wildcard *' '
        client_view "//depot/*/bar/... //client/*/bar/..." &&
        test_when_finished cleanup_git &&
-       test_must_fail git p4 clone --use-client-spec --dest="$git" //depot
+       git p4 clone --use-client-spec --dest="$git" //depot
 '
 
-test_expect_success 'wildcard ... only supported at end of spec 1' '
+test_expect_success 'wildcard ... in the middle' '
        client_view "//depot/.../file11 //client/.../file11" &&
        test_when_finished cleanup_git &&
-       test_must_fail git p4 clone --use-client-spec --dest="$git" //depot
+       git p4 clone --use-client-spec --dest="$git" //depot
 '
 
-test_expect_success 'wildcard ... only supported at end of spec 2' '
+test_expect_success 'wildcard ... in the middle and at the end' '
        client_view "//depot/.../a/... //client/.../a/..." &&
        test_when_finished cleanup_git &&
-       test_must_fail git p4 clone --use-client-spec --dest="$git" //depot
+       git p4 clone --use-client-spec --dest="$git" //depot
 '
 
 test_expect_success 'basic map' '
index 67633257f3fccb2408d678759bce87f7a58acd0a..c7472cbf54584c7610094677693114d41b422afe 100755 (executable)
@@ -161,6 +161,56 @@ test_expect_success 'wildcard files submit back to p4, delete' '
        )
 '
 
+test_expect_success 'p4 deleted a wildcard file' '
+       (
+               cd "$cli" &&
+               echo "wild delete test" >wild@delete &&
+               p4 add -f wild@delete &&
+               p4 submit -d "add wild@delete"
+       ) &&
+       test_when_finished cleanup_git &&
+       git p4 clone --dest="$git" //depot &&
+       (
+               cd "$git" &&
+               test_path_is_file wild@delete
+       ) &&
+       (
+               cd "$cli" &&
+               # must use its encoded name
+               p4 delete wild%40delete &&
+               p4 submit -d "delete wild@delete"
+       ) &&
+       (
+               cd "$git" &&
+               git p4 sync &&
+               git merge --ff-only p4/master &&
+               test_path_is_missing wild@delete
+       )
+'
+
+test_expect_success 'wildcard files requiring keyword scrub' '
+       (
+               cd "$cli" &&
+               cat <<-\EOF >scrub@wild &&
+               $Id$
+               line2
+               EOF
+               p4 add -t text+k -f scrub@wild &&
+               p4 submit -d "scrub at wild"
+       ) &&
+       test_when_finished cleanup_git &&
+       git p4 clone --dest="$git" //depot &&
+       (
+               cd "$git" &&
+               git config git-p4.skipSubmitEdit true &&
+               git config git-p4.attemptRCSCleanup true &&
+               sed "s/^line2/line2 edit/" <scrub@wild >scrub@wild.tmp &&
+               mv -f scrub@wild.tmp scrub@wild &&
+               git commit -m "scrub at wild line2 edit" scrub@wild &&
+               git p4 submit
+       )
+'
+
 test_expect_success 'kill p4d' '
        kill_p4d
 '
index f2e85e518b650d9760c74533325e5a2d75154ebb..166b840bfae01d65e02d9ac7f7187142829d4f5f 100755 (executable)
@@ -19,16 +19,6 @@ test_expect_success 'create files' '
        )
 '
 
-p4_add_user() {
-       name=$1 fullname=$2 &&
-       p4 user -f -i <<-EOF &&
-       User: $name
-       Email: $name@localhost
-       FullName: $fullname
-       EOF
-       p4 passwd -P secret $name
-}
-
 p4_grant_admin() {
        name=$1 &&
        {
@@ -51,8 +41,8 @@ make_change_by_user() {
 
 # Test username support, submitting as user 'alice'
 test_expect_success 'preserve users' '
-       p4_add_user alice Alice &&
-       p4_add_user bob Bob &&
+       p4_add_user alice &&
+       p4_add_user bob &&
        p4_grant_admin alice &&
        git p4 clone --dest="$git" //depot &&
        test_when_finished cleanup_git &&
@@ -60,8 +50,8 @@ test_expect_success 'preserve users' '
                cd "$git" &&
                echo "username: a change by alice" >>file1 &&
                echo "username: a change by bob" >>file2 &&
-               git commit --author "Alice <alice@localhost>" -m "a change by alice" file1 &&
-               git commit --author "Bob <bob@localhost>" -m "a change by bob" file2 &&
+               git commit --author "Alice <alice@example.com>" -m "a change by alice" file1 &&
+               git commit --author "Bob <bob@example.com>" -m "a change by bob" file2 &&
                git config git-p4.skipSubmitEditCheck true &&
                P4EDITOR=touch P4USER=alice P4PASSWD=secret git p4 commit --preserve-user &&
                p4_check_commit_author file1 alice &&
@@ -78,7 +68,7 @@ test_expect_success 'refuse to preserve users without perms' '
                cd "$git" &&
                git config git-p4.skipSubmitEditCheck true &&
                echo "username-noperms: a change by alice" >>file1 &&
-               git commit --author "Alice <alice@localhost>" -m "perms: a change by alice" file1 &&
+               git commit --author "Alice <alice@example.com>" -m "perms: a change by alice" file1 &&
                P4EDITOR=touch P4USER=bob P4PASSWD=secret &&
                export P4EDITOR P4USER P4PASSWD &&
                test_must_fail git p4 commit --preserve-user &&
@@ -94,9 +84,9 @@ test_expect_success 'preserve user where author is unknown to p4' '
                cd "$git" &&
                git config git-p4.skipSubmitEditCheck true &&
                echo "username-bob: a change by bob" >>file1 &&
-               git commit --author "Bob <bob@localhost>" -m "preserve: a change by bob" file1 &&
+               git commit --author "Bob <bob@example.com>" -m "preserve: a change by bob" file1 &&
                echo "username-unknown: a change by charlie" >>file1 &&
-               git commit --author "Charlie <charlie@localhost>" -m "preserve: a change by charlie" file1 &&
+               git commit --author "Charlie <charlie@example.com>" -m "preserve: a change by charlie" file1 &&
                P4EDITOR=touch P4USER=alice P4PASSWD=secret &&
                export P4EDITOR P4USER P4PASSWD &&
                test_must_fail git p4 commit --preserve-user &&
@@ -121,24 +111,24 @@ test_expect_success 'not preserving user with mixed authorship' '
        (
                cd "$git" &&
                git config git-p4.skipSubmitEditCheck true &&
-               p4_add_user derek Derek &&
+               p4_add_user derek &&
 
-               make_change_by_user usernamefile3 Derek derek@localhost &&
+               make_change_by_user usernamefile3 Derek derek@example.com &&
                P4EDITOR=cat P4USER=alice P4PASSWD=secret &&
                export P4EDITOR P4USER P4PASSWD &&
                git p4 commit |\
-               grep "git author derek@localhost does not match" &&
+               grep "git author derek@example.com does not match" &&
 
-               make_change_by_user usernamefile3 Charlie charlie@localhost &&
+               make_change_by_user usernamefile3 Charlie charlie@example.com &&
                git p4 commit |\
-               grep "git author charlie@localhost does not match" &&
+               grep "git author charlie@example.com does not match" &&
 
-               make_change_by_user usernamefile3 alice alice@localhost &&
+               make_change_by_user usernamefile3 alice alice@example.com &&
                git p4 commit |\
                test_must_fail grep "git author.*does not match" &&
 
                git config git-p4.skipUserNameCheck true &&
-               make_change_by_user usernamefile3 Charlie charlie@localhost &&
+               make_change_by_user usernamefile3 Charlie charlie@example.com &&
                git p4 commit |\
                test_must_fail grep "git author.*does not match" &&
 
diff --git a/t/t9816-git-p4-locked.sh b/t/t9816-git-p4-locked.sh
new file mode 100755 (executable)
index 0000000..e71e543
--- /dev/null
@@ -0,0 +1,145 @@
+#!/bin/sh
+
+test_description='git p4 locked file behavior'
+
+. ./lib-git-p4.sh
+
+test_expect_success 'start p4d' '
+       start_p4d
+'
+
+# See
+# http://www.perforce.com/perforce/doc.current/manuals/p4sag/03_superuser.html#1088563
+# for suggestions on how to configure "sitewide pessimistic locking"
+# where only one person can have a file open for edit at a time.
+test_expect_success 'init depot' '
+       (
+               cd "$cli" &&
+               echo "TypeMap: +l //depot/..." | p4 typemap -i &&
+               echo file1 >file1 &&
+               p4 add file1 &&
+               p4 submit -d "add file1"
+       )
+'
+
+test_expect_success 'edit with lock not taken' '
+       test_when_finished cleanup_git &&
+       git p4 clone --dest="$git" //depot &&
+       (
+               cd "$git" &&
+               echo line2 >>file1 &&
+               git add file1 &&
+               git commit -m "line2 in file1" &&
+               git config git-p4.skipSubmitEdit true &&
+               git p4 submit
+       )
+'
+
+test_expect_failure 'add with lock not taken' '
+       test_when_finished cleanup_git &&
+       git p4 clone --dest="$git" //depot &&
+       (
+               cd "$git" &&
+               echo line1 >>add-lock-not-taken &&
+               git add file2 &&
+               git commit -m "add add-lock-not-taken" &&
+               git config git-p4.skipSubmitEdit true &&
+               git p4 submit --verbose
+       )
+'
+
+lock_in_another_client() {
+       # build a different client
+       cli2="$TRASH_DIRECTORY/cli2" &&
+       mkdir -p "$cli2" &&
+       test_when_finished "p4 client -f -d client2 && rm -rf \"$cli2\"" &&
+       (
+               cd "$cli2" &&
+               P4CLIENT=client2 &&
+               cli="$cli2" &&
+               client_view "//depot/... //client2/..." &&
+               p4 sync &&
+               p4 open file1
+       )
+}
+
+test_expect_failure 'edit with lock taken' '
+       lock_in_another_client &&
+       test_when_finished cleanup_git &&
+       test_when_finished "cd \"$cli\" && p4 sync -f file1" &&
+       git p4 clone --dest="$git" //depot &&
+       (
+               cd "$git" &&
+               echo line3 >>file1 &&
+               git add file1 &&
+               git commit -m "line3 in file1" &&
+               git config git-p4.skipSubmitEdit true &&
+               git p4 submit --verbose
+       )
+'
+
+test_expect_failure 'delete with lock taken' '
+       lock_in_another_client &&
+       test_when_finished cleanup_git &&
+       test_when_finished "cd \"$cli\" && p4 sync -f file1" &&
+       git p4 clone --dest="$git" //depot &&
+       (
+               cd "$git" &&
+               git rm file1 &&
+               git commit -m "delete file1" &&
+               git config git-p4.skipSubmitEdit true &&
+               git p4 submit --verbose
+       )
+'
+
+test_expect_failure 'chmod with lock taken' '
+       lock_in_another_client &&
+       test_when_finished cleanup_git &&
+       test_when_finished "cd \"$cli\" && p4 sync -f file1" &&
+       git p4 clone --dest="$git" //depot &&
+       (
+               cd "$git" &&
+               chmod +x file1 &&
+               git add file1 &&
+               git commit -m "chmod +x file1" &&
+               git config git-p4.skipSubmitEdit true &&
+               git p4 submit --verbose
+       )
+'
+
+test_expect_failure 'copy with lock taken' '
+       lock_in_another_client &&
+       test_when_finished cleanup_git &&
+       test_when_finished "cd \"$cli\" && p4 revert file2 && rm -f file2" &&
+       git p4 clone --dest="$git" //depot &&
+       (
+               cd "$git" &&
+               cp file1 file2 &&
+               git add file2 &&
+               git commit -m "cp file1 to file2" &&
+               git config git-p4.skipSubmitEdit true &&
+               git config git-p4.detectCopies true &&
+               git p4 submit --verbose
+       )
+'
+
+test_expect_failure 'move with lock taken' '
+       lock_in_another_client &&
+       test_when_finished cleanup_git &&
+       test_when_finished "cd \"$cli\" && p4 sync file1 && rm -f file2" &&
+       git p4 clone --dest="$git" //depot &&
+       (
+               cd "$git" &&
+               git mv file1 file2 &&
+               git commit -m "mv file1 to file2" &&
+               git config git-p4.skipSubmitEdit true &&
+               git config git-p4.detectRenames true &&
+               git p4 submit --verbose
+       )
+'
+
+test_expect_success 'kill p4d' '
+       kill_p4d
+'
+
+test_done
index 2f79146e6ce0c5df32845f7c9c0927cea133ad5f..aeae3ca7699b4c828f8ebe50624b5179fb5098b8 100644 (file)
@@ -1,4 +1,5 @@
-#!/bin/sh
+# Library of functions shared by all tests scripts, included by
+# test-lib.sh.
 #
 # Copyright (c) 2005 Junio C Hamano
 #
index d303e6c943ab94e15e65fb8a51cbe871cf9ef544..1531c241c08f0fad1a8e0789dbed2675b0fe5e56 100644 (file)
@@ -1,4 +1,4 @@
-#!/bin/sh
+# Test framework for git.  See t/README for usage.
 #
 # Copyright (c) 2005 Junio C Hamano
 #
@@ -481,8 +481,6 @@ test_at_end_hook_ () {
 test_done () {
        GIT_EXIT_OK=t
 
-       # Note: t0000 relies on $HARNESS_ACTIVE disabling the .counts
-       # output file
        if test -z "$HARNESS_ACTIVE"
        then
                test_results_dir="$TEST_OUTPUT_DIRECTORY/test-results"
@@ -577,11 +575,9 @@ then
 
        make_valgrind_symlink () {
                # handle only executables, unless they are shell libraries that
-               # need to be in the exec-path.  We will just use "#!" as a
-               # guess for a shell-script, since we have no idea what the user
-               # may have configured as the shell path.
+               # need to be in the exec-path.
                test -x "$1" ||
-               test "#!" = "$(head -c 2 <"$1")" ||
+               test "# " = "$(head -c 2 <"$1")" ||
                return;
 
                base=$(basename "$1")
@@ -834,6 +830,10 @@ test_lazy_prereq SYMLINKS '
        ln -s x y && test -h y
 '
 
+test_lazy_prereq FILEMODE '
+       test "$(git config --bool core.filemode)" = true
+'
+
 test_lazy_prereq CASE_INSENSITIVE_FS '
        echo good >CamelCase &&
        echo bad >camelcase &&
diff --git a/tag.c b/tag.c
index 78d272b863f22285048cf54b9dcb03f80cb36f00..7b07921b30641994ab3a877b6b377c5ce1f40149 100644 (file)
--- a/tag.c
+++ b/tag.c
@@ -86,7 +86,7 @@ int parse_tag_buffer(struct tag *item, const void *data, unsigned long size)
                return -1;
        bufptr += 48; /* "object " + sha1 + "\n" */
 
-       if (prefixcmp(bufptr, "type "))
+       if (!starts_with(bufptr, "type "))
                return -1;
        bufptr += 5;
        nl = memchr(bufptr, '\n', tail - bufptr);
@@ -109,7 +109,7 @@ int parse_tag_buffer(struct tag *item, const void *data, unsigned long size)
                item->tagged = NULL;
        }
 
-       if (bufptr + 4 < tail && !prefixcmp(bufptr, "tag "))
+       if (bufptr + 4 < tail && starts_with(bufptr, "tag "))
                ;               /* good */
        else
                return -1;
@@ -120,7 +120,7 @@ int parse_tag_buffer(struct tag *item, const void *data, unsigned long size)
        item->tag = xmemdupz(bufptr, nl - bufptr);
        bufptr = nl + 1;
 
-       if (bufptr + 7 < tail && !prefixcmp(bufptr, "tagger "))
+       if (bufptr + 7 < tail && starts_with(bufptr, "tagger "))
                item->date = parse_tag_date(bufptr, tail);
        else
                item->date = 0;
@@ -160,8 +160,8 @@ size_t parse_signature(const char *buf, unsigned long size)
 {
        char *eol;
        size_t len = 0;
-       while (len < size && prefixcmp(buf + len, PGP_SIGNATURE) &&
-                       prefixcmp(buf + len, PGP_MESSAGE)) {
+       while (len < size && !starts_with(buf + len, PGP_SIGNATURE) &&
+                       !starts_with(buf + len, PGP_MESSAGE)) {
                eol = memchr(buf + len, '\n', size - len);
                len += eol ? eol - (buf + len) + 1 : size - len;
        }
index ef1d7bae1441e07b6cb74e83a23d07b4720a8ebf..1e58f0476f3465f9b8b361cc4776abf5c051430b 100644 (file)
@@ -19,7 +19,7 @@ static void handle_command(const char *command, const char *arg, struct line_buf
 {
        switch (*command) {
        case 'b':
-               if (!prefixcmp(command, "binary ")) {
+               if (starts_with(command, "binary ")) {
                        struct strbuf sb = STRBUF_INIT;
                        strbuf_addch(&sb, '>');
                        buffer_read_binary(buf, &sb, strtouint32(arg));
@@ -28,12 +28,12 @@ static void handle_command(const char *command, const char *arg, struct line_buf
                        return;
                }
        case 'c':
-               if (!prefixcmp(command, "copy ")) {
+               if (starts_with(command, "copy ")) {
                        buffer_copy_bytes(buf, strtouint32(arg));
                        return;
                }
        case 's':
-               if (!prefixcmp(command, "skip ")) {
+               if (starts_with(command, "skip ")) {
                        buffer_skip_bytes(buf, strtouint32(arg));
                        return;
                }
index 00ce6c9a129674c1922e0c388366592c5ae7c8c7..14bdf9d2153a98d0b2a5d03395c1adcc166f8a07 100644 (file)
@@ -38,7 +38,7 @@ static void write_list_compact(const struct string_list *list)
 static int prefix_cb(struct string_list_item *item, void *cb_data)
 {
        const char *prefix = (const char *)cb_data;
-       return !prefixcmp(item->string, prefix);
+       return starts_with(item->string, prefix);
 }
 
 int main(int argc, char **argv)
diff --git a/trace.c b/trace.c
index 3d744d1d4d93e7d4770ce858a7d5aee1c955a82b..08180a90bc0074f821e5f2e05988e27c174be39f 100644 (file)
--- a/trace.c
+++ b/trace.c
@@ -76,7 +76,7 @@ static void trace_vprintf(const char *key, const char *fmt, va_list ap)
 }
 
 __attribute__((format (printf, 2, 3)))
-static void trace_printf_key(const char *key, const char *fmt, ...)
+void trace_printf_key(const char *key, const char *fmt, ...)
 {
        va_list ap;
        va_start(ap, fmt);
index 673b7c214f981cf158a8790b7278e37611c700fa..ad72fbd53cb7bfc7fc037d98288b0cc831ea949a 100644 (file)
@@ -190,7 +190,7 @@ static struct child_process *get_helper(struct transport *transport)
                        data->export = 1;
                else if (!strcmp(capname, "check-connectivity"))
                        data->check_connectivity = 1;
-               else if (!data->refspecs && !prefixcmp(capname, "refspec ")) {
+               else if (!data->refspecs && starts_with(capname, "refspec ")) {
                        ALLOC_GROW(refspecs,
                                   refspec_nr + 1,
                                   refspec_alloc);
@@ -199,17 +199,17 @@ static struct child_process *get_helper(struct transport *transport)
                        data->connect = 1;
                } else if (!strcmp(capname, "signed-tags")) {
                        data->signed_tags = 1;
-               } else if (!prefixcmp(capname, "export-marks ")) {
+               } else if (starts_with(capname, "export-marks ")) {
                        struct strbuf arg = STRBUF_INIT;
                        strbuf_addstr(&arg, "--export-marks=");
                        strbuf_addstr(&arg, capname + strlen("export-marks "));
                        data->export_marks = strbuf_detach(&arg, NULL);
-               } else if (!prefixcmp(capname, "import-marks")) {
+               } else if (starts_with(capname, "import-marks")) {
                        struct strbuf arg = STRBUF_INIT;
                        strbuf_addstr(&arg, "--import-marks=");
                        strbuf_addstr(&arg, capname + strlen("import-marks "));
                        data->import_marks = strbuf_detach(&arg, NULL);
-               } else if (!prefixcmp(capname, "no-private-update")) {
+               } else if (starts_with(capname, "no-private-update")) {
                        data->no_private_update = 1;
                } else if (mandatory) {
                        die("Unknown mandatory capability %s. This remote "
@@ -311,7 +311,7 @@ static int set_helper_option(struct transport *transport,
 
        if (!strcmp(buf.buf, "ok"))
                ret = 0;
-       else if (!prefixcmp(buf.buf, "error")) {
+       else if (starts_with(buf.buf, "error")) {
                ret = -1;
        } else if (!strcmp(buf.buf, "unsupported"))
                ret = 1;
@@ -360,6 +360,12 @@ static int fetch_with_fetch(struct transport *transport,
            data->transport_options.check_self_contained_and_connected)
                set_helper_option(transport, "check-connectivity", "true");
 
+       if (transport->cloning)
+               set_helper_option(transport, "cloning", "true");
+
+       if (data->transport_options.update_shallow)
+               set_helper_option(transport, "update-shallow", "true");
+
        for (i = 0; i < nr_heads; i++) {
                const struct ref *posn = to_fetch[i];
                if (posn->status & REF_STATUS_UPTODATE)
@@ -375,7 +381,7 @@ static int fetch_with_fetch(struct transport *transport,
        while (1) {
                recvline(data, &buf);
 
-               if (!prefixcmp(buf.buf, "lock ")) {
+               if (starts_with(buf.buf, "lock ")) {
                        const char *name = buf.buf + 5;
                        if (transport->pack_lockfile)
                                warning("%s also locked %s", data->name, name);
@@ -646,10 +652,10 @@ static int push_update_ref_status(struct strbuf *buf,
        char *refname, *msg;
        int status;
 
-       if (!prefixcmp(buf->buf, "ok ")) {
+       if (starts_with(buf->buf, "ok ")) {
                status = REF_STATUS_OK;
                refname = buf->buf + 3;
-       } else if (!prefixcmp(buf->buf, "error ")) {
+       } else if (starts_with(buf->buf, "error ")) {
                status = REF_STATUS_REMOTE_REJECT;
                refname = buf->buf + 6;
        } else
@@ -1129,9 +1135,8 @@ static int udt_do_write(struct unidirectional_transfer *t)
                return 0;       /* Nothing to write. */
 
        transfer_debug("%s is writable", t->dest_name);
-       bytes = write(t->dest, t->buf, t->bufuse);
-       if (bytes < 0 && errno != EWOULDBLOCK && errno != EAGAIN &&
-               errno != EINTR) {
+       bytes = xwrite(t->dest, t->buf, t->bufuse);
+       if (bytes < 0 && errno != EWOULDBLOCK) {
                error("write(%s) failed: %s", t->dest_name, strerror(errno));
                return -1;
        } else if (bytes > 0) {
index 7202b7777d804b75eb2ec54f666e81173690678c..ca7bb441bf503c598d9679b338d7f7c0d8103cb0 100644 (file)
@@ -14,6 +14,7 @@
 #include "url.h"
 #include "submodule.h"
 #include "string-list.h"
+#include "sha1-array.h"
 
 /* rsync support */
 
@@ -169,13 +170,13 @@ static void set_upstreams(struct transport *transport, struct ref *refs,
                remotename = ref->name;
                tmp = resolve_ref_unsafe(localname, sha, 1, &flag);
                if (tmp && flag & REF_ISSYMREF &&
-                       !prefixcmp(tmp, "refs/heads/"))
+                       starts_with(tmp, "refs/heads/"))
                        localname = tmp;
 
                /* Both source and destination must be local branches. */
-               if (!localname || prefixcmp(localname, "refs/heads/"))
+               if (!localname || !starts_with(localname, "refs/heads/"))
                        continue;
-               if (!remotename || prefixcmp(remotename, "refs/heads/"))
+               if (!remotename || !starts_with(remotename, "refs/heads/"))
                        continue;
 
                if (!pretend)
@@ -191,7 +192,7 @@ static void set_upstreams(struct transport *transport, struct ref *refs,
 
 static const char *rsync_url(const char *url)
 {
-       return prefixcmp(url, "rsync://") ? skip_prefix(url, "rsync:") : url;
+       return !starts_with(url, "rsync://") ? skip_prefix(url, "rsync:") : url;
 }
 
 static struct ref *get_refs_via_rsync(struct transport *transport, int for_push)
@@ -296,8 +297,8 @@ static int write_one_ref(const char *name, const unsigned char *sha1,
        FILE *f;
 
        /* when called via for_each_ref(), flags is non-zero */
-       if (flags && prefixcmp(name, "refs/heads/") &&
-                       prefixcmp(name, "refs/tags/"))
+       if (flags && !starts_with(name, "refs/heads/") &&
+                       !starts_with(name, "refs/tags/"))
                return 0;
 
        strbuf_addstr(buf, name);
@@ -454,7 +455,8 @@ struct git_transport_data {
        struct child_process *conn;
        int fd[2];
        unsigned got_remote_heads : 1;
-       struct extra_have_objects extra_have;
+       struct sha1_array extra_have;
+       struct sha1_array shallow;
 };
 
 static int set_git_option(struct git_transport_options *opts,
@@ -475,6 +477,9 @@ static int set_git_option(struct git_transport_options *opts,
        } else if (!strcmp(name, TRANS_OPT_KEEP)) {
                opts->keep = !!value;
                return 0;
+       } else if (!strcmp(name, TRANS_OPT_UPDATE_SHALLOW)) {
+               opts->update_shallow = !!value;
+               return 0;
        } else if (!strcmp(name, TRANS_OPT_DEPTH)) {
                if (!value)
                        opts->depth = 0;
@@ -511,7 +516,9 @@ static struct ref *get_refs_via_connect(struct transport *transport, int for_pus
 
        connect_setup(transport, for_push, 0);
        get_remote_heads(data->fd[0], NULL, 0, &refs,
-                        for_push ? REF_NORMAL : 0, &data->extra_have);
+                        for_push ? REF_NORMAL : 0,
+                        &data->extra_have,
+                        &data->shallow);
        data->got_remote_heads = 1;
 
        return refs;
@@ -538,16 +545,19 @@ static int fetch_refs_via_pack(struct transport *transport,
        args.depth = data->options.depth;
        args.check_self_contained_and_connected =
                data->options.check_self_contained_and_connected;
+       args.cloning = transport->cloning;
+       args.update_shallow = data->options.update_shallow;
 
        if (!data->got_remote_heads) {
                connect_setup(transport, 0, 0);
-               get_remote_heads(data->fd[0], NULL, 0, &refs_tmp, 0, NULL);
+               get_remote_heads(data->fd[0], NULL, 0, &refs_tmp, 0,
+                                NULL, &data->shallow);
                data->got_remote_heads = 1;
        }
 
        refs = fetch_pack(&args, data->fd, data->conn,
                          refs_tmp ? refs_tmp : transport->remote_refs,
-                         dest, to_fetch, nr_heads,
+                         dest, to_fetch, nr_heads, &data->shallow,
                          &transport->pack_lockfile);
        close(data->fd[0]);
        close(data->fd[1]);
@@ -652,7 +662,7 @@ static void print_ok_ref_status(struct ref *ref, int porcelain)
                print_ref_status('-', "[deleted]", ref, NULL, NULL, porcelain);
        else if (is_null_sha1(ref->old_sha1))
                print_ref_status('*',
-                       (!prefixcmp(ref->name, "refs/tags/") ? "[new tag]" :
+                       (starts_with(ref->name, "refs/tags/") ? "[new tag]" :
                        "[new branch]"),
                        ref, ref->peer_ref, NULL, porcelain);
        else {
@@ -713,6 +723,10 @@ static int print_one_push_status(struct ref *ref, const char *dest, int count, i
                print_ref_status('!', "[rejected]", ref, ref->peer_ref,
                                                 "stale info", porcelain);
                break;
+       case REF_STATUS_REJECT_SHALLOW:
+               print_ref_status('!', "[rejected]", ref, ref->peer_ref,
+                                                "new shallow roots not allowed", porcelain);
+               break;
        case REF_STATUS_REMOTE_REJECT:
                print_ref_status('!', "[remote rejected]", ref,
                                                 ref->deletion ? NULL : ref->peer_ref,
@@ -805,7 +819,8 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re
                struct ref *tmp_refs;
                connect_setup(transport, 1, 0);
 
-               get_remote_heads(data->fd[0], NULL, 0, &tmp_refs, REF_NORMAL, NULL);
+               get_remote_heads(data->fd[0], NULL, 0, &tmp_refs, REF_NORMAL,
+                                NULL, &data->shallow);
                data->got_remote_heads = 1;
        }
 
@@ -885,14 +900,6 @@ void transport_take_over(struct transport *transport,
        transport->cannot_reuse = 1;
 }
 
-static int is_local(const char *url)
-{
-       const char *colon = strchr(url, ':');
-       const char *slash = strchr(url, '/');
-       return !colon || (slash && slash < colon) ||
-               has_dos_drive_prefix(url);
-}
-
 static int is_file(const char *url)
 {
        struct stat buf;
@@ -930,18 +937,18 @@ struct transport *transport_get(struct remote *remote, const char *url)
 
                while (is_urlschemechar(p == url, *p))
                        p++;
-               if (!prefixcmp(p, "::"))
+               if (starts_with(p, "::"))
                        helper = xstrndup(url, p - url);
        }
 
        if (helper) {
                transport_helper_init(ret, helper);
-       } else if (!prefixcmp(url, "rsync:")) {
+       } else if (starts_with(url, "rsync:")) {
                ret->get_refs_list = get_refs_via_rsync;
                ret->fetch = fetch_objs_via_rsync;
                ret->push = rsync_transport_push;
                ret->smart_options = NULL;
-       } else if (is_local(url) && is_file(url) && is_bundle(url, 1)) {
+       } else if (url_is_local_not_ssh(url) && is_file(url) && is_bundle(url, 1)) {
                struct bundle_transport_data *data = xcalloc(1, sizeof(*data));
                ret->data = data;
                ret->get_refs_list = get_refs_from_bundle;
@@ -949,11 +956,11 @@ struct transport *transport_get(struct remote *remote, const char *url)
                ret->disconnect = close_bundle;
                ret->smart_options = NULL;
        } else if (!is_url(url)
-               || !prefixcmp(url, "file://")
-               || !prefixcmp(url, "git://")
-               || !prefixcmp(url, "ssh://")
-               || !prefixcmp(url, "git+ssh://")
-               || !prefixcmp(url, "ssh+git://")) {
+               || starts_with(url, "file://")
+               || starts_with(url, "git://")
+               || starts_with(url, "ssh://")
+               || starts_with(url, "git+ssh://")
+               || starts_with(url, "ssh+git://")) {
                /* These are builtin smart transports. */
                struct git_transport_data *data = xcalloc(1, sizeof(*data));
                ret->data = data;
@@ -1297,7 +1304,7 @@ char *transport_anonymize_url(const char *url)
        size_t anon_len, prefix_len = 0;
 
        anon_part = strchr(url, '@');
-       if (is_local(url) || !anon_part)
+       if (url_is_local_not_ssh(url) || !anon_part)
                goto literal_copy;
 
        anon_len = strlen(++anon_part);
index 8f96bed775e720043658169b46f70e192cf76b41..02ea248db18a558674825384a5d2678a45483873 100644 (file)
@@ -11,6 +11,7 @@ struct git_transport_options {
        unsigned followtags : 1;
        unsigned check_self_contained_and_connected : 1;
        unsigned self_contained_and_connected : 1;
+       unsigned update_shallow : 1;
        int depth;
        const char *uploadpack;
        const char *receivepack;
@@ -35,6 +36,12 @@ struct transport {
         */
        unsigned cannot_reuse : 1;
 
+       /*
+        * A hint from caller that it will be performing a clone, not
+        * normal fetch. IOW the repository is guaranteed empty.
+        */
+       unsigned cloning : 1;
+
        /**
         * Returns 0 if successful, positive if the option is not
         * recognized or is inapplicable, and negative if the option
@@ -146,6 +153,9 @@ struct transport *transport_get(struct remote *, const char *);
 /* Aggressively fetch annotated tags if possible */
 #define TRANS_OPT_FOLLOWTAGS "followtags"
 
+/* Accept refs that may update .git/shallow without --depth */
+#define TRANS_OPT_UPDATE_SHALLOW "updateshallow"
+
 /**
  * Returns 0 if the option was used, non-zero otherwise. Prints a
  * message to stderr if the option is not used.
@@ -193,10 +203,4 @@ void transport_print_push_status(const char *dest, struct ref *refs,
 
 typedef void alternate_ref_fn(const struct ref *, void *);
 extern void for_each_alternate_ref(alternate_ref_fn, void *);
-
-struct send_pack_args;
-extern int send_pack(struct send_pack_args *args,
-                    int fd[], struct child_process *conn,
-                    struct ref *remote_refs,
-                    struct extra_have_objects *extra_have);
 #endif
index ad3e9a04fe8ccb9286b94480f4484b0569f70a57..164354dad7cbbaa7100f73256807680a75188021 100644 (file)
@@ -830,23 +830,24 @@ static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, str
 }
 
 static int clear_ce_flags_1(struct cache_entry **cache, int nr,
-                           char *prefix, int prefix_len,
+                           struct strbuf *prefix,
                            int select_mask, int clear_mask,
                            struct exclude_list *el, int defval);
 
 /* Whole directory matching */
 static int clear_ce_flags_dir(struct cache_entry **cache, int nr,
-                             char *prefix, int prefix_len,
+                             struct strbuf *prefix,
                              char *basename,
                              int select_mask, int clear_mask,
                              struct exclude_list *el, int defval)
 {
        struct cache_entry **cache_end;
        int dtype = DT_DIR;
-       int ret = is_excluded_from_list(prefix, prefix_len,
+       int ret = is_excluded_from_list(prefix->buf, prefix->len,
                                        basename, &dtype, el);
+       int rc;
 
-       prefix[prefix_len++] = '/';
+       strbuf_addch(prefix, '/');
 
        /* If undecided, use matching result of parent dir in defval */
        if (ret < 0)
@@ -854,7 +855,7 @@ static int clear_ce_flags_dir(struct cache_entry **cache, int nr,
 
        for (cache_end = cache; cache_end != cache + nr; cache_end++) {
                struct cache_entry *ce = *cache_end;
-               if (strncmp(ce->name, prefix, prefix_len))
+               if (strncmp(ce->name, prefix->buf, prefix->len))
                        break;
        }
 
@@ -865,10 +866,12 @@ static int clear_ce_flags_dir(struct cache_entry **cache, int nr,
         * calling clear_ce_flags_1(). That function will call
         * the expensive is_excluded_from_list() on every entry.
         */
-       return clear_ce_flags_1(cache, cache_end - cache,
-                               prefix, prefix_len,
-                               select_mask, clear_mask,
-                               el, ret);
+       rc = clear_ce_flags_1(cache, cache_end - cache,
+                             prefix,
+                             select_mask, clear_mask,
+                             el, ret);
+       strbuf_setlen(prefix, prefix->len - 1);
+       return rc;
 }
 
 /*
@@ -887,7 +890,7 @@ static int clear_ce_flags_dir(struct cache_entry **cache, int nr,
  * Top level path has prefix_len zero.
  */
 static int clear_ce_flags_1(struct cache_entry **cache, int nr,
-                           char *prefix, int prefix_len,
+                           struct strbuf *prefix,
                            int select_mask, int clear_mask,
                            struct exclude_list *el, int defval)
 {
@@ -907,10 +910,10 @@ static int clear_ce_flags_1(struct cache_entry **cache, int nr,
                        continue;
                }
 
-               if (prefix_len && strncmp(ce->name, prefix, prefix_len))
+               if (prefix->len && strncmp(ce->name, prefix->buf, prefix->len))
                        break;
 
-               name = ce->name + prefix_len;
+               name = ce->name + prefix->len;
                slash = strchr(name, '/');
 
                /* If it's a directory, try whole directory match first */
@@ -918,29 +921,26 @@ static int clear_ce_flags_1(struct cache_entry **cache, int nr,
                        int processed;
 
                        len = slash - name;
-                       memcpy(prefix + prefix_len, name, len);
+                       strbuf_add(prefix, name, len);
 
-                       /*
-                        * terminate the string (no trailing slash),
-                        * clear_c_f_dir needs it
-                        */
-                       prefix[prefix_len + len] = '\0';
                        processed = clear_ce_flags_dir(cache, cache_end - cache,
-                                                      prefix, prefix_len + len,
-                                                      prefix + prefix_len,
+                                                      prefix,
+                                                      prefix->buf + prefix->len - len,
                                                       select_mask, clear_mask,
                                                       el, defval);
 
                        /* clear_c_f_dir eats a whole dir already? */
                        if (processed) {
                                cache += processed;
+                               strbuf_setlen(prefix, prefix->len - len);
                                continue;
                        }
 
-                       prefix[prefix_len + len++] = '/';
+                       strbuf_addch(prefix, '/');
                        cache += clear_ce_flags_1(cache, cache_end - cache,
-                                                 prefix, prefix_len + len,
+                                                 prefix,
                                                  select_mask, clear_mask, el, defval);
+                       strbuf_setlen(prefix, prefix->len - len - 1);
                        continue;
                }
 
@@ -961,9 +961,12 @@ static int clear_ce_flags(struct cache_entry **cache, int nr,
                            int select_mask, int clear_mask,
                            struct exclude_list *el)
 {
-       char prefix[PATH_MAX];
+       static struct strbuf prefix = STRBUF_INIT;
+
+       strbuf_reset(&prefix);
+
        return clear_ce_flags_1(cache, nr,
-                               prefix, 0,
+                               &prefix,
                                select_mask, clear_mask,
                                el, 0);
 }
index d30f339e707fe3263b9c186e54b6b3317afc986c..0c44f6b292563e7c87bce0b7b5d9b8a4ccd32ec3 100644 (file)
@@ -84,7 +84,7 @@ static void create_pack_file(void)
        char *shallow_file = NULL;
 
        if (shallow_nr) {
-               shallow_file = setup_temporary_shallow();
+               shallow_file = setup_temporary_shallow(NULL);
                argv[arg++] = "--shallow-file";
                argv[arg++] = shallow_file;
        }
@@ -394,7 +394,7 @@ static int get_common_commits(void)
                        got_other = 0;
                        continue;
                }
-               if (!prefixcmp(line, "have ")) {
+               if (starts_with(line, "have ")) {
                        switch (got_sha1(line+5, sha1)) {
                        case -1: /* they have what we do not */
                                got_other = 1;
@@ -540,7 +540,7 @@ static void receive_needs(void)
                if (!line)
                        break;
 
-               if (!prefixcmp(line, "shallow ")) {
+               if (starts_with(line, "shallow ")) {
                        unsigned char sha1[20];
                        struct object *object;
                        if (get_sha1_hex(line + 8, sha1))
@@ -556,14 +556,14 @@ static void receive_needs(void)
                        }
                        continue;
                }
-               if (!prefixcmp(line, "deepen ")) {
+               if (starts_with(line, "deepen ")) {
                        char *end;
                        depth = strtol(line + 7, &end, 0);
                        if (end == line + 7 || depth <= 0)
                                die("Invalid deepen: %s", line);
                        continue;
                }
-               if (prefixcmp(line, "want ") ||
+               if (!starts_with(line, "want ") ||
                    get_sha1_hex(line+5, sha1_buf))
                        die("git upload-pack: protocol error, "
                            "expected to get sha, not '%s'", line);
@@ -619,7 +619,7 @@ static void receive_needs(void)
        if (depth > 0) {
                struct commit_list *result = NULL, *backup = NULL;
                int i;
-               if (depth == INFINITE_DEPTH)
+               if (depth == INFINITE_DEPTH && !is_repository_shallow())
                        for (i = 0; i < shallows.nr; i++) {
                                struct object *object = shallows.objects[i].item;
                                object->flags |= NOT_SHALLOW;
@@ -757,6 +757,7 @@ static void upload_pack(void)
                reset_timeout();
                head_ref_namespaced(send_ref, &symref);
                for_each_namespaced_ref(send_ref, &symref);
+               advertise_shallow_grafts(1);
                packet_flush(1);
        } else {
                head_ref_namespaced(mark_our_ref, NULL);
@@ -814,7 +815,7 @@ int main(int argc, char **argv)
                        strict = 1;
                        continue;
                }
-               if (!prefixcmp(arg, "--timeout=")) {
+               if (starts_with(arg, "--timeout=")) {
                        timeout = atoi(arg+10);
                        daemon_mode = 1;
                        continue;
@@ -834,8 +835,7 @@ int main(int argc, char **argv)
 
        if (!enter_repo(dir, strict))
                die("'%s' does not appear to be a git repository", dir);
-       if (is_repository_shallow())
-               die("attempt to fetch/clone from a shallow repository");
+
        git_config(upload_pack_config, NULL);
        upload_pack();
        return 0;
index f2b23c81de11a63c2c1c2ac925be9081ad5bea5c..bd0f2c2b86847708e5ebe668773f0fd08b9efc5d 100644 (file)
@@ -162,22 +162,13 @@ static void die_short_read(struct line_buffer *input)
        die("invalid dump: unexpected end of file");
 }
 
-static int ends_with(const char *s, size_t len, const char *suffix)
-{
-       const size_t suffixlen = strlen(suffix);
-       if (len < suffixlen)
-               return 0;
-       return !memcmp(s + len - suffixlen, suffix, suffixlen);
-}
-
 static int parse_cat_response_line(const char *header, off_t *len)
 {
-       size_t headerlen = strlen(header);
        uintmax_t n;
        const char *type;
        const char *end;
 
-       if (ends_with(header, headerlen, " missing"))
+       if (ends_with(header, " missing"))
                return error("cat-blob reports missing blob: %s", header);
        type = strstr(header, " blob ");
        if (!type)
index 4625cdb900f179e94314348340719b677fed1ca1..4e5581005936a1e7832e3ab224cfe142ac777a63 100644 (file)
@@ -17,6 +17,9 @@
 #include "strbuf.h"
 #include "utf8.h"
 
+static char cut_line[] =
+"------------------------ >8 ------------------------\n";
+
 static char default_wt_status_colors[][COLOR_MAXLEN] = {
        GIT_COLOR_NORMAL, /* WT_STATUS_HEADER */
        GIT_COLOR_GREEN,  /* WT_STATUS_UPDATED */
@@ -793,6 +796,18 @@ static void wt_status_print_other(struct wt_status *s,
        status_printf_ln(s, GIT_COLOR_NORMAL, "");
 }
 
+void wt_status_truncate_message_at_cut_line(struct strbuf *buf)
+{
+       const char *p;
+       struct strbuf pattern = STRBUF_INIT;
+
+       strbuf_addf(&pattern, "%c %s", comment_line_char, cut_line);
+       p = strstr(buf->buf, pattern.buf);
+       if (p && (p == buf->buf || p[-1] == '\n'))
+               strbuf_setlen(buf, p - buf->buf);
+       strbuf_release(&pattern);
+}
+
 static void wt_status_print_verbose(struct wt_status *s)
 {
        struct rev_info rev;
@@ -813,10 +828,20 @@ static void wt_status_print_verbose(struct wt_status *s)
         * If we're not going to stdout, then we definitely don't
         * want color, since we are going to the commit message
         * file (and even the "auto" setting won't work, since it
-        * will have checked isatty on stdout).
+        * will have checked isatty on stdout). But we then do want
+        * to insert the scissor line here to reliably remove the
+        * diff before committing.
         */
-       if (s->fp != stdout)
+       if (s->fp != stdout) {
+               const char *explanation = _("Do not touch the line above.\nEverything below will be removed.");
+               struct strbuf buf = STRBUF_INIT;
+
                rev.diffopt.use_color = 0;
+               fprintf(s->fp, "%c %s", comment_line_char, cut_line);
+               strbuf_add_commented_lines(&buf, explanation, strlen(explanation));
+               fputs(buf.buf, s->fp);
+               strbuf_release(&buf);
+       }
        run_diff_index(&rev, 1);
 }
 
@@ -829,7 +854,7 @@ static void wt_status_print_tracking(struct wt_status *s)
        int i;
 
        assert(s->branch && !s->is_initial);
-       if (prefixcmp(s->branch, "refs/heads/"))
+       if (!starts_with(s->branch, "refs/heads/"))
                return;
        branch = branch_get(s->branch + 11);
        if (!format_tracking_info(branch, &sb))
@@ -1088,9 +1113,9 @@ static char *read_and_strip_branch(const char *path)
                strbuf_setlen(&sb, sb.len - 1);
        if (!sb.len)
                goto got_nothing;
-       if (!prefixcmp(sb.buf, "refs/heads/"))
+       if (starts_with(sb.buf, "refs/heads/"))
                strbuf_remove(&sb,0, strlen("refs/heads/"));
-       else if (!prefixcmp(sb.buf, "refs/"))
+       else if (starts_with(sb.buf, "refs/"))
                ;
        else if (!get_sha1_hex(sb.buf, sha1)) {
                const char *abbrev;
@@ -1120,7 +1145,7 @@ static int grab_1st_switch(unsigned char *osha1, unsigned char *nsha1,
        struct grab_1st_switch_cbdata *cb = cb_data;
        const char *target = NULL, *end;
 
-       if (prefixcmp(message, "checkout: moving from "))
+       if (!starts_with(message, "checkout: moving from "))
                return 0;
        message += strlen("checkout: moving from ");
        target = strstr(message, " to ");
@@ -1155,9 +1180,9 @@ static void wt_status_get_detached_from(struct wt_status_state *state)
             ((commit = lookup_commit_reference_gently(sha1, 1)) != NULL &&
              !hashcmp(cb.nsha1, commit->object.sha1)))) {
                int ofs;
-               if (!prefixcmp(ref, "refs/tags/"))
+               if (starts_with(ref, "refs/tags/"))
                        ofs = strlen("refs/tags/");
-               else if (!prefixcmp(ref, "refs/remotes/"))
+               else if (starts_with(ref, "refs/remotes/"))
                        ofs = strlen("refs/remotes/");
                else
                        ofs = 0;
@@ -1246,7 +1271,7 @@ void wt_status_print(struct wt_status *s)
        if (s->branch) {
                const char *on_what = _("On branch ");
                const char *branch_name = s->branch;
-               if (!prefixcmp(branch_name, "refs/heads/"))
+               if (starts_with(branch_name, "refs/heads/"))
                        branch_name += 11;
                else if (!strcmp(branch_name, "HEAD")) {
                        branch_status_color = color(WT_STATUS_NOBRANCH, s);
@@ -1447,7 +1472,7 @@ static void wt_shortstatus_print_tracking(struct wt_status *s)
                return;
        branch_name = s->branch;
 
-       if (!prefixcmp(branch_name, "refs/heads/"))
+       if (starts_with(branch_name, "refs/heads/"))
                branch_name += 11;
        else if (!strcmp(branch_name, "HEAD")) {
                branch_name = _("HEAD (no branch)");
index 6c29e6f5e57f84eb7acdc3cf1ee621cc717fd5a9..30a481258366733ad5d6a65e3a9c3ae1ee74e833 100644 (file)
@@ -91,6 +91,7 @@ struct wt_status_state {
        unsigned char cherry_pick_head_sha1[20];
 };
 
+void wt_status_truncate_message_at_cut_line(struct strbuf *);
 void wt_status_prepare(struct wt_status *s);
 void wt_status_print(struct wt_status *s);
 void wt_status_collect(struct wt_status *s);