a mailing list (git@vger.kernel.org) for code submissions, code
reviews, and bug reports.
-Nevertheless, you can use [submitGit](http://submitgit.herokuapp.com/) to
+Nevertheless, you can use [GitGitGadget](https://gitgitgadget.github.io/) to
conveniently send your Pull Requests commits to our mailing list.
Please read ["A note from the maintainer"](https://git.kernel.org/pub/scm/git/git.git/plain/MaintNotes?h=todo)
Thanks for taking the time to contribute to Git! Please be advised that the
Git community does not use github.com for their contributions. Instead, we use
a mailing list (git@vger.kernel.org) for code submissions, code reviews, and
-bug reports. Nevertheless, you can use submitGit to conveniently send your Pull
-Requests commits to our mailing list.
+bug reports. Nevertheless, you can use GitGitGadget (https://gitgitgadget.github.io/)
+to conveniently send your Pull Requests commits to our mailing list.
Please read the "guidelines for contributing" linked above!
/git-init-db
/git-interpret-trailers
/git-instaweb
-/git-legacy-rebase
+/git-legacy-stash
/git-log
/git-ls-files
/git-ls-remote
manpage-base-url.xsl
SubmittingPatches.txt
tmp-doc-diff/
+GIT-ASCIIDOCFLAGS
or commands:
Literal examples (e.g. use of command-line options, command names,
- branch names, configuration and environment variables) must be
- typeset in monospace (i.e. wrapped with backticks):
+ branch names, URLs, pathnames (files and directories), configuration and
+ environment variables) must be typeset in monospace (i.e. wrapped with
+ backticks):
`--pretty=oneline`
`git rev-list`
`remote.pushDefault`
+ `http://git.example.com`
+ `.git/config`
`GIT_DIR`
`HEAD`
$(wildcard git-*.txt))
MAN1_TXT += git.txt
MAN1_TXT += gitk.txt
-MAN1_TXT += gitremote-helpers.txt
MAN1_TXT += gitweb.txt
MAN5_TXT += gitattributes.txt
MAN7_TXT += giteveryday.txt
MAN7_TXT += gitglossary.txt
MAN7_TXT += gitnamespaces.txt
+MAN7_TXT += gitremote-helpers.txt
MAN7_TXT += gitrevisions.txt
MAN7_TXT += gitsubmodules.txt
MAN7_TXT += gittutorial-2.txt
show_tool_names can_merge "* " || :' >mergetools-merge.txt && \
date >$@
+TRACK_ASCIIDOCFLAGS = $(subst ','\'',$(ASCIIDOC_COMMON):$(ASCIIDOC_HTML):$(ASCIIDOC_DOCBOOK))
+
+GIT-ASCIIDOCFLAGS: FORCE
+ @FLAGS='$(TRACK_ASCIIDOCFLAGS)'; \
+ if test x"$$FLAGS" != x"`cat GIT-ASCIIDOCFLAGS 2>/dev/null`" ; then \
+ echo >&2 " * new asciidoc flags"; \
+ echo "$$FLAGS" >GIT-ASCIIDOCFLAGS; \
+ fi
+
clean:
$(RM) *.xml *.xml+ *.html *.html+ *.1 *.5 *.7
$(RM) *.texi *.texi+ *.texi++ git.info gitman.info
$(RM) SubmittingPatches.txt
$(RM) $(cmds_txt) $(mergetools_txt) *.made
$(RM) manpage-base-url.xsl
+ $(RM) GIT-ASCIIDOCFLAGS
-$(MAN_HTML): %.html : %.txt asciidoc.conf
+$(MAN_HTML): %.html : %.txt asciidoc.conf asciidoctor-extensions.rb GIT-ASCIIDOCFLAGS
$(QUIET_ASCIIDOC)$(RM) $@+ $@ && \
$(TXT_TO_HTML) -d manpage -o $@+ $< && \
mv $@+ $@
-$(OBSOLETE_HTML): %.html : %.txto asciidoc.conf
+$(OBSOLETE_HTML): %.html : %.txto asciidoc.conf asciidoctor-extensions.rb GIT-ASCIIDOCFLAGS
$(QUIET_ASCIIDOC)$(RM) $@+ $@ && \
$(TXT_TO_HTML) -o $@+ $< && \
mv $@+ $@
manpage-base-url.xsl: manpage-base-url.xsl.in
$(QUIET_GEN)sed "s|@@MAN_BASE_URL@@|$(MAN_BASE_URL)|" $< > $@
-%.1 %.5 %.7 : %.xml manpage-base-url.xsl
+%.1 %.5 %.7 : %.xml manpage-base-url.xsl $(wildcard manpage*.xsl)
$(QUIET_XMLTO)$(RM) $@ && \
$(XMLTO) -m $(MANPAGE_XSL) $(XMLTO_EXTRA) man $<
-%.xml : %.txt asciidoc.conf
+%.xml : %.txt asciidoc.conf asciidoctor-extensions.rb GIT-ASCIIDOCFLAGS
$(QUIET_ASCIIDOC)$(RM) $@+ $@ && \
$(TXT_TO_XML) -d manpage -o $@+ $< && \
mv $@+ $@
-user-manual.xml: user-manual.txt user-manual.conf
+user-manual.xml: user-manual.txt user-manual.conf asciidoctor-extensions.rb GIT-ASCIIDOCFLAGS
$(QUIET_ASCIIDOC)$(RM) $@+ $@ && \
$(TXT_TO_XML) -d book -o $@+ $< && \
mv $@+ $@
$(QUIET_GEN)cd technical && '$(SHELL_PATH_SQ)' ./api-index.sh
technical/%.html: ASCIIDOC_EXTRA += -a git-relative-html-prefix=../
-$(patsubst %,%.html,$(API_DOCS) technical/api-index $(TECH_DOCS)): %.html : %.txt asciidoc.conf
+$(patsubst %,%.html,$(API_DOCS) technical/api-index $(TECH_DOCS)): %.html : %.txt \
+ asciidoc.conf GIT-ASCIIDOCFLAGS
$(QUIET_ASCIIDOC)$(TXT_TO_HTML) $*.txt
SubmittingPatches.txt: SubmittingPatches
WEBDOC_DEST = /pub/software/scm/git/docs
howto/%.html: ASCIIDOC_EXTRA += -a git-relative-html-prefix=../
-$(patsubst %.txt,%.html,$(wildcard howto/*.txt)): %.html : %.txt
+$(patsubst %.txt,%.html,$(wildcard howto/*.txt)): %.html : %.txt GIT-ASCIIDOCFLAGS
$(QUIET_ASCIIDOC)$(RM) $@+ $@ && \
sed -e '1,/^$$/d' $< | \
$(TXT_TO_HTML) - >$@+ && \
* The command line completion (in contrib/) has been taught to
complete more subcommand parameters.
+ * The final report from "git bisect" used to show the suspected
+ culprit using a raw "diff-tree", with which there is no output for
+ a merge commit. This has been updated to use a more modern and
+ human readable output that still is concise enough.
+
+ * "git rebase --rebase-merges" replaces its old "--preserve-merges"
+ option; the latter is now marked as deprecated.
+
+ * Error message given while cloning with --recurse-submodules has
+ been updated.
+
+ * The completion helper code now pays attention to repository-local
+ configuration (when available), which allows --list-cmds to honour
+ a repository specific setting of completion.commands, for example.
+
+ * "git mergetool" learned to offer Sublime Merge (smerge) as one of
+ its backends.
+
Performance, Internal Implementation, Development Support etc.
* "git prune" has been taught to take advantage of reachability
bitmap when able.
+ * The command line parser of "git commit-tree" has been rewritten to
+ use the parse-options API.
+
+ * Suggest GitGitGadget instead of submitGit as a way to submit
+ patches based on GitHub PR to us.
+
+ * The test framework has been updated to help developers by making it
+ easier to run most of the tests under different versions of
+ over-the-wire protocols.
+
+ * Dev support update to make it easier to compare two formatted
+ results from our documentation.
+
+ * The scripted "git rebase" implementation has been retired.
+
+ * "git multi-pack-index verify" did not scale well with the number of
+ packfiles, which is being improved.
+
+ * "git stash" has been rewritten in C.
+
+ * The "check-docs" Makefile target to support developers has been
+ updated.
+
+ * The tests have been updated not to rely on the abbreviated option
+ names the parse-options API offers, to protect us from an
+ abbreviated form of an option that used to be unique within the
+ command getting non-unique when a new option that share the same
+ prefix is added.
+
Fixes since v2.21
-----------------
ETAGS on.
(merge 92b88eba9f js/find-lib-h-with-ls-files-when-possible later to maint).
+ * "git rebase" that was reimplemented in C did not set ORIG_HEAD
+ correctly, which has been corrected.
+ (merge cbd29ead92 js/rebase-orig-head-fix later to maint).
+
+ * Dev support.
+ (merge f545737144 js/stress-test-ui-tweak later to maint).
+
+ * CFLAGS now can be tweaked when invoking Make while using
+ DEVELOPER=YesPlease; this did not work well before.
+ (merge 6d5d4b4e93 ab/makefile-help-devs-more later to maint).
+
+ * "git fsck --connectivity-only" omits computation necessary to sift
+ the objects that are not reachable from any of the refs into
+ unreachable and dangling. This is now enabled when dangling
+ objects are requested (which is done by default, but can be
+ overridden with the "--no-dangling" option).
+ (merge 8d8c2a5aef jk/fsck-doc later to maint).
+
+ * On platforms where "git fetch" is killed with SIGPIPE (e.g. OSX),
+ the upload-pack that runs on the other end that hangs up after
+ detecting an error could cause "git fetch" to die with a signal,
+ which led to a flakey test. "git fetch" now ignores SIGPIPE during
+ the network portion of its operation (this is not a problem as we
+ check the return status from our write(2)s).
+ (merge 143588949c jk/no-sigpipe-during-network-transport later to maint).
+
+ * A recent update broke "is this object available to us?" check for
+ well-known objects like an empty tree (which should yield "yes",
+ even when there is no on-disk object for an empty tree), which has
+ been corrected.
+ (merge f06ab027ef jk/virtual-objects-do-exist later to maint).
+
+ * The setup code has been cleaned up to avoid leaks around the
+ repository_format structure.
+ (merge e8805af1c3 ma/clear-repository-format later to maint).
+
+ * "git config --type=color ..." is meant to replace "git config --get-color"
+ but there is a slight difference that wasn't documented, which is
+ now fixed.
+ (merge cd8e7593b9 jk/config-type-color-ends-with-lf later to maint).
+
+ * When the "clean" filter can reduce the size of a huge file in the
+ working tree down to a small "token" (a la Git LFS), there is no
+ point in allocating a huge scratch area upfront, but the buffer is
+ sized based on the original file size. The convert mechanism now
+ allocates very minimum and reallocates as it receives the output
+ from the clean filter process.
+ (merge 02156ab031 jh/resize-convert-scratch-buffer later to maint).
+
+ * "git rebase" uses the refs/rewritten/ hierarchy to store its
+ intermediate states, which inherently makes the hierarchy per
+ worktree, but it didn't quite work well.
+ (merge b9317d55a3 nd/rewritten-ref-is-per-worktree later to maint).
+
+ * "git log -L<from>,<to>:<path>" with "-s" did not suppress the patch
+ output as it should. This has been corrected.
+ (merge 05314efaea jk/line-log-with-patch later to maint).
+
+ * "git worktree add" used to do a "find an available name with stat
+ and then mkdir", which is race-prone. This has been fixed by using
+ mkdir and reacting to EEXIST in a loop.
+ (merge 7af01f2367 ms/worktree-add-atomic-mkdir later to maint).
+
+ * Build update for SHA-1 with collision detection.
+ (merge 07a20f569b jk/sha1dc later to maint).
+
+ * Build procedure has been fixed around use of asciidoctor instead of
+ asciidoc.
+ (merge 185f9a0ea0 ma/asciidoctor-fixes later to maint).
+
+ * remote-http transport did not anonymize URLs reported in its error
+ messages at places.
+ (merge c1284b21f2 js/anonymize-remote-curl-diag later to maint).
+
+ * Error messages given from the http transport have been updated so
+ that they can be localized.
+ (merge ed8b4132c8 js/remote-curl-i18n later to maint).
+
+ * "git init" forgot to read platform-specific repository
+ configuration, which made Windows port to ignore settings of
+ core.hidedotfiles, for example.
+
+ * A corner-case object name ambiguity while the sequencer machinery
+ is working (e.g. "rebase -i -x") has been fixed.
+
+ * "git format-patch" did not diagnose an error while opening the
+ output file for the cover-letter, which has been corrected.
+ (merge 2fe95f494c jc/format-patch-error-check later to maint).
+
+ * "git checkout -f <branch>" while the index has an unmerged path
+ incorrectly left some paths in an unmerged state, which has been
+ corrected.
+
+ * A corner case bug in the refs API has been corrected.
+ (merge d3322eb28b jk/refs-double-abort later to maint).
+
+ * Unicode update.
+ (merge 584b62c37b bb/unicode-12 later to maint).
+
+ * dumb-http walker has been updated to share more error recovery
+ strategy with the normal codepath.
+
+ * A buglet in configuration parser has been fixed.
+ (merge 19e7fdaa58 nd/include-if-wildmatch later to maint).
+
+ * The documentation for "git read-tree --reset -u" has been updated.
+ (merge b5a0bd694c nd/read-tree-reset-doc later to maint).
+
* Code cleanup, docfix, build fix, etc.
(merge 11f470aee7 jc/test-yes-doc later to maint).
(merge 90503a240b js/doc-symref-in-proto-v1 later to maint).
(merge 716a5af812 rd/gc-prune-doc-fix later to maint).
(merge 50b206371d js/untravis-windows later to maint).
(merge dbf47215e3 js/rebase-recreate-merge later to maint).
+ (merge 56cb2d30f8 dl/reset-doc-no-wrt-abbrev later to maint).
+ (merge 64eca306a2 ja/dir-rename-doc-markup-fix later to maint).
+ (merge af91b0230c dl/ignore-docs later to maint).
+ (merge 59a06e947b ra/t3600-test-path-funcs later to maint).
+ (merge e041d0781b ar/t4150-remove-cruft later to maint).
+ (merge 8d75a1d183 ma/asciidoctor-fixes-more later to maint).
+ (merge 74cc547b0f mh/pack-protocol-doc-fix later to maint).
+ (merge ed31851fa6 ab/doc-misc-typofixes later to maint).
+ (merge a7256debd4 nd/checkout-m-doc-update later to maint).
+ (merge 3a9e1ad78d jt/t5551-protocol-v2-does-not-have-half-auth later to maint).
+ (merge 0b918b75af sg/t5318-cleanup later to maint).
+ (merge 68ed71b53c cb/doco-mono later to maint).
+ (merge a34dca2451 nd/interpret-trailers-docfix later to maint).
Some parts of the system have dedicated maintainers with their own
repositories.
-- 'git-gui/' comes from git-gui project, maintained by Pat Thoyts:
+- `git-gui/` comes from git-gui project, maintained by Pat Thoyts:
git://repo.or.cz/git-gui.git
-- 'gitk-git/' comes from Paul Mackerras's gitk project:
+- `gitk-git/` comes from Paul Mackerras's gitk project:
git://ozlabs.org/~paulus/gitk
-- 'po/' comes from the localization coordinator, Jiang Xin:
+- `po/` comes from the localization coordinator, Jiang Xin:
https://github.com/git-l10n/git-po/
def process(parent, target, attrs)
if parent.document.basebackend? 'html'
prefix = parent.document.attr('git-relative-html-prefix')
- %(<a href="#{prefix}#{target}.html">#{target}(#{attrs[1]})</a>\n)
+ %(<a href="#{prefix}#{target}.html">#{target}(#{attrs[1]})</a>)
elsif parent.document.basebackend? 'docbook'
"<citerefentry>\n" \
"<refentrytitle>#{target}</refentrytitle>" \
"<manvolnum>#{attrs[1]}</manvolnum>\n" \
- "</citerefentry>\n"
+ "</citerefentry>"
end
end
end
so that the local merge commits are included in the rebase (see
linkgit:git-rebase[1] for details).
+
-When preserve, also pass `--preserve-merges` along to 'git rebase'
-so that locally committed merge commits will not be flattened
-by running 'git pull'.
+When `preserve` (deprecated in favor of `merges`), also pass
+`--preserve-merges` along to 'git rebase' so that locally committed merge
+commits will not be flattened by running 'git pull'.
+
When the value is `interactive`, the rebase is run in interactive mode.
+
core.excludesFile::
Specifies the pathname to the file that contains patterns to
describe paths that are not meant to be tracked, in addition
- to '.gitignore' (per-directory) and '.git/info/exclude'.
+ to `.gitignore` (per-directory) and `.git/info/exclude`.
Defaults to `$XDG_CONFIG_HOME/git/ignore`.
If `$XDG_CONFIG_HOME` is either not set or empty, `$HOME/.config/git/ignore`
is used instead. See linkgit:gitignore[5].
command-line argument and write the password on its STDOUT.
core.attributesFile::
- In addition to '.gitattributes' (per-directory) and
- '.git/info/attributes', Git looks into this file for attributes
+ In addition to `.gitattributes` (per-directory) and
+ `.git/info/attributes`, Git looks into this file for attributes
(see linkgit:gitattributes[5]). Path expansions are made the same
way as for `core.excludesFile`. Its default value is
`$XDG_CONFIG_HOME/git/attributes`. If `$XDG_CONFIG_HOME` is either not
core.hooksPath::
By default Git will look for your hooks in the
- '$GIT_DIR/hooks' directory. Set this to different path,
- e.g. '/etc/git/hooks', and Git will try to find your hooks in
- that directory, e.g. '/etc/git/hooks/pre-receive' instead of
- in '$GIT_DIR/hooks/pre-receive'.
+ `$GIT_DIR/hooks` directory. Set this to different path,
+ e.g. `/etc/git/hooks`, and Git will try to find your hooks in
+ that directory, e.g. `/etc/git/hooks/pre-receive` instead of
+ in `$GIT_DIR/hooks/pre-receive`.
+
The path can be either absolute or relative. A relative path is
taken as relative to the directory where the hooks are run (see
diff.dirstat::
A comma separated list of `--dirstat` parameters specifying the
- default behavior of the `--dirstat` option to linkgit:git-diff[1]`
+ default behavior of the `--dirstat` option to linkgit:git-diff[1]
and friends. The defaults can be overridden on the command line
(using `--dirstat=<param1,param2,...>`). The fallback defaults
(when not changed by `diff.dirstat`) are `changes,noncumulative,3`.
environment variable. The command is called with parameters
as described under "git Diffs" in linkgit:git[1]. Note: if
you want to use an external diff program only on a subset of
- your files, you might want to use linkgit:gitattributes[5] instead.
+ your files, you might want to use linkgit:gitattributes[5] instead.
diff.ignoreSubmodules::
Sets the default value of --ignore-submodules. Note that this
vice versa by configuring the `fsck.<msg-id>` setting where the
`<msg-id>` is the fsck message ID and the value is one of `error`,
`warn` or `ignore`. For convenience, fsck prefixes the error/warning
-with the message ID, e.g. "missingEmail: invalid author/committer line
-- missing email" means that setting `fsck.missingEmail = ignore` will
-hide that issue.
+with the message ID, e.g. "missingEmail: invalid author/committer
+line - missing email" means that setting `fsck.missingEmail = ignore`
+will hide that issue.
+
In general, it is better to enumerate existing objects with problems
with `fsck.skipList`, instead of listing the kind of breakages these
When there are more than this many packs that are not
marked with `*.keep` file in the repository, `git gc
--auto` consolidates them into one larger pack. The
- default value is 50. Setting this to 0 disables it.
+ default value is 50. Setting this to 0 disables it.
gc.autoDetach::
Make `git gc --auto` return immediately and run in background
gpg.<format>.program::
Use this to customize the program used for the signing format you
chose. (see `gpg.program` and `gpg.format`) `gpg.program` can still
- be used as a legacy synonym for `gpg.openpgp.program`. The default
+ be used as a legacy synonym for `gpg.openpgp.program`. The default
value for `gpg.x509.program` is "gpgsm".
so that the local merge commits are included in the rebase (see
linkgit:git-rebase[1] for details).
+
-When preserve, also pass `--preserve-merges` along to 'git rebase'
-so that locally committed merge commits will not be flattened
-by running 'git pull'.
+When `preserve` (deprecated in favor of `merges`), also pass
+`--preserve-merges` along to 'git rebase' so that locally committed merge
+commits will not be flattened by running 'git pull'.
+
When the value is `interactive`, the rebase is run in interactive mode.
+
rebase.useBuiltin::
- Set to `false` to use the legacy shellscript implementation of
- linkgit:git-rebase[1]. Is `true` by default, which means use
- the built-in rewrite of it in C.
-+
-The C rewrite is first included with Git version 2.20. This option
-serves an an escape hatch to re-enable the legacy version in case any
-bugs are found in the rewrite. This option and the shellscript version
-of linkgit:git-rebase[1] will be removed in some future release.
-+
-If you find some reason to set this option to `false` other than
-one-off testing you should report the behavior difference as a bug in
-git.
+ Unused configuration variable. Used in Git versions 2.20 and
+ 2.21 as an escape hatch to enable the legacy shellscript
+ implementation of rebase. Now the built-in rewrite of it in C
+ is always used. Setting this will emit a warning, to alert any
+ remaining users that setting this now does nothing.
rebase.stat::
Whether to show a diffstat of what changed upstream since the last
--binary::
In addition to `--full-index`, output a binary diff that
- can be applied with `git-apply`.
+ can be applied with `git-apply`. Implies `--patch`.
--abbrev[=<n>]::
Instead of showing the full 40-byte hexadecimal object
doc-diff [options] <from> <to> [-- <diff-options>]
doc-diff (-c|--clean)
--
-j=n parallel argument to pass to make
-f force rebuild; do not rely on cached results
-c,clean cleanup temporary working files
+j=n parallel argument to pass to make
+f force rebuild; do not rely on cached results
+c,clean cleanup temporary working files
+from-asciidoc use asciidoc with the 'from'-commit
+from-asciidoctor use asciidoctor with the 'from'-commit
+asciidoc use asciidoc with both commits
+to-asciidoc use asciidoc with the 'to'-commit
+to-asciidoctor use asciidoctor with the 'to'-commit
+asciidoctor use asciidoctor with both commits
+cut-header-footer cut away header and footer
"
SUBDIRECTORY_OK=1
. "$(git --exec-path)/git-sh-setup"
parallel=
force=
clean=
+from_program=
+to_program=
+cut_header_footer=
while test $# -gt 0
do
case "$1" in
clean=t ;;
-f)
force=t ;;
+ --from-asciidoctor)
+ from_program=-asciidoctor ;;
+ --to-asciidoctor)
+ to_program=-asciidoctor ;;
+ --asciidoctor)
+ from_program=-asciidoctor
+ to_program=-asciidoctor ;;
+ --from-asciidoc)
+ from_program=-asciidoc ;;
+ --to-asciidoc)
+ to_program=-asciidoc ;;
+ --asciidoc)
+ from_program=-asciidoc
+ to_program=-asciidoc ;;
+ --cut-header-footer)
+ cut_header_footer=-cut-header-footer ;;
--)
shift; break ;;
*)
ln -s "$dots/config.mak" "$tmp/worktree/config.mak"
fi
+construct_makemanflags () {
+ if test "$1" = "-asciidoc"
+ then
+ echo USE_ASCIIDOCTOR=
+ elif test "$1" = "-asciidoctor"
+ then
+ echo USE_ASCIIDOCTOR=YesPlease
+ fi
+}
+
+from_makemanflags=$(construct_makemanflags "$from_program") &&
+to_makemanflags=$(construct_makemanflags "$to_program") &&
+
+from_dir=$from_oid$from_program$cut_header_footer &&
+to_dir=$to_oid$to_program$cut_header_footer &&
+
# generate_render_makefile <srcdir> <dstdir>
generate_render_makefile () {
find "$1" -type f |
done
}
-# render_tree <committish_oid>
+# render_tree <committish_oid> <directory_name> <makemanflags>
render_tree () {
# Skip install-man entirely if we already have an installed directory.
# We can't rely on make here, since "install-man" unconditionally
# we then can't rely on during the render step). We use "mv" to make
# sure we don't get confused by a previous run that failed partway
# through.
- if ! test -d "$tmp/installed/$1"
+ oid=$1 &&
+ dname=$2 &&
+ makemanflags=$3 &&
+ if ! test -d "$tmp/installed/$dname"
then
- git -C "$tmp/worktree" checkout --detach "$1" &&
+ git -C "$tmp/worktree" checkout --detach "$oid" &&
make -j$parallel -C "$tmp/worktree" \
+ $makemanflags \
GIT_VERSION=omitted \
SOURCE_DATE_EPOCH=0 \
- DESTDIR="$tmp/installed/$1+" \
+ DESTDIR="$tmp/installed/$dname+" \
install-man &&
- mv "$tmp/installed/$1+" "$tmp/installed/$1"
+ mv "$tmp/installed/$dname+" "$tmp/installed/$dname"
fi &&
# As with "installed" above, we skip the render if it's already been
# done. So using make here is primarily just about running in
# parallel.
- if ! test -d "$tmp/rendered/$1"
+ if ! test -d "$tmp/rendered/$dname"
then
- generate_render_makefile "$tmp/installed/$1" "$tmp/rendered/$1+" |
+ generate_render_makefile "$tmp/installed/$dname" \
+ "$tmp/rendered/$dname+" |
make -j$parallel -f - &&
- mv "$tmp/rendered/$1+" "$tmp/rendered/$1"
+ mv "$tmp/rendered/$dname+" "$tmp/rendered/$dname"
+
+ if test "$cut_header_footer" = "-cut-header-footer"
+ then
+ for f in $(find "$tmp/rendered/$dname" -type f)
+ do
+ tail -n +3 "$f" | head -n -2 |
+ sed -e '1{/^$/d}' -e '${/^$/d}' >"$f+" &&
+ mv "$f+" "$f" ||
+ return 1
+ done
+ fi
fi
}
-render_tree $from_oid &&
-render_tree $to_oid &&
-git -C $tmp/rendered diff --no-index "$@" $from_oid $to_oid
+render_tree $from_oid $from_dir $from_makemanflags &&
+render_tree $to_oid $to_dir $to_makemanflags &&
+git -C $tmp/rendered diff --no-index "$@" $from_dir $to_dir
for command-line options).
-CONFIGURATION
--------------
-
-The optional configuration variable `core.excludesFile` indicates a path to a
-file containing patterns of file names to exclude from git-add, similar to
-$GIT_DIR/info/exclude. Patterns in the exclude file are used in addition to
-those in info/exclude. See linkgit:gitignore[5].
-
-
EXAMPLES
--------
am.threeWay configuration variable. For more information,
see am.threeWay in linkgit:git-config[1].
+--rerere-autoupdate::
+--no-rerere-autoupdate::
+ Allow the rerere mechanism to update the index with the
+ result of auto-conflict resolution if possible.
+
--ignore-space-change::
--ignore-whitespace::
--whitespace=<option>::
+
When checking out paths from the index, this option lets you recreate
the conflicted merge in the specified paths.
++
+When switching branches with `--merge`, staged changes may be lost.
--conflict=<style>::
The same as --merge option above, but changes the way the
Pass the merge strategy-specific option through to the
merge strategy. See linkgit:git-merge[1] for details.
+--rerere-autoupdate::
+--no-rerere-autoupdate::
+ Allow the rerere mechanism to update the index with the
+ result of auto-conflict resolution if possible.
+
SEQUENCER SUBCOMMANDS
---------------------
include::sequencer.txt[]
-e <pattern>::
--exclude=<pattern>::
- In addition to those found in .gitignore (per directory) and
- $GIT_DIR/info/exclude, also consider these patterns to be in the
- set of the ignore rules in effect.
+ Use the given exclude pattern in addition to the standard ignore rules
+ (see linkgit:gitignore[5]).
-x::
- Don't use the standard ignore rules read from .gitignore (per
- directory) and $GIT_DIR/info/exclude, but do still use the ignore
- rules given with `-e` options. This allows removing all untracked
+ Don't use the standard ignore rules (see linkgit:gitignore[5]), but
+ still use the ignore rules given with `-e` options from the command
+ line. This allows removing all untracked
files, including build products. This can be used (possibly in
conjunction with 'git reset') to create a pristine
working directory to test a clean build.
emits the new commit object id on stdout. The log message is read
from the standard input, unless `-m` or `-F` options are given.
+The `-m` and `-F` options can be given any number of times, in any
+order. The commit log message will be composed in the order in which
+the options are given.
+
A commit object may have any number of parents. With exactly one
parent, it is an ordinary commit. Having more than one parent makes
the commit a merge between several lines of history. Initial (root)
OPTIONS
-------
<tree>::
- An existing tree object
+ An existing tree object.
-p <parent>::
Each `-p` indicates the id of a parent commit object.
-F <file>::
Read the commit log message from the given file. Use `-` to read
- from the standard input.
+ from the standard input. This can be given more than once and the
+ content of each file becomes its own paragraph.
-S[<keyid>]::
--gpg-sign[=<keyid>]::
--local::
For writing options: write to the repository `.git/config` file.
- This is the default behavior.
+ This is the default behavior.
+
For reading options: read only from the repository `.git/config` rather than
from all available files.
output. The optional `default` parameter is used instead, if
there is no color configured for `name`.
+
-`--type=color [--default=<default>]` is preferred over `--get-color`.
+`--type=color [--default=<default>]` is preferred over `--get-color`
+(but note that `--get-color` will omit the trailing newline printed by
+`--type=color`).
-e::
--edit::
This is sort of "Git root" - if you run 'git daemon' with
'--base-path=/srv/git' on example.com, then if you later try to pull
'git://example.com/hello.git', 'git daemon' will interpret the path
- as '/srv/git/hello.git'.
+ as `/srv/git/hello.git`.
--base-path-relaxed::
If --base-path is enabled and repo lookup fails, with this option
include::pretty-formats.txt[]
+
include::diff-format.txt[]
GIT
rewriting. When applying a tree filter, the command needs to
temporarily check out the tree to some directory, which may consume
considerable space in case of large projects. By default it
- does this in the '.git-rewrite/' directory but you can override
+ does this in the `.git-rewrite/` directory but you can override
that choice by this parameter.
-f::
with --no-full.
--connectivity-only::
- Check only the connectivity of tags, commits and tree objects. By
- avoiding to unpack blobs, this speeds up the operation, at the
- expense of missing corrupt objects or other problematic issues.
+ Check only the connectivity of reachable objects, making sure
+ that any objects referenced by a reachable tag, commit, or tree
+ is present. This speeds up the operation by avoiding reading
+ blobs entirely (though it does still check that referenced blobs
+ exist). This will detect corruption in commits and trees, but
+ not do any semantic checks (e.g., for format errors). Corruption
+ in blob objects will not be detected at all.
++
+Unreachable tags, commits, and trees will also be accessed to find the
+tips of dangling segments of history. Use `--no-dangling` if you don't
+care about this output and want to speed it up further.
--strict::
Enable more strict checking, namely to catch a file mode
mechanism. Only useful with `--untracked`.
--exclude-standard::
- Do not pay attention to ignored files specified via the `.gitignore`
+ Do not pay attention to ignored files specified via the `.gitignore`
mechanism. Only useful when searching files in the current directory
with `--no-index`.
already opened konqueror in a new tab if possible.
For consistency, we also try such a trick if 'man.konqueror.path' is
-set to something like 'A_PATH_TO/konqueror'. That means we will try to
-launch 'A_PATH_TO/kfmclient' instead.
+set to something like `A_PATH_TO/konqueror`. That means we will try to
+launch `A_PATH_TO/kfmclient` instead.
If you really want to use 'konqueror', then you can use something like
the following:
Accelerated static Apache 2.x::
Similar to the above, but Apache can be used to return static
- files that are stored on disk. On many systems this may
+ files that are stored on disk. On many systems this may
be more efficient as Apache can ask the kernel to copy the
file contents from the file system directly to the network:
+
NAME
----
-git-interpret-trailers - add or parse structured information in commit messages
+git-interpret-trailers - Add or parse structured information in commit messages
SYNOPSIS
--------
displayed.
--refs::
- Do not show peeled tags or pseudorefs like HEAD in the output.
+ Do not show peeled tags or pseudorefs like `HEAD` in the output.
-q::
--quiet::
taken as relative to the current working directory. E.g. when you are
in a directory 'sub' that has a directory 'dir', you can run 'git
ls-tree -r HEAD dir' to list the contents of the tree (that is
- 'sub/dir' in `HEAD`). You don't want to give a tree that is not at the
+ `sub/dir` in `HEAD`). You don't want to give a tree that is not at the
root level (e.g. `git ls-tree -r HEAD:sub dir`) in this case, as that
- would result in asking for 'sub/sub/dir' in the `HEAD` commit.
+ would result in asking for `sub/sub/dir` in the `HEAD` commit.
However, the current working directory can be ignored by passing
--full-tree option.
If `--log` is specified, a shortlog of the commits being merged
will be appended to the specified message.
---[no-]rerere-autoupdate::
+--rerere-autoupdate::
+--no-rerere-autoupdate::
Allow the rerere mechanism to update the index with the
result of auto-conflict resolution if possible.
-C <object>::
--reuse-message=<object>::
- Take the given blob object (for example, another note) as the
+ Take the given blob object (for example, another note) as the
note message. (Use `git notes copy <object>` instead to
copy notes between objects.)
started.
--reset::
- Same as -m, except that unmerged entries are discarded
- instead of failing.
+ Same as -m, except that unmerged entries are discarded instead
+ of failing. When used with `-u`, updates leading to loss of
+ working tree changes will not abort the operation.
-u::
After a successful merge, update the files in the work
+
See also INCOMPATIBLE OPTIONS below.
+--rerere-autoupdate::
+--no-rerere-autoupdate::
+ Allow the rerere mechanism to update the index with the
+ result of auto-conflict resolution if possible.
+
-S[<keyid>]::
--gpg-sign[=<keyid>]::
GPG-sign commits. The `keyid` argument is optional and
the `rebase-cousins` mode is turned on, such commits are instead rebased
onto `<upstream>` (or `<onto>`, if specified).
+
-The `--rebase-merges` mode is similar in spirit to `--preserve-merges`, but
-in contrast to that option works well in interactive rebases: commits can be
-reordered, inserted and dropped at will.
+The `--rebase-merges` mode is similar in spirit to the deprecated
+`--preserve-merges`, but in contrast to that option works well in interactive
+rebases: commits can be reordered, inserted and dropped at will.
+
It is currently only possible to recreate the merge commits using the
`recursive` merge strategy; Different merge strategies can be used only via
-p::
--preserve-merges::
- Recreate merge commits instead of flattening the history by replaying
- commits a merge commit introduces. Merge conflict resolutions or manual
- amendments to merge commits are not preserved.
+ [DEPRECATED: use `--rebase-merges` instead] Recreate merge commits
+ instead of flattening the history by replaying commits a merge commit
+ introduces. Merge conflict resolutions or manual amendments to merge
+ commits are not preserved.
+
This uses the `--interactive` machinery internally, but combining it
with the `--interactive` option explicitly is generally not a good
BUGS
----
-The todo list presented by `--preserve-merges --interactive` does not
-represent the topology of the revision graph. Editing commits and
-rewording their commit messages should work fine, but attempts to
-reorder commits tend to produce counterintuitive results. Use
-`--rebase-merges` in such scenarios instead.
+The todo list presented by the deprecated `--preserve-merges --interactive`
+does not represent the topology of the revision graph (use `--rebase-merges`
+instead). Editing commits and rewording their commit messages should work
+fine, but attempts to reorder commits tend to produce counterintuitive results.
+Use `--rebase-merges` in such scenarios instead.
For example, an attempt to rearrange
------------
link-level address).
"ext::git-server-alias foo %G/repo% with% spaces %Vfoo"::
- Represents a repository with path '/repo with spaces' accessed
+ Represents a repository with path `/repo with spaces` accessed
using the helper program "git-server-alias foo". The hostname for
the remote server passed in the protocol stream will be "foo"
(this allows multiple virtual Git servers to share a
SEE ALSO
--------
-linkgit:gitremote-helpers[1]
+linkgit:gitremote-helpers[7]
GIT
---
SEE ALSO
--------
-linkgit:gitremote-helpers[1]
+linkgit:gitremote-helpers[7]
GIT
---
git-remote-helpers
==================
-This document has been moved to linkgit:gitremote-helpers[1].
+This document has been moved to linkgit:gitremote-helpers[7].
Please let the owners of the referring site know so that they can update the
link you clicked to get here.
+++ /dev/null
-git-remote-testgit(1)
-=====================
-
-NAME
-----
-git-remote-testgit - Example remote-helper
-
-
-SYNOPSIS
---------
-[verse]
-git clone testgit::<source-repo> [<destination>]
-
-DESCRIPTION
------------
-
-This command is a simple remote-helper, that is used both as a
-testcase for the remote-helper functionality, and as an example to
-show remote-helper authors one possible implementation.
-
-The best way to learn more is to read the comments and source code in
-'git-remote-testgit'.
-
-SEE ALSO
---------
-linkgit:gitremote-helpers[1]
-
-GIT
----
-Part of the linkgit:git[1] suite
`reset --merge` is meant to be used when resetting out of a conflicted
merge. Any mergy operation guarantees that the working tree file that is
-involved in the merge does not have local change wrt the index before
-it starts, and that it writes the result out to the working tree. So if
+involved in the merge does not have a local change with respect to the index
+before it starts, and that it writes the result out to the working tree. So if
we see some difference between the index and the target and also
between the index and the working tree, then it means that we are not
resetting out from a state that a mergy operation left after failing
Pass the merge strategy-specific option through to the
merge strategy. See linkgit:git-merge[1] for details.
+--rerere-autoupdate::
+--no-rerere-autoupdate::
+ Allow the rerere mechanism to update the index with the
+ result of auto-conflict resolution if possible.
+
SEQUENCER SUBCOMMANDS
---------------------
include::sequencer.txt[]
--------
[verse]
'git stash' list [<options>]
-'git stash' show [<stash>]
+'git stash' show [<options>] [<stash>]
'git stash' drop [-q|--quiet] [<stash>]
'git stash' ( pop | apply ) [--index] [-q|--quiet] [<stash>]
'git stash' branch <branchname> [<stash>]
The command takes options applicable to the 'git log'
command to control what is shown and how. See linkgit:git-log[1].
-show [<stash>]::
+show [<options>] [<stash>]::
Show the changes recorded in the stash entry as a diff between the
stashed contents and the commit back when the stash entry was first
command line arguments. Parsers should ignore headers they
don't recognize.
-### Branch Headers
+Branch Headers
+^^^^^^^^^^^^^^
If `--branch` is given, a series of header lines are printed with
information about the current branch.
------------------------------------------------------------
....
-### Changed Tracked Entries
+Changed Tracked Entries
+^^^^^^^^^^^^^^^^^^^^^^^
Following the headers, a series of lines are printed for tracked
entries. One of three different line formats may be used to describe
--------------------------------------------------------
....
-### Other Items
+Other Items
+^^^^^^^^^^^
Following the tracked entries (and if requested), a series of
lines will be printed for untracked and then ignored items
! <path>
-### Pathname Format Notes and -z
+Pathname Format Notes and -z
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
When the `-z` option is given, pathnames are printed as is and
without any quoting and lines are terminated with a NUL (ASCII 0x00)
or ../), the location relative to the superproject's default remote
repository (Please note that to specify a repository 'foo.git'
which is located right next to a superproject 'bar.git', you'll
-have to use '../foo.git' instead of './foo.git' - as one might expect
+have to use `../foo.git` instead of `./foo.git` - as one might expect
when following the rules for relative URLs - because the evaluation
of relative URLs in Git is identical to that of relative directories).
+
command-line argument.
+
This automatically updates the rev_map if needed (see
-'$GIT_DIR/svn/\*\*/.rev_map.*' in the FILES section below for details).
+'$GIT_DIR/svn/\**/.rev_map.*' in the FILES section below for details).
--localtime;;
Store Git commit times in the local time zone instead of UTC. This
and have no uncommitted changes.
+
This automatically updates the rev_map if needed (see
-'$GIT_DIR/svn/\*\*/.rev_map.*' in the FILES section below for details).
+'$GIT_DIR/svn/\**/.rev_map.*' in the FILES section below for details).
-l;;
--local;;
way to repair the repo is to use 'reset'.
+
Only the rev_map and refs/remotes/git-svn are changed (see
-'$GIT_DIR/svn/\*\*/.rev_map.*' in the FILES section below for details).
+'$GIT_DIR/svn/\**/.rev_map.*' in the FILES section below for details).
Follow 'reset' with a 'fetch' and then 'git reset' or 'git rebase' to
move local branches onto the new tree.
+
This option can only be used for one-shot imports as 'git svn'
will not be able to fetch again without metadata. Additionally,
-if you lose your '$GIT_DIR/svn/\*\*/.rev_map.*' files, 'git svn' will not
+if you lose your '$GIT_DIR/svn/\**/.rev_map.*' files, 'git svn' will not
be able to rebuild them.
+
The 'git svn log' command will not work on repositories using
FILES
-----
-$GIT_DIR/svn/\*\*/.rev_map.*::
+$GIT_DIR/svn/\**/.rev_map.*::
Mapping between Subversion revision numbers and Git commit
names. In a repository where the noMetadata option is not set,
this can be rebuilt from the git-svn-id: lines that are at the
man page on an already opened konqueror in a new tab if possible.
For consistency, we also try such a trick if 'browser.konqueror.path' is
-set to something like 'A_PATH_TO/konqueror'. That means we will try to
-launch 'A_PATH_TO/kfmclient' instead.
+set to something like `A_PATH_TO/konqueror`. That means we will try to
+launch `A_PATH_TO/kfmclient` instead.
If you really want to use 'konqueror', then you can use something like
the following:
In general, all pseudo refs are per working tree and all refs starting
with "refs/" are shared. Pseudo refs are ones like HEAD which are
-directly under GIT_DIR instead of inside GIT_DIR/refs. There are one
+directly under GIT_DIR instead of inside GIT_DIR/refs. There is one
exception to this: refs inside refs/bisect and refs/worktree is not
shared.
The command-line parameters passed to the configured command are
determined by the ssh variant. See `ssh.variant` option in
linkgit:git-config[1] for details.
-
+
`$GIT_SSH_COMMAND` takes precedence over `$GIT_SSH`, and is interpreted
by the shell, which allows additional arguments to be included.
Each line in `gitattributes` file is of form:
- pattern attr1 attr2 ...
+ pattern attr1 attr2 ...
That is, a pattern followed by an attributes list,
separated by whitespaces. Leading and trailing whitespaces are
support will checkout `foo.ps1` as UTF-8 encoded file. This will
typically cause trouble for the users of this file.
+
-If a Git client, that does not support the `working-tree-encoding`
-attribute, adds a new file `bar.ps1`, then `bar.ps1` will be
+If a Git client that does not support the `working-tree-encoding`
+attribute adds a new file `bar.ps1`, then `bar.ps1` will be
stored "as-is" internally (in this example probably as UTF-16).
A client with `working-tree-encoding` support will interpret the
internal contents as UTF-8 and try to convert it to UTF-16 on checkout.
from standard input. Exiting with non-zero status from this script prevent
`git-p4 submit` from launching. Run `git-p4 submit --help` for details.
+post-index-change
+~~~~~~~~~~~~~~~~~
+
+This hook is invoked when the index is written in read-cache.c
+do_write_locked_index.
+
+The first parameter passed to the hook is the indicator for the
+working directory being updated. "1" meaning working directory
+was updated or "0" when the working directory was not updated.
+
+The second parameter passed to the hook is the indicator for whether
+or not the index was updated and the skip-worktree bit could have
+changed. "1" meaning skip-worktree bits could have been updated
+and "0" meaning they were not.
+
+Only one parameter should be set to "1" when the hook runs. The hook
+running passing "1", "1" should not be possible.
+
GIT
---
Part of the linkgit:git[1] suite
- Other consecutive asterisks are considered regular asterisks and
will match according to the previous rules.
+CONFIGURATION
+-------------
+
+The optional configuration variable `core.excludesFile` indicates a path to a
+file containing patterns of file names to exclude, similar to
+`$GIT_DIR/info/exclude`. Patterns in the exclude file are used in addition to
+those in `$GIT_DIR/info/exclude`.
+
NOTES
-----
-----
User configuration and preferences are stored at:
-* '$XDG_CONFIG_HOME/git/gitk' if it exists, otherwise
-* '$HOME/.gitk' if it exists
+* `$XDG_CONFIG_HOME/git/gitk` if it exists, otherwise
+* `$HOME/.gitk` if it exists
-If neither of the above exist then '$XDG_CONFIG_HOME/git/gitk' is created and
+If neither of the above exist then `$XDG_CONFIG_HOME/git/gitk` is created and
used by default. If '$XDG_CONFIG_HOME' is not set it defaults to
-'$HOME/.config' in all cases.
+`$HOME/.config` in all cases.
History
-------
This defines two submodules, `libfoo` and `libbar`. These are expected to
-be checked out in the paths 'include/foo' and 'include/bar', and for both
+be checked out in the paths `include/foo` and `include/bar`, and for both
submodules a URL is specified which can be used for cloning the submodules.
SEE ALSO
-gitremote-helpers(1)
+gitremote-helpers(7)
====================
NAME
'option dry-run' {'true'|'false'}:
If true, pretend the operation completed successfully,
- but don't actually change any repository data. For most
+ but don't actually change any repository data. For most
helpers this only applies to the 'push', if supported.
'option servpath <c-style-quoted-path>'::
linkgit:git-remote-fd[1]
-linkgit:git-remote-testgit[1]
-
linkgit:git-fast-import[1]
GIT
to the object database, not to the repository!) in your
alternates file, but it will not work if you use absolute
paths unless the absolute path in filesystem and web URL
- is the same. See also 'objects/info/http-alternates'.
+ is the same. See also `objects/info/http-alternates`.
objects/info/http-alternates::
This file records URLs to alternate object stores that
* built-in values (some set during build stage),
* common system-wide configuration file (defaults to
- '/etc/gitweb-common.conf'),
+ `/etc/gitweb-common.conf`),
* either per-instance configuration file (defaults to 'gitweb_config.perl'
in the same directory as the installed gitweb), or if it does not exists
- then fallback system-wide configuration file (defaults to '/etc/gitweb.conf').
+ then fallback system-wide configuration file (defaults to `/etc/gitweb.conf`).
Values obtained in later configuration files override values obtained earlier
in the above sequence.
subroutine. For example, one might want to put gitweb configuration
related to access control for viewing repositories via Gitolite (one
of Git repository management tools) in a separate file, e.g. in
-'/etc/gitweb-gitolite.conf'. To include it, put
+`/etc/gitweb-gitolite.conf`. To include it, put
--------------------------------------------------
read_config_file("/etc/gitweb-gitolite.conf");
http://git.example.com/gitweb.cgi/foo/bar.git
------------------------------------------------
+
-will map to the path '/srv/git/foo/bar.git' on the filesystem.
+will map to the path `/srv/git/foo/bar.git` on the filesystem.
$projects_list::
Name of a plain text file listing projects, or a name of directory
$mimetypes_file::
File to use for (filename extension based) guessing of MIME types before
- trying '/etc/mime.types'. *NOTE* that this path, if relative, is taken
+ trying `/etc/mime.types`. *NOTE* that this path, if relative, is taken
as relative to the current Git repository, not to CGI script. If unset,
- only '/etc/mime.types' is used (if present on filesystem). If no mimetypes
+ only `/etc/mime.types` is used (if present on filesystem). If no mimetypes
file is found, mimetype guessing based on extension of file is disabled.
Unset by default.
+
This list should contain the URI of gitweb's standard stylesheet. The default
URI of gitweb stylesheet can be set at build time using the `GITWEB_CSS`
-makefile variable. Its default value is 'static/gitweb.css'
-(or 'static/gitweb.min.css' if the `CSSMIN` variable is defined,
+makefile variable. Its default value is `static/gitweb.css`
+(or `static/gitweb.min.css` if the `CSSMIN` variable is defined,
i.e. if CSS minifier is used during build).
+
*Note*: there is also a legacy `$stylesheet` configuration variable, which was
is displayed in the top right corner of each gitweb page and used as
a logo for the Atom feed. Relative to the base URI of gitweb (as a path).
Can be adjusted when building gitweb using `GITWEB_LOGO` variable
- By default set to 'static/git-logo.png'.
+ By default set to `static/git-logo.png`.
$favicon::
Points to the location where you put 'git-favicon.png' on your web
may display them in the browser's URL bar and next to the site name in
bookmarks. Relative to the base URI of gitweb. Can be adjusted at
build time using `GITWEB_FAVICON` variable.
- By default set to 'static/git-favicon.png'.
+ By default set to `static/git-favicon.png`.
$javascript::
Points to the location where you put 'gitweb.js' on your web server,
Relative to the base URI of gitweb. Can be set at build time using
the `GITWEB_JS` build-time configuration variable.
+
-The default value is either 'static/gitweb.js', or 'static/gitweb.min.js' if
+The default value is either `static/gitweb.js`, or `static/gitweb.min.js` if
the `JSMIN` build variable was defined, i.e. if JavaScript minifier was used
at build time. *Note* that this single file is generated from multiple
individual JavaScript "modules".
doesn't result in some other type; by default "text/plain".
Gitweb guesses mimetype of a file to display based on extension
of its filename, using `$mimetypes_file` (if set and file exists)
- and '/etc/mime.types' files (see *mime.types*(5) manpage; only
+ and `/etc/mime.types` files (see *mime.types*(5) manpage; only
filename extension rules are supported by gitweb).
$default_text_plain_charset::
(for example one for `git://` protocol, and one for `http://`
protocol).
+
-Note that per repository configuration can be set in '$GIT_DIR/cloneurl'
+Note that per repository configuration can be set in `$GIT_DIR/cloneurl`
file, or as values of multi-value `gitweb.url` configuration variable in
project config. Per-repository configuration takes precedence over value
composed from `@git_base_url_list` elements and project name.
If the server load exceeds this value then gitweb will return
"503 Service Unavailable" error. The server load is taken to be 0
if gitweb cannot determine its value. Currently it works only on Linux,
- where it uses '/proc/loadavg'; the load there is the number of active
+ where it uses `/proc/loadavg`; the load there is the number of active
tasks on the system -- processes that are actually running -- averaged
over the last minute.
+
$per_request_config::
If this is set to code reference, it will be run once for each request.
- You can set parts of configuration that change per session this way.
+ You can set parts of configuration that change per session this way.
For example, one might use the following code in a gitweb configuration
file
+
Only one provider at a time can be selected ('default' is one element list).
If an unknown provider is specified, the feature is disabled.
*Note* that some providers might require extra Perl packages to be
-installed; see 'gitweb/INSTALL' for more details.
+installed; see `gitweb/INSTALL` for more details.
+
This feature can be configured on a per-repository basis via
repository's `gitweb.avatar` configuration variable.
CONFIGURATION
-------------
Various aspects of gitweb's behavior can be controlled through the configuration
-file 'gitweb_config.perl' or '/etc/gitweb.conf'. See the linkgit:gitweb.conf[5]
+file `gitweb_config.perl` or `/etc/gitweb.conf`. See the linkgit:gitweb.conf[5]
for details.
Repositories
our $projectroot = '/path/to/parent/directory';
-----------------------------------------------------------------------
-The default value for `$projectroot` is '/pub/git'. You can change it during
+The default value for `$projectroot` is `/pub/git`. You can change it during
building gitweb via `GITWEB_PROJECTROOT` build configuration variable.
By default all Git repositories under `$projectroot` are visible and available
-------------------------------------------------------------------------------
+
from the template during repository creation, usually installed in
-'/usr/share/git-core/templates/'. You can use the `gitweb.description` repo
+`/usr/share/git-core/templates/`. You can use the `gitweb.description` repo
configuration variable, but the file takes precedence.
category (or `gitweb.category`)::
Apache as CGI
~~~~~~~~~~~~~
Apache must be configured to support CGI scripts in the directory in
-which gitweb is installed. Let's assume that it is '/var/www/cgi-bin'
+which gitweb is installed. Let's assume that it is `/var/www/cgi-bin`
directory.
-----------------------------------------------------------------------
(for mod_perl 1.x) or ModPerl::Registry (for mod_perl 2.x) to enable
this support.
-Assuming that gitweb is installed to '/var/www/perl', the following
+Assuming that gitweb is installed to `/var/www/perl`, the following
Apache configuration (for mod_perl 2.x) is suitable.
-----------------------------------------------------------------------
~~~~~~~~~~~~~~~~~~~
Gitweb works with Apache and FastCGI. First you need to rename, copy
or symlink gitweb.cgi to gitweb.fcgi. Let's assume that gitweb is
-installed in '/usr/share/gitweb' directory. The following Apache
+installed in `/usr/share/gitweb` directory. The following Apache
configuration is suitable (UNTESTED!)
-----------------------------------------------------------------------
-----------------------------------------------------------------------
The above configuration expects your public repositories to live under
-'/pub/git' and will serve them as `http://git.domain.org/dir-under-pub-git`,
+`/pub/git` and will serve them as `http://git.domain.org/dir-under-pub-git`,
both as clonable Git URL and as browseable gitweb interface. If you then
start your linkgit:git-daemon[1] with `--base-path=/pub/git --export-all`
then you can even use the `git://` URL with exactly the same path.
Setting the environment variable `GITWEB_CONFIG` will tell gitweb to use the
-named file (i.e. in this example '/etc/gitweb.conf') as a configuration for
+named file (i.e. in this example `/etc/gitweb.conf`) as a configuration for
gitweb. You don't really need it in above example; it is required only if
your configuration file is in different place than built-in (during
-compiling gitweb) 'gitweb_config.perl' or '/etc/gitweb.conf'. See
+compiling gitweb) 'gitweb_config.perl' or `/etc/gitweb.conf`. See
linkgit:gitweb.conf[5] for details, especially information about precedence
rules.
If you use the rewrite rules from the example you *might* also need
something like the following in your gitweb configuration file
-('/etc/gitweb.conf' following example):
+(`/etc/gitweb.conf` following example):
----------------------------------------------------------------------------
@stylesheets = ("/some/absolute/path/gitweb.css");
$my_uri = "/";
Here actual project root is passed to gitweb via `GITWEB_PROJECT_ROOT`
environment variable from a web server, so you need to put the following
-line in gitweb configuration file ('/etc/gitweb.conf' in above example):
+line in gitweb configuration file (`/etc/gitweb.conf` in above example):
--------------------------------------------------------------------------
$projectroot = $ENV{'GITWEB_PROJECTROOT'} || "/pub/git";
--------------------------------------------------------------------------
These configurations enable two things. First, each unix user (`<user>`) of
the server will be able to browse through gitweb Git repositories found in
-'~/public_git/' with the following url:
+`~/public_git/` with the following url:
http://git.example.org/~<user>/
use the \'~' as first character, just comment or remove the second rewrite
rule, and uncomment one of the following according to what you want.
-Second, repositories found in '/pub/scm/' and '/var/git/' will be accessible
+Second, repositories found in `/pub/scm/` and `/var/git/` will be accessible
through `http://git.example.org/scm/` and `http://git.example.org/var/`.
You can add as many project roots as you want by adding rewrite rules like
the third and the fourth.
http://git.example.com/project.git/shortlog/sometag
i.e. without 'gitweb.cgi' part, by using a configuration such as the
-following. This configuration assumes that '/var/www/gitweb' is the
+following. This configuration assumes that `/var/www/gitweb` is the
DocumentRoot of your webserver, contains the gitweb.cgi script and
complementary static files (stylesheet, favicon, JavaScript):
`@stylesheets`, `$my_uri` and `$home_link`, but you lose "dumb client"
access to your project .git dirs (described in "Single URL for gitweb and
for fetching" section). A possible workaround for the latter is the
-following: in your project root dir (e.g. '/pub/git') have the projects
-named *without* a .git extension (e.g. '/pub/git/project' instead of
-'/pub/git/project.git') and configure Apache as follows:
+following: in your project root dir (e.g. `/pub/git`) have the projects
+named *without* a .git extension (e.g. `/pub/git/project` instead of
+`/pub/git/project.git`) and configure Apache as follows:
----------------------------------------------------------------------------
<VirtualHost *:80>
ServerAlias git.example.com
will provide human-friendly gitweb access.
This solution is not 100% bulletproof, in the sense that if some project has
-a named ref (branch, tag) starting with 'git/', then paths such as
+a named ref (branch, tag) starting with `git/`, then paths such as
http://git.example.com/project/command/abranch..git/abranch
--------
linkgit:gitweb.conf[5], linkgit:git-instaweb[1]
-'gitweb/README', 'gitweb/INSTALL'
+`gitweb/README`, `gitweb/INSTALL`
GIT
---
origin/name-of-upstream-branch, which you can see using
`git branch -r`.
+[[def_overlay]]overlay::
+ Only update and add files to the working directory, but don't
+ delete them, similar to how 'cp -R' would update the contents
+ in the destination directory. This is the default mode in a
+ <<def_checkout,checkout>> when checking out files from the
+ <<def_index,index>> or a <<def_tree-ish,tree-ish>>. In
+ contrast, no-overlay mode also deletes tracked files not
+ present in the source, similar to 'rsync --delete'.
+
[[def_pack]]pack::
A set of objects which have been compressed into one file (to save space
or to transmit them efficiently).
--------------
If you have to access the WebDAV server from behind an HTTP(S) proxy,
-set the variable 'all_proxy' to 'http://proxy-host.com:port', or
-'http://login-on-proxy:passwd-on-proxy@proxy-host.com:port'. See 'man
+set the variable 'all_proxy' to `http://proxy-host.com:port`, or
+`http://login-on-proxy:passwd-on-proxy@proxy-host.com:port`. See 'man
curl' for details.
--filter-print-omitted::
Only useful with `--filter=`; prints a list of the objects omitted
- by the filter. Object IDs are prefixed with a ``~'' character.
+ by the filter. Object IDs are prefixed with a ``~'' character.
--missing=<missing-action>::
A debug option to help with future "partial clone" development.
author's). If `-local` is appended to the format (e.g.,
`iso-local`), the user's local time zone is used instead.
+
+--
`--date=relative` shows dates relative to the current time,
e.g. ``2 hours ago''. The `-local` option has no effect for
`--date=relative`.
-+
+
`--date=local` is an alias for `--date=default-local`.
-+
+
`--date=iso` (or `--date=iso8601`) shows timestamps in a ISO 8601-like format.
The differences to the strict ISO 8601 format are:
- a space between time and time zone
- no colon between hours and minutes of the time zone
-+
`--date=iso-strict` (or `--date=iso8601-strict`) shows timestamps in strict
ISO 8601 format.
-+
+
`--date=rfc` (or `--date=rfc2822`) shows timestamps in RFC 2822
format, often found in email messages.
-+
+
`--date=short` shows only the date, but not the time, in `YYYY-MM-DD` format.
-+
+
`--date=raw` shows the date as seconds since the epoch (1970-01-01
00:00:00 UTC), followed by a space, and then the timezone as an offset
from UTC (a `+` or `-` with four digits; the first two are hours, and
Note that the `-local` option does not affect the seconds-since-epoch
value (which is always measured in UTC), but does switch the accompanying
timezone value.
-+
+
`--date=human` shows the timezone if the timezone does not match the
current time-zone, and doesn't print the whole date if that matches
(ie skip printing year for dates that are "this year", but also skip
the whole date itself if it's in the last few days and we can just say
what weekday it was). For older dates the hour and minute is also
omitted.
-+
+
`--date=unix` shows the date as a Unix epoch timestamp (seconds since
1970). As with `--raw`, this is always in UTC and therefore `-local`
has no effect.
-+
+
`--date=format:...` feeds the format `...` to your system `strftime`,
except for %z and %Z, which are handled internally.
Use `--date=format:%c` to show the date in your system locale's
preferred format. See the `strftime` manual for a complete list of
format placeholders. When using `-local`, the correct syntax is
`--date=format-local:...`.
-+
+
`--date=default` is the default format, and is similar to
`--date=rfc2822`, with a few exceptions:
-
+--
- there is no comma after the day-of-week
- the time zone is omitted when the local time zone is used
when you run `git cherry-pick`.
+
Note that any of the 'refs/*' cases above may come either from
-the '$GIT_DIR/refs' directory or from the '$GIT_DIR/packed-refs' file.
+the `$GIT_DIR/refs` directory or from the `$GIT_DIR/packed-refs` file.
While the ref name encoding is unspecified, UTF-8 is preferred as
some output processing may assume ref names in UTF-8.
`git push` were run while `branchname` was checked out (or the current
`HEAD` if no branchname is specified). Since our push destination is
in a remote repository, of course, we report the local tracking branch
- that corresponds to that branch (i.e., something in 'refs/remotes/').
+ that corresponds to that branch (i.e., something in `refs/remotes/`).
+
Here's an example to make it more clear:
+
--continue::
Continue the operation in progress using the information in
- '.git/sequencer'. Can be used to continue after resolving
+ `.git/sequencer`. Can be used to continue after resolving
conflicts in a failed cherry-pick or revert.
--quit::
config-like files that the caller specifies (i.e., files like `.gitmodules`,
`~/.gitconfig` etc.). For example,
----------------------------------------
+----------------------------------------
struct config_set gm_config;
git_configset_init(&gm_config);
int b;
* one side of history renames x -> z, and the other renames some file to
x/e, causing the need for the merge to do a transitive rename.
- * one side of history renames x -> z, but also renames all files within
- x. For example, x/a -> z/alpha, x/b -> z/bravo, etc.
+ * one side of history renames x -> z, but also renames all files within x.
+ For example, x/a -> z/alpha, x/b -> z/bravo, etc.
* both 'x' and 'y' being merged into a single directory 'z', with a
directory rename being detected for both x->z and y->z.
An example client/server communication might look like this:
----
- S: 007c74730d410fcb6603ace96f1dc55ea6196122532d refs/heads/local\0report-status delete-refs ofs-delta\n
+ S: 006274730d410fcb6603ace96f1dc55ea6196122532d refs/heads/local\0report-status delete-refs ofs-delta\n
S: 003e7d1665144a3a975c05f1f43902ddaf084e784dbe refs/heads/debug\n
S: 003f74730d410fcb6603ace96f1dc55ea6196122532d refs/heads/master\n
- S: 003f74730d410fcb6603ace96f1dc55ea6196122532d refs/heads/team\n
+ S: 003d74730d410fcb6603ace96f1dc55ea6196122532d refs/heads/team\n
S: 0000
- C: 003e7d1665144a3a975c05f1f43902ddaf084e784dbe 74730d410fcb6603ace96f1dc55ea6196122532d refs/heads/debug\n
- C: 003e74730d410fcb6603ace96f1dc55ea6196122532d 5a3f6be755bbb7deae50065988cbfa1ffa9ab68a refs/heads/master\n
+ C: 00677d1665144a3a975c05f1f43902ddaf084e784dbe 74730d410fcb6603ace96f1dc55ea6196122532d refs/heads/debug\n
+ C: 006874730d410fcb6603ace96f1dc55ea6196122532d 5a3f6be755bbb7deae50065988cbfa1ffa9ab68a refs/heads/master\n
C: 0000
C: [PACKDATA]
- Git Wire Protocol, Version 2
-==============================
+Git Wire Protocol, Version 2
+============================
This document presents a specification for a version 2 of Git's wire
protocol. Protocol v2 will improve upon v1 in the following ways:
has completed, a client can reuse the connection and request that other
commands be executed.
- Packet-Line Framing
----------------------
+Packet-Line Framing
+-------------------
All communication is done using packet-line framing, just as in v1. See
`Documentation/technical/pack-protocol.txt` and
* '0000' Flush Packet (flush-pkt) - indicates the end of a message
* '0001' Delimiter Packet (delim-pkt) - separates sections of a message
- Initial Client Request
-------------------------
+Initial Client Request
+----------------------
In general a client can request to speak protocol v2 by sending
`version=2` through the respective side-channel for the transport being
found in `pack-protocol.txt` and `http-protocol.txt`. In all cases the
response from the server is the capability advertisement.
- Git Transport
-~~~~~~~~~~~~~~~
+Git Transport
+~~~~~~~~~~~~~
When using the git:// transport, you can request to use protocol v2 by
sending "version=2" as an extra parameter:
003egit-upload-pack /project.git\0host=myserver.com\0\0version=2\0
- SSH and File Transport
-~~~~~~~~~~~~~~~~~~~~~~~~
+SSH and File Transport
+~~~~~~~~~~~~~~~~~~~~~~
When using either the ssh:// or file:// transport, the GIT_PROTOCOL
environment variable must be set explicitly to include "version=2".
- HTTP Transport
-~~~~~~~~~~~~~~~~
+HTTP Transport
+~~~~~~~~~~~~~~
When using the http:// or https:// transport a client makes a "smart"
info/refs request as described in `http-protocol.txt` and requests that
Subsequent requests are then made directly to the service
`$GIT_URL/git-upload-pack`. (This works the same for git-receive-pack).
- Capability Advertisement
---------------------------
+Capability Advertisement
+------------------------
A server which decides to communicate (based on a request from a client)
using protocol version 2, notifies the client by sending a version string
key = 1*(ALPHA | DIGIT | "-_")
value = 1*(ALPHA | DIGIT | " -_.,?\/{}[]()<>!@#$%^&*+=:;")
- Command Request
------------------
+Command Request
+---------------
After receiving the capability advertisement, a client can then issue a
request to select the command it wants with any particular capabilities
optionally send an empty request consisting of just a flush-pkt to
indicate that no more requests will be made.
- Capabilities
---------------
+Capabilities
+------------
There are two different types of capabilities: normal capabilities,
which can be used to to convey information or alter the behavior of a
permits simple round-robin load-balancing on the server side, without
needing to worry about state management.
- agent
-~~~~~~~
+agent
+~~~~~
The server can advertise the `agent` capability with a value `X` (in the
form `agent=X`) to notify the client that the server is running version
and debugging purposes, and MUST NOT be used to programmatically assume
the presence or absence of particular features.
- ls-refs
-~~~~~~~~~
+ls-refs
+~~~~~~~
`ls-refs` is the command used to request a reference advertisement in v2.
Unlike the current reference advertisement, ls-refs takes in arguments
symref = "symref-target:" symref-target
peeled = "peeled:" obj-id
- fetch
-~~~~~~~
+fetch
+~~~~~
`fetch` is the command used to fetch a packfile in v2. It can be looked
at as a modified version of the v1 fetch where the ref-advertisement is
2 - progress messages
3 - fatal error message just before stream aborts
- server-option
-~~~~~~~~~~~~~~~
+server-option
+~~~~~~~~~~~~~
If advertised, indicates that any number of server specific options can be
included in a request. This is done by sending each option as a
where <address> may be a path, a server and path, or an arbitrary
URL-like string recognized by the specific remote helper being
-invoked. See linkgit:gitremote-helpers[1] for details.
+invoked. See linkgit:gitremote-helpers[7] for details.
If there are a large number of similarly-named remote repositories and
you want to use a different format for them (such that the URLs you
#
# Define DEVELOPER to enable more compiler warnings. Compiler version
# and family are auto detected, but could be overridden by defining
-# COMPILER_FEATURES (see config.mak.dev)
+# COMPILER_FEATURES (see config.mak.dev). You can still set
+# CFLAGS="..." in combination with DEVELOPER enables, whether that's
+# for tweaking something unrelated (e.g. optimization level), or for
+# selectively overriding something DEVELOPER or one of the DEVOPTS
+# (see just below) brings in.
#
# When DEVELOPER is set, DEVOPTS can be used to control compiler
# options. This variable contains keywords separated by
@$(SHELL_PATH) ./GIT-VERSION-GEN
-include GIT-VERSION-FILE
-# CFLAGS and LDFLAGS are for the users to override from the command line.
-
-CFLAGS = -g -O2 -Wall
-LDFLAGS =
-ALL_CFLAGS = $(CPPFLAGS) $(CFLAGS)
-ALL_LDFLAGS = $(LDFLAGS)
-STRIP ?= strip
-
-# Create as necessary, replace existing, make ranlib unneeded.
-ARFLAGS = rcs
-
+# Set our default configuration.
+#
# Among the variables below, these:
# gitexecdir
# template_dir
export prefix bindir sharedir sysconfdir gitwebdir perllibdir localedir
+# Set our default programs
CC = cc
AR = ar
RM = rm -f
XGETTEXT = xgettext
MSGFMT = msgfmt
CURL_CONFIG = curl-config
-PTHREAD_LIBS = -lpthread
-PTHREAD_CFLAGS =
GCOV = gcov
+STRIP = strip
SPATCH = spatch
export TCL_PATH TCLTK_PATH
-# user customisation variable for 'sparse' target
-SPARSE_FLAGS ?=
-# internal/platform customisation variable for 'sparse'
-SP_EXTRA_FLAGS =
-
-SPATCH_FLAGS = --all-includes --patch .
-
-
-
-### --- END CONFIGURATION SECTION ---
-
-# Those must not be GNU-specific; they are shared with perl/ which may
-# be built by a different compiler. (Note that this is an artifact now
-# but it still might be nice to keep that distinction.)
-BASIC_CFLAGS = -I.
-BASIC_LDFLAGS =
+# Set our default LIBS variables
+PTHREAD_LIBS = -lpthread
# Guard against environment variables
BUILTIN_OBJS =
SCRIPT_SH += git-merge-resolve.sh
SCRIPT_SH += git-mergetool.sh
SCRIPT_SH += git-quiltimport.sh
-SCRIPT_SH += git-legacy-rebase.sh
+SCRIPT_SH += git-legacy-stash.sh
SCRIPT_SH += git-remote-testgit.sh
SCRIPT_SH += git-request-pull.sh
-SCRIPT_SH += git-stash.sh
SCRIPT_SH += git-submodule.sh
SCRIPT_SH += git-web--browse.sh
BUILTIN_OBJS += builtin/show-branch.o
BUILTIN_OBJS += builtin/show-index.o
BUILTIN_OBJS += builtin/show-ref.o
+BUILTIN_OBJS += builtin/stash.o
BUILTIN_OBJS += builtin/stripspace.o
BUILTIN_OBJS += builtin/submodule--helper.o
BUILTIN_OBJS += builtin/symbolic-ref.o
DC_SHA1_SUBMODULE = auto
endif
+# Set CFLAGS, LDFLAGS and other *FLAGS variables. These might be
+# tweaked by config.* below as well as the command-line, both of
+# which'll override these defaults.
+CFLAGS = -g -O2 -Wall
+LDFLAGS =
+BASIC_CFLAGS = -I.
+BASIC_LDFLAGS =
+
+# library flags
+ARFLAGS = rcs
+PTHREAD_CFLAGS =
+
+# For the 'sparse' target
+SPARSE_FLAGS ?=
+SP_EXTRA_FLAGS =
+
+# For the 'coccicheck' target
+SPATCH_FLAGS = --all-includes --patch .
+
include config.mak.uname
-include config.mak.autogen
-include config.mak
include config.mak.dev
endif
+ALL_CFLAGS = $(DEVELOPER_CFLAGS) $(CPPFLAGS) $(CFLAGS)
+ALL_LDFLAGS = $(LDFLAGS)
+
comma := ,
empty :=
space := $(empty) $(empty)
BASIC_CFLAGS += -fno-omit-frame-pointer
ifneq ($(filter undefined,$(SANITIZERS)),)
BASIC_CFLAGS += -DNO_UNALIGNED_LOADS
+BASIC_CFLAGS += -DSHA1DC_FORCE_ALIGNED_ACCESS
endif
ifneq ($(filter leak,$(SANITIZERS)),)
BASIC_CFLAGS += -DSUPPRESS_ANNOTATED_LEAKS
.PHONY: check-docs
check-docs::
$(MAKE) -C Documentation lint-docs
- @(for v in $(ALL_COMMANDS); \
+ @(for v in $(patsubst %$X,%,$(ALL_COMMANDS)); \
do \
case "$$v" in \
git-merge-octopus | git-merge-ours | git-merge-recursive | \
( \
sed -e '1,/^### command list/d' \
-e '/^#/d' \
+ -e '/guide$$/d' \
-e 's/[ ].*//' \
-e 's/^/listed /' command-list.txt; \
$(MAKE) -C Documentation print-man1 | \
grep '\.txt$$' | \
- sed -e 's|Documentation/|documented |' \
+ sed -e 's|^|documented |' \
-e 's/\.txt//'; \
) | while read how cmd; \
do \
- case " $(ALL_COMMANDS) " in \
+ case " $(patsubst %$X,%,$(ALL_COMMANDS)) " in \
*" $$cmd "*) ;; \
*) echo "removed but $$how: $$cmd" ;; \
esac; \
const char *prefix,
struct commit *commit)
{
+ const char *argv[] = {
+ "diff-tree", "--pretty", "--stat", "--summary", "--cc", NULL
+ };
struct rev_info opt;
- /* diff-tree init */
+ git_config(git_diff_ui_config, NULL);
repo_init_revisions(r, &opt, prefix);
- git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
- opt.abbrev = 0;
- opt.diff = 1;
- /* This is what "--pretty" does */
- opt.verbose_header = 1;
- opt.use_terminator = 0;
- opt.commit_format = CMIT_FMT_DEFAULT;
-
- /* diff-tree init */
- if (!opt.diffopt.output_format)
- opt.diffopt.output_format = DIFF_FORMAT_RAW;
-
- setup_revisions(0, NULL, &opt, NULL);
+ setup_revisions(ARRAY_SIZE(argv) - 1, argv, &opt, NULL);
log_tree_commit(&opt, commit);
}
extern int cmd_show_branch(int argc, const char **argv, const char *prefix);
extern int cmd_show_index(int argc, const char **argv, const char *prefix);
extern int cmd_status(int argc, const char **argv, const char *prefix);
+extern int cmd_stash(int argc, const char **argv, const char *prefix);
extern int cmd_stripspace(int argc, const char **argv, const char *prefix);
extern int cmd_submodule__helper(int argc, const char **argv, const char *prefix);
extern int cmd_symbolic_ref(int argc, const char **argv, const char *prefix);
struct tree *result;
struct tree *work;
struct merge_options o;
+ struct strbuf sb = STRBUF_INIT;
+
if (!opts->merge)
return 1;
if (!old_branch_info->commit)
return 1;
+ if (repo_index_has_changes(the_repository,
+ get_commit_tree(old_branch_info->commit),
+ &sb))
+ warning(_("staged changes in the following files may be lost: %s"),
+ sb.buf);
+ strbuf_release(&sb);
+
/* Do more real merge */
/*
#include "builtin.h"
#include "utf8.h"
#include "gpg-interface.h"
+#include "parse-options.h"
-static const char commit_tree_usage[] = "git commit-tree [(-p <sha1>)...] [-S[<keyid>]] [-m <message>] [-F <file>] <sha1>";
+static const char * const commit_tree_usage[] = {
+ N_("git commit-tree [(-p <parent>)...] [-S[<keyid>]] [(-m <message>)...] "
+ "[(-F <file>)...] <tree>"),
+ NULL
+};
static const char *sign_commit;
struct commit_list *parents;
for (parents = *parents_p; parents; parents = parents->next) {
if (parents->item == parent) {
- error("duplicate parent %s ignored", oid_to_hex(oid));
+ error(_("duplicate parent %s ignored"), oid_to_hex(oid));
return;
}
parents_p = &parents->next;
return git_default_config(var, value, cb);
}
+static int parse_parent_arg_callback(const struct option *opt,
+ const char *arg, int unset)
+{
+ struct object_id oid;
+ struct commit_list **parents = opt->value;
+
+ BUG_ON_OPT_NEG_NOARG(unset, arg);
+
+ if (get_oid_commit(arg, &oid))
+ die(_("not a valid object name %s"), arg);
+
+ assert_oid_type(&oid, OBJ_COMMIT);
+ new_parent(lookup_commit(the_repository, &oid), parents);
+ return 0;
+}
+
+static int parse_message_arg_callback(const struct option *opt,
+ const char *arg, int unset)
+{
+ struct strbuf *buf = opt->value;
+
+ BUG_ON_OPT_NEG_NOARG(unset, arg);
+
+ if (buf->len)
+ strbuf_addch(buf, '\n');
+ strbuf_addstr(buf, arg);
+ strbuf_complete_line(buf);
+
+ return 0;
+}
+
+static int parse_file_arg_callback(const struct option *opt,
+ const char *arg, int unset)
+{
+ int fd;
+ struct strbuf *buf = opt->value;
+
+ BUG_ON_OPT_NEG_NOARG(unset, arg);
+
+ if (buf->len)
+ strbuf_addch(buf, '\n');
+ if (!strcmp(arg, "-"))
+ fd = 0;
+ else {
+ fd = open(arg, O_RDONLY);
+ if (fd < 0)
+ die_errno(_("git commit-tree: failed to open '%s'"), arg);
+ }
+ if (strbuf_read(buf, fd, 0) < 0)
+ die_errno(_("git commit-tree: failed to read '%s'"), arg);
+ if (fd && close(fd))
+ die_errno(_("git commit-tree: failed to close '%s'"), arg);
+
+ return 0;
+}
+
int cmd_commit_tree(int argc, const char **argv, const char *prefix)
{
- int i, got_tree = 0;
+ static struct strbuf buffer = STRBUF_INIT;
struct commit_list *parents = NULL;
struct object_id tree_oid;
struct object_id commit_oid;
- struct strbuf buffer = STRBUF_INIT;
+
+ struct option options[] = {
+ { OPTION_CALLBACK, 'p', NULL, &parents, N_("parent"),
+ N_("id of a parent commit object"), PARSE_OPT_NONEG,
+ parse_parent_arg_callback },
+ { OPTION_CALLBACK, 'm', NULL, &buffer, N_("message"),
+ N_("commit message"), PARSE_OPT_NONEG,
+ parse_message_arg_callback },
+ { OPTION_CALLBACK, 'F', NULL, &buffer, N_("file"),
+ N_("read commit log message from file"), PARSE_OPT_NONEG,
+ parse_file_arg_callback },
+ { OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"),
+ N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
+ OPT_END()
+ };
git_config(commit_tree_config, NULL);
if (argc < 2 || !strcmp(argv[1], "-h"))
- usage(commit_tree_usage);
-
- for (i = 1; i < argc; i++) {
- const char *arg = argv[i];
- if (!strcmp(arg, "-p")) {
- struct object_id oid;
- if (argc <= ++i)
- usage(commit_tree_usage);
- if (get_oid_commit(argv[i], &oid))
- die("Not a valid object name %s", argv[i]);
- assert_oid_type(&oid, OBJ_COMMIT);
- new_parent(lookup_commit(the_repository, &oid),
- &parents);
- continue;
- }
+ usage_with_options(commit_tree_usage, options);
- if (!strcmp(arg, "--gpg-sign")) {
- sign_commit = "";
- continue;
- }
+ argc = parse_options(argc, argv, prefix, options, commit_tree_usage, 0);
- if (skip_prefix(arg, "-S", &sign_commit) ||
- skip_prefix(arg, "--gpg-sign=", &sign_commit))
- continue;
+ if (argc != 1)
+ die(_("must give exactly one tree"));
- if (!strcmp(arg, "--no-gpg-sign")) {
- sign_commit = NULL;
- continue;
- }
-
- if (!strcmp(arg, "-m")) {
- if (argc <= ++i)
- usage(commit_tree_usage);
- if (buffer.len)
- strbuf_addch(&buffer, '\n');
- strbuf_addstr(&buffer, argv[i]);
- strbuf_complete_line(&buffer);
- continue;
- }
-
- if (!strcmp(arg, "-F")) {
- int fd;
-
- if (argc <= ++i)
- usage(commit_tree_usage);
- if (buffer.len)
- strbuf_addch(&buffer, '\n');
- if (!strcmp(argv[i], "-"))
- fd = 0;
- else {
- fd = open(argv[i], O_RDONLY);
- if (fd < 0)
- die_errno("git commit-tree: failed to open '%s'",
- argv[i]);
- }
- if (strbuf_read(&buffer, fd, 0) < 0)
- die_errno("git commit-tree: failed to read '%s'",
- argv[i]);
- if (fd && close(fd))
- die_errno("git commit-tree: failed to close '%s'",
- argv[i]);
- continue;
- }
-
- if (get_oid_tree(arg, &tree_oid))
- die("Not a valid object name %s", arg);
- if (got_tree)
- die("Cannot give more than one trees");
- got_tree = 1;
- }
+ if (get_oid_tree(argv[0], &tree_oid))
+ die(_("not a valid object name %s"), argv[0]);
if (!buffer.len) {
if (strbuf_read(&buffer, 0, 0) < 0)
- die_errno("git commit-tree: failed to read");
+ die_errno(_("git commit-tree: failed to read"));
}
if (commit_tree(buffer.buf, buffer.len, &tree_oid, parents, &commit_oid,
sigchain_push_common(unlock_pack_on_signal);
atexit(unlock_pack);
+ sigchain_push(SIGPIPE, SIG_IGN);
exit_code = do_fetch(gtransport, &rs);
+ sigchain_pop(SIGPIPE);
refspec_clear(&rs);
transport_disconnect(gtransport);
gtransport = NULL;
return 0;
}
+static void mark_unreachable_referents(const struct object_id *oid)
+{
+ struct fsck_options options = FSCK_OPTIONS_DEFAULT;
+ struct object *obj = lookup_object(the_repository, oid->hash);
+
+ if (!obj || !(obj->flags & HAS_OBJ))
+ return; /* not part of our original set */
+ if (obj->flags & REACHABLE)
+ return; /* reachable objects already traversed */
+
+ /*
+ * Avoid passing OBJ_NONE to fsck_walk, which will parse the object
+ * (and we want to avoid parsing blobs).
+ */
+ if (obj->type == OBJ_NONE) {
+ enum object_type type = oid_object_info(the_repository,
+ &obj->oid, NULL);
+ if (type > 0)
+ object_as_type(the_repository, obj, type, 0);
+ }
+
+ options.walk = mark_used;
+ fsck_walk(obj, NULL, &options);
+}
+
+static int mark_loose_unreachable_referents(const struct object_id *oid,
+ const char *path,
+ void *data)
+{
+ mark_unreachable_referents(oid);
+ return 0;
+}
+
+static int mark_packed_unreachable_referents(const struct object_id *oid,
+ struct packed_git *pack,
+ uint32_t pos,
+ void *data)
+{
+ mark_unreachable_referents(oid);
+ return 0;
+}
+
/*
* Check a single reachable object
*/
/* Traverse the pending reachable objects */
traverse_reachable();
+ /*
+ * With --connectivity-only, we won't have actually opened and marked
+ * unreachable objects with USED. Do that now to make --dangling, etc
+ * accurate.
+ */
+ if (connectivity_only && (show_dangling || write_lost_and_found)) {
+ /*
+ * Even though we already have a "struct object" for each of
+ * these in memory, we must not iterate over the internal
+ * object hash as we do below. Our loop would potentially
+ * resize the hash, making our iteration invalid.
+ *
+ * Instead, we'll just go back to the source list of objects,
+ * and ignore any that weren't present in our earlier
+ * traversal.
+ */
+ for_each_loose_object(mark_loose_unreachable_referents, NULL, 0);
+ for_each_packed_object(mark_packed_unreachable_referents, NULL, 0);
+ }
+
/* Look up all the requirements, warn about missing objects.. */
max = get_max_object_index();
if (verbose)
struct strbuf path = STRBUF_INIT;
struct strbuf template_path = STRBUF_INIT;
size_t template_len;
- struct repository_format template_format;
+ struct repository_format template_format = REPOSITORY_FORMAT_INIT;
struct strbuf err = STRBUF_INIT;
DIR *dir;
char *to_free = NULL;
free(to_free);
strbuf_release(&path);
strbuf_release(&template_path);
+ clear_repository_format(&template_format);
}
static int git_init_db_config(const char *k, const char *v, void *cb)
if (!strcmp(k, "init.templatedir"))
return git_config_pathname(&init_db_template_dir, k, v);
+ if (starts_with(k, "core."))
+ return platform_core_config(k, v, cb);
+
return 0;
}
struct strbuf err = STRBUF_INIT;
/* Just look for `init.templatedir` */
+ init_db_template_dir = NULL; /* re-set in case it was set before */
git_config(git_init_db_config, NULL);
/*
}
startup_info->have_repository = 1;
+ /* Just look for `core.hidedotfiles` */
+ git_config(git_init_db_config, NULL);
+
safe_create_dir(git_dir, 0);
init_is_bare_repository = is_bare_repository();
if (get_oid_with_context(the_repository, obj_name,
GET_OID_RECORD_PATH,
&oidc, &obj_context))
- die(_("Not a valid object name %s"), obj_name);
+ die(_("not a valid object name %s"), obj_name);
if (!obj_context.path ||
!textconv_object(the_repository, obj_context.path,
obj_context.mode, &oidc, 1, &buf, &size)) {
int offset = 0;
if (!buf)
- return error(_("Could not read object %s"), oid_to_hex(oid));
+ return error(_("could not read object %s"), oid_to_hex(oid));
assert(type == OBJ_TAG);
while (offset < size && buf[offset] != '\n') {
break;
o = parse_object(the_repository, &t->tagged->oid);
if (!o)
- ret = error(_("Could not read object %s"),
+ ret = error(_("could not read object %s"),
oid_to_hex(&t->tagged->oid));
objects[i].item = o;
i--;
ret = cmd_log_walk(&rev);
break;
default:
- ret = error(_("Unknown type: %d"), o->type);
+ ret = error(_("unknown type: %d"), o->type);
}
}
free(objects);
printf("%s\n", filename.buf + outdir_offset);
if ((rev->diffopt.file = fopen(filename.buf, "w")) == NULL) {
- error_errno(_("Cannot open patch file %s"), filename.buf);
+ error_errno(_("cannot open patch file %s"), filename.buf);
strbuf_release(&filename);
return -1;
}
unsigned flags1, flags2;
if (rev->pending.nr != 2)
- die(_("Need exactly one range."));
+ die(_("need exactly one range"));
o1 = rev->pending.objects[0].item;
o2 = rev->pending.objects[1].item;
c2 = lookup_commit_reference(the_repository, &o2->oid);
if ((flags1 & UNINTERESTING) == (flags2 & UNINTERESTING))
- die(_("Not a range."));
+ die(_("not a range"));
init_patch_ids(the_repository, ids);
struct commit *head = list[0];
if (!cmit_fmt_is_mail(rev->commit_format))
- die(_("Cover letter needs email format"));
+ die(_("cover letter needs email format"));
committer = git_committer_info(0);
if (!use_stdout &&
open_next_file(NULL, rev->numbered_files ? NULL : "cover-letter", rev, quiet))
- return;
+ die(_("failed to create cover-letter file"));
log_write_email_headers(rev, head, &pp.after_subject, &need_8bit_cte, 0);
const char **dir = (const char **)opt->value;
BUG_ON_OPT_NEG(unset);
if (*dir)
- die(_("Two output directories?"));
+ die(_("two output directories?"));
*dir = arg;
return 0;
}
if (base_commit && strcmp(base_commit, "auto")) {
base = lookup_commit_reference_by_name(base_commit);
if (!base)
- die(_("Unknown commit %s"), base_commit);
+ die(_("unknown commit %s"), base_commit);
} else if ((base_commit && !strcmp(base_commit, "auto")) || base_auto) {
struct branch *curr_branch = branch_get(NULL);
const char *upstream = branch_get_upstream(curr_branch, NULL);
struct object_id oid;
if (get_oid(upstream, &oid))
- die(_("Failed to resolve '%s' as a valid ref."), upstream);
+ die(_("failed to resolve '%s' as a valid ref"), upstream);
commit = lookup_commit_or_die(&oid, "upstream base");
base_list = get_merge_bases_many(commit, total, list);
/* There should be one and only one merge base. */
if (!base_list || base_list->next)
- die(_("Could not find exact merge base."));
+ die(_("could not find exact merge base"));
base = base_list->item;
free_commit_list(base_list);
} else {
- die(_("Failed to get upstream, if you want to record base commit automatically,\n"
+ die(_("failed to get upstream, if you want to record base commit automatically,\n"
"please use git branch --set-upstream-to to track a remote branch.\n"
- "Or you could specify base commit by --base=<base-commit-id> manually."));
+ "Or you could specify base commit by --base=<base-commit-id> manually"));
}
}
struct commit_list *merge_base;
merge_base = get_merge_bases(rev[2 * i], rev[2 * i + 1]);
if (!merge_base || merge_base->next)
- die(_("Failed to find exact merge base"));
+ die(_("failed to find exact merge base"));
rev[i] = merge_base->item;
}
if (use_stdout)
die(_("standard output, or directory, which one?"));
if (mkdir(output_directory, 0777) < 0 && errno != EEXIST)
- die_errno(_("Could not create directory '%s'"),
+ die_errno(_("could not create directory '%s'"),
output_directory);
}
if (!use_stdout &&
open_next_file(rev.numbered_files ? NULL : commit, NULL, &rev, quiet))
- die(_("Failed to create output files"));
+ die(_("failed to create output files"));
shown = log_tree_commit(&rev, commit);
free_commit_buffer(the_repository->parsed_objects,
commit);
revs.max_parents = 1;
if (add_pending_commit(head, &revs, 0))
- die(_("Unknown commit %s"), head);
+ die(_("unknown commit %s"), head);
if (add_pending_commit(upstream, &revs, UNINTERESTING))
- die(_("Unknown commit %s"), upstream);
+ die(_("unknown commit %s"), upstream);
/* Don't say anything if head and upstream are the same. */
if (revs.pending.nr == 2) {
get_patch_ids(&revs, &ids);
if (limit && add_pending_commit(limit, &revs, UNINTERESTING))
- die(_("Unknown commit %s"), limit);
+ die(_("unknown commit %s"), limit);
/* reverse the list of commits */
if (prepare_revision_walk(&revs))
#include "config.h"
#include "parse-options.h"
#include "midx.h"
+#include "trace2.h"
static char const * const builtin_multi_pack_index_usage[] = {
N_("git multi-pack-index [--object-dir=<dir>] (write|verify)"),
return 1;
}
+ trace2_cmd_mode(argv[0]);
+
if (!strcmp(argv[0], "write"))
return write_midx_file(opts.object_dir);
if (!strcmp(argv[0], "verify"))
static GIT_PATH_FUNC(path_squash_onto, "rebase-merge/squash-onto")
static GIT_PATH_FUNC(path_interactive, "rebase-merge/interactive")
+static int add_exec_commands(struct string_list *commands)
+{
+ const char *todo_file = rebase_path_todo();
+ struct todo_list todo_list = TODO_LIST_INIT;
+ int res;
+
+ if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
+ return error_errno(_("could not read '%s'."), todo_file);
+
+ if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,
+ &todo_list)) {
+ todo_list_release(&todo_list);
+ return error(_("unusable todo list: '%s'"), todo_file);
+ }
+
+ todo_list_add_exec_commands(&todo_list, commands);
+ res = todo_list_write_to_file(the_repository, &todo_list,
+ todo_file, NULL, NULL, -1, 0);
+ todo_list_release(&todo_list);
+
+ if (res)
+ return error_errno(_("could not write '%s'."), todo_file);
+ return 0;
+}
+
+static int rearrange_squash_in_todo_file(void)
+{
+ const char *todo_file = rebase_path_todo();
+ struct todo_list todo_list = TODO_LIST_INIT;
+ int res = 0;
+
+ if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
+ return error_errno(_("could not read '%s'."), todo_file);
+ if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,
+ &todo_list)) {
+ todo_list_release(&todo_list);
+ return error(_("unusable todo list: '%s'"), todo_file);
+ }
+
+ res = todo_list_rearrange_squash(&todo_list);
+ if (!res)
+ res = todo_list_write_to_file(the_repository, &todo_list,
+ todo_file, NULL, NULL, -1, 0);
+
+ todo_list_release(&todo_list);
+
+ if (res)
+ return error_errno(_("could not write '%s'."), todo_file);
+ return 0;
+}
+
+static int transform_todo_file(unsigned flags)
+{
+ const char *todo_file = rebase_path_todo();
+ struct todo_list todo_list = TODO_LIST_INIT;
+ int res;
+
+ if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
+ return error_errno(_("could not read '%s'."), todo_file);
+
+ if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,
+ &todo_list)) {
+ todo_list_release(&todo_list);
+ return error(_("unusable todo list: '%s'"), todo_file);
+ }
+
+ res = todo_list_write_to_file(the_repository, &todo_list, todo_file,
+ NULL, NULL, -1, flags);
+ todo_list_release(&todo_list);
+
+ if (res)
+ return error_errno(_("could not write '%s'."), todo_file);
+ return 0;
+}
+
+static int edit_todo_file(unsigned flags)
+{
+ const char *todo_file = rebase_path_todo();
+ struct todo_list todo_list = TODO_LIST_INIT,
+ new_todo = TODO_LIST_INIT;
+ int res = 0;
+
+ if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
+ return error_errno(_("could not read '%s'."), todo_file);
+
+ strbuf_stripspace(&todo_list.buf, 1);
+ res = edit_todo_list(the_repository, &todo_list, &new_todo, NULL, NULL, flags);
+ if (!res && todo_list_write_to_file(the_repository, &new_todo, todo_file,
+ NULL, NULL, -1, flags & ~(TODO_LIST_SHORTEN_IDS)))
+ res = error_errno(_("could not write '%s'"), todo_file);
+
+ todo_list_release(&todo_list);
+ todo_list_release(&new_todo);
+
+ return res;
+}
+
static int get_revision_ranges(const char *upstream, const char *onto,
const char **head_hash,
char **revisions, char **shortrevisions)
const char *onto, const char *onto_name,
const char *squash_onto, const char *head_name,
const char *restrict_revision, char *raw_strategies,
- const char *cmd, unsigned autosquash)
+ struct string_list *commands, unsigned autosquash)
{
int ret;
const char *head_hash = NULL;
char *revisions = NULL, *shortrevisions = NULL;
struct argv_array make_script_args = ARGV_ARRAY_INIT;
- FILE *todo_list;
+ struct todo_list todo_list = TODO_LIST_INIT;
if (prepare_branch_to_be_rebased(opts, switch_to))
return -1;
if (!upstream && squash_onto)
write_file(path_squash_onto(), "%s\n", squash_onto);
- todo_list = fopen(rebase_path_todo(), "w");
- if (!todo_list) {
- free(revisions);
- free(shortrevisions);
-
- return error_errno(_("could not open %s"), rebase_path_todo());
- }
-
argv_array_pushl(&make_script_args, "", revisions, NULL);
if (restrict_revision)
argv_array_push(&make_script_args, restrict_revision);
- ret = sequencer_make_script(the_repository, todo_list,
+ ret = sequencer_make_script(the_repository, &todo_list.buf,
make_script_args.argc, make_script_args.argv,
flags);
- fclose(todo_list);
if (ret)
error(_("could not generate todo list"));
else {
discard_cache();
- ret = complete_action(the_repository, opts, flags,
- shortrevisions, onto_name, onto,
- head_hash, cmd, autosquash);
+ if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,
+ &todo_list))
+ BUG("unusable todo list");
+
+ ret = complete_action(the_repository, opts, flags, shortrevisions, onto_name,
+ onto, head_hash, commands, autosquash, &todo_list);
}
free(revisions);
free(shortrevisions);
+ todo_list_release(&todo_list);
argv_array_clear(&make_script_args);
return ret;
const char *onto = NULL, *onto_name = NULL, *restrict_revision = NULL,
*squash_onto = NULL, *upstream = NULL, *head_name = NULL,
*switch_to = NULL, *cmd = NULL;
+ struct string_list commands = STRING_LIST_INIT_DUP;
char *raw_strategies = NULL;
enum {
NONE = 0, CONTINUE, SKIP, EDIT_TODO, SHOW_CURRENT_PATCH,
warning(_("--[no-]rebase-cousins has no effect without "
"--rebase-merges"));
+ if (cmd && *cmd) {
+ string_list_split(&commands, cmd, '\n', -1);
+
+ /* rebase.c adds a new line to cmd after every command,
+ * so here the last command is always empty */
+ string_list_remove_empty_items(&commands, 0);
+ }
+
switch (command) {
case NONE:
if (!onto && !upstream)
ret = do_interactive_rebase(&opts, flags, switch_to, upstream, onto,
onto_name, squash_onto, head_name, restrict_revision,
- raw_strategies, cmd, autosquash);
+ raw_strategies, &commands, autosquash);
break;
case SKIP: {
struct string_list merge_rr = STRING_LIST_INIT_DUP;
break;
}
case EDIT_TODO:
- ret = edit_todo_list(the_repository, flags);
+ ret = edit_todo_file(flags);
break;
case SHOW_CURRENT_PATCH: {
struct child_process cmd = CHILD_PROCESS_INIT;
}
case SHORTEN_OIDS:
case EXPAND_OIDS:
- ret = transform_todos(the_repository, flags);
+ ret = transform_todo_file(flags);
break;
case CHECK_TODO_LIST:
- ret = check_todo_list(the_repository);
+ ret = check_todo_list_from_file(the_repository);
break;
case REARRANGE_SQUASH:
- ret = rearrange_squash(the_repository);
+ ret = rearrange_squash_in_todo_file();
break;
case ADD_EXEC:
- ret = sequencer_add_exec_commands(the_repository, cmd);
+ ret = add_exec_commands(&commands);
break;
default:
BUG("invalid command '%d'", command);
}
+ string_list_clear(&commands, 0);
return !!ret;
}
REBASE_PRESERVE_MERGES
};
-static int use_builtin_rebase(void)
-{
- struct child_process cp = CHILD_PROCESS_INIT;
- struct strbuf out = STRBUF_INIT;
- int ret, env = git_env_bool("GIT_TEST_REBASE_USE_BUILTIN", -1);
-
- if (env != -1)
- return env;
-
- argv_array_pushl(&cp.args,
- "config", "--bool", "rebase.usebuiltin", NULL);
- cp.git_cmd = 1;
- if (capture_command(&cp, &out, 6)) {
- strbuf_release(&out);
- return 1;
- }
-
- strbuf_trim(&out);
- ret = !strcmp("true", out.buf);
- strbuf_release(&out);
- return ret;
-}
-
struct rebase_options {
enum rebase_type type;
const char *state_dir;
char *strategy, *strategy_opts;
struct strbuf git_format_patch_opt;
int reschedule_failed_exec;
+ int use_legacy_rebase;
};
static int is_interactive(struct rebase_options *opts)
#define RESET_HEAD_HARD (1<<1)
#define RESET_HEAD_RUN_POST_CHECKOUT_HOOK (1<<2)
#define RESET_HEAD_REFS_ONLY (1<<3)
+#define RESET_ORIG_HEAD (1<<4)
static int reset_head(struct object_id *oid, const char *action,
const char *switch_to_branch, unsigned flags,
unsigned reset_hard = flags & RESET_HEAD_HARD;
unsigned run_hook = flags & RESET_HEAD_RUN_POST_CHECKOUT_HOOK;
unsigned refs_only = flags & RESET_HEAD_REFS_ONLY;
+ unsigned update_orig_head = flags & RESET_ORIG_HEAD;
struct object_id head_oid;
struct tree_desc desc[2] = { { NULL }, { NULL } };
struct lock_file lock = LOCK_INIT;
strbuf_addf(&msg, "%s: ", reflog_action ? reflog_action : "rebase");
prefix_len = msg.len;
- if (!get_oid("ORIG_HEAD", &oid_old_orig))
- old_orig = &oid_old_orig;
- if (!get_oid("HEAD", &oid_orig)) {
- orig = &oid_orig;
- if (!reflog_orig_head) {
- strbuf_addstr(&msg, "updating ORIG_HEAD");
- reflog_orig_head = msg.buf;
- }
- update_ref(reflog_orig_head, "ORIG_HEAD", orig, old_orig, 0,
- UPDATE_REFS_MSG_ON_ERR);
- } else if (old_orig)
- delete_ref(NULL, "ORIG_HEAD", old_orig, 0);
+ if (update_orig_head) {
+ if (!get_oid("ORIG_HEAD", &oid_old_orig))
+ old_orig = &oid_old_orig;
+ if (!get_oid("HEAD", &oid_orig)) {
+ orig = &oid_orig;
+ if (!reflog_orig_head) {
+ strbuf_addstr(&msg, "updating ORIG_HEAD");
+ reflog_orig_head = msg.buf;
+ }
+ update_ref(reflog_orig_head, "ORIG_HEAD", orig,
+ old_orig, 0, UPDATE_REFS_MSG_ON_ERR);
+ } else if (old_orig)
+ delete_ref(NULL, "ORIG_HEAD", old_orig, 0);
+ }
+
if (!reflog_head) {
strbuf_setlen(&msg, prefix_len);
strbuf_addstr(&msg, "updating HEAD");
detach_head ? REF_NO_DEREF : 0,
UPDATE_REFS_MSG_ON_ERR);
else {
- ret = update_ref(reflog_orig_head, switch_to_branch, oid,
+ ret = update_ref(reflog_head, switch_to_branch, oid,
NULL, 0, UPDATE_REFS_MSG_ON_ERR);
if (!ret)
ret = create_symref("HEAD", switch_to_branch,
return 0;
}
+ if (!strcmp(var, "rebase.usebuiltin")) {
+ opts->use_legacy_rebase = !git_config_bool(var, value);
+ return 0;
+ }
+
return git_default_config(var, value, data);
}
PARSE_OPT_NOARG | PARSE_OPT_NONEG,
parse_opt_interactive },
OPT_SET_INT('p', "preserve-merges", &options.type,
- N_("try to recreate merges instead of ignoring "
- "them"), REBASE_PRESERVE_MERGES),
+ N_("(DEPRECATED) try to recreate merges instead of "
+ "ignoring them"), REBASE_PRESERVE_MERGES),
OPT_BOOL(0, "rerere-autoupdate",
&options.allow_rerere_autoupdate,
N_("allow rerere to update index with resolved "
};
int i;
- /*
- * NEEDSWORK: Once the builtin rebase has been tested enough
- * and git-legacy-rebase.sh is retired to contrib/, this preamble
- * can be removed.
- */
-
- if (!use_builtin_rebase()) {
- const char *path = mkpath("%s/git-legacy-rebase",
- git_exec_path());
-
- if (sane_execvp(path, (char **)argv) < 0)
- die_errno(_("could not exec %s"), path);
- else
- BUG("sane_execvp() returned???");
- }
-
if (argc == 2 && !strcmp(argv[1], "-h"))
usage_with_options(builtin_rebase_usage,
builtin_rebase_options);
git_config(rebase_config, &options);
+ if (options.use_legacy_rebase ||
+ !git_env_bool("GIT_TEST_REBASE_USE_BUILTIN", -1))
+ warning(_("the rebase.useBuiltin support has been removed!\n"
+ "See its entry in 'git help config' for details."));
+
strbuf_reset(&buf);
strbuf_addf(&buf, "%s/applying", apply_dir());
if(file_exists(buf.buf))
usage_with_options(builtin_rebase_usage,
builtin_rebase_options);
+ if (options.type == REBASE_PRESERVE_MERGES)
+ warning(_("git rebase --preserve-merges is deprecated. "
+ "Use --rebase-merges instead."));
+
if (action != NO_ACTION && !in_progress)
die(_("No rebase in progress?"));
setenv(GIT_REFLOG_ACTION_ENVIRONMENT, "rebase", 0);
strbuf_addf(&msg, "%s: checkout %s",
getenv(GIT_REFLOG_ACTION_ENVIRONMENT), options.onto_name);
if (reset_head(&options.onto->object.oid, "checkout", NULL,
- RESET_HEAD_DETACH | RESET_HEAD_RUN_POST_CHECKOUT_HOOK,
+ RESET_HEAD_DETACH | RESET_ORIG_HEAD |
+ RESET_HEAD_RUN_POST_CHECKOUT_HOOK,
NULL, msg.buf))
die(_("Could not detach HEAD"));
strbuf_release(&msg);
strbuf_addf(&msg, "rebase finished: %s onto %s",
options.head_name ? options.head_name : "detached HEAD",
oid_to_hex(&options.onto->object.oid));
- reset_head(NULL, "Fast-forwarded", options.head_name, 0,
- "HEAD", msg.buf);
+ reset_head(NULL, "Fast-forwarded", options.head_name,
+ RESET_HEAD_REFS_ONLY, "HEAD", msg.buf);
strbuf_release(&msg);
ret = !!finish_rebase(&options);
goto cleanup;
int flags = quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN;
if (read_from_tree(&pathspec, &oid, intent_to_add))
return 1;
+ the_index.updated_skipworktree = 1;
if (!quiet && get_git_work_tree()) {
uint64_t t_begin, t_delta_in_ms;
static int finish_object(struct object *obj, const char *name, void *cb_data)
{
struct rev_list_info *info = cb_data;
- if (!has_object_file(&obj->oid)) {
+ if (oid_object_info_extended(the_repository, &obj->oid, NULL, 0) < 0) {
finish_object__ma(obj);
return 1;
}
--- /dev/null
+#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#include "builtin.h"
+#include "config.h"
+#include "parse-options.h"
+#include "refs.h"
+#include "lockfile.h"
+#include "cache-tree.h"
+#include "unpack-trees.h"
+#include "merge-recursive.h"
+#include "argv-array.h"
+#include "run-command.h"
+#include "dir.h"
+#include "rerere.h"
+#include "revision.h"
+#include "log-tree.h"
+#include "diffcore.h"
+#include "exec-cmd.h"
+
+#define INCLUDE_ALL_FILES 2
+
+static const char * const git_stash_usage[] = {
+ N_("git stash list [<options>]"),
+ N_("git stash show [<options>] [<stash>]"),
+ N_("git stash drop [-q|--quiet] [<stash>]"),
+ N_("git stash ( pop | apply ) [--index] [-q|--quiet] [<stash>]"),
+ N_("git stash branch <branchname> [<stash>]"),
+ N_("git stash clear"),
+ N_("git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
+ " [-u|--include-untracked] [-a|--all] [-m|--message <message>]\n"
+ " [--] [<pathspec>...]]"),
+ N_("git stash save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
+ " [-u|--include-untracked] [-a|--all] [<message>]"),
+ NULL
+};
+
+static const char * const git_stash_list_usage[] = {
+ N_("git stash list [<options>]"),
+ NULL
+};
+
+static const char * const git_stash_show_usage[] = {
+ N_("git stash show [<options>] [<stash>]"),
+ NULL
+};
+
+static const char * const git_stash_drop_usage[] = {
+ N_("git stash drop [-q|--quiet] [<stash>]"),
+ NULL
+};
+
+static const char * const git_stash_pop_usage[] = {
+ N_("git stash pop [--index] [-q|--quiet] [<stash>]"),
+ NULL
+};
+
+static const char * const git_stash_apply_usage[] = {
+ N_("git stash apply [--index] [-q|--quiet] [<stash>]"),
+ NULL
+};
+
+static const char * const git_stash_branch_usage[] = {
+ N_("git stash branch <branchname> [<stash>]"),
+ NULL
+};
+
+static const char * const git_stash_clear_usage[] = {
+ N_("git stash clear"),
+ NULL
+};
+
+static const char * const git_stash_store_usage[] = {
+ N_("git stash store [-m|--message <message>] [-q|--quiet] <commit>"),
+ NULL
+};
+
+static const char * const git_stash_push_usage[] = {
+ N_("git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
+ " [-u|--include-untracked] [-a|--all] [-m|--message <message>]\n"
+ " [--] [<pathspec>...]]"),
+ NULL
+};
+
+static const char * const git_stash_save_usage[] = {
+ N_("git stash save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
+ " [-u|--include-untracked] [-a|--all] [<message>]"),
+ NULL
+};
+
+static const char *ref_stash = "refs/stash";
+static struct strbuf stash_index_path = STRBUF_INIT;
+
+/*
+ * w_commit is set to the commit containing the working tree
+ * b_commit is set to the base commit
+ * i_commit is set to the commit containing the index tree
+ * u_commit is set to the commit containing the untracked files tree
+ * w_tree is set to the working tree
+ * b_tree is set to the base tree
+ * i_tree is set to the index tree
+ * u_tree is set to the untracked files tree
+ */
+struct stash_info {
+ struct object_id w_commit;
+ struct object_id b_commit;
+ struct object_id i_commit;
+ struct object_id u_commit;
+ struct object_id w_tree;
+ struct object_id b_tree;
+ struct object_id i_tree;
+ struct object_id u_tree;
+ struct strbuf revision;
+ int is_stash_ref;
+ int has_u;
+};
+
+static void free_stash_info(struct stash_info *info)
+{
+ strbuf_release(&info->revision);
+}
+
+static void assert_stash_like(struct stash_info *info, const char *revision)
+{
+ if (get_oidf(&info->b_commit, "%s^1", revision) ||
+ get_oidf(&info->w_tree, "%s:", revision) ||
+ get_oidf(&info->b_tree, "%s^1:", revision) ||
+ get_oidf(&info->i_tree, "%s^2:", revision))
+ die(_("'%s' is not a stash-like commit"), revision);
+}
+
+static int get_stash_info(struct stash_info *info, int argc, const char **argv)
+{
+ int ret;
+ char *end_of_rev;
+ char *expanded_ref;
+ const char *revision;
+ const char *commit = NULL;
+ struct object_id dummy;
+ struct strbuf symbolic = STRBUF_INIT;
+
+ if (argc > 1) {
+ int i;
+ struct strbuf refs_msg = STRBUF_INIT;
+
+ for (i = 0; i < argc; i++)
+ strbuf_addf(&refs_msg, " '%s'", argv[i]);
+
+ fprintf_ln(stderr, _("Too many revisions specified:%s"),
+ refs_msg.buf);
+ strbuf_release(&refs_msg);
+
+ return -1;
+ }
+
+ if (argc == 1)
+ commit = argv[0];
+
+ strbuf_init(&info->revision, 0);
+ if (!commit) {
+ if (!ref_exists(ref_stash)) {
+ free_stash_info(info);
+ fprintf_ln(stderr, _("No stash entries found."));
+ return -1;
+ }
+
+ strbuf_addf(&info->revision, "%s@{0}", ref_stash);
+ } else if (strspn(commit, "0123456789") == strlen(commit)) {
+ strbuf_addf(&info->revision, "%s@{%s}", ref_stash, commit);
+ } else {
+ strbuf_addstr(&info->revision, commit);
+ }
+
+ revision = info->revision.buf;
+
+ if (get_oid(revision, &info->w_commit)) {
+ error(_("%s is not a valid reference"), revision);
+ free_stash_info(info);
+ return -1;
+ }
+
+ assert_stash_like(info, revision);
+
+ info->has_u = !get_oidf(&info->u_tree, "%s^3:", revision);
+
+ end_of_rev = strchrnul(revision, '@');
+ strbuf_add(&symbolic, revision, end_of_rev - revision);
+
+ ret = dwim_ref(symbolic.buf, symbolic.len, &dummy, &expanded_ref);
+ strbuf_release(&symbolic);
+ switch (ret) {
+ case 0: /* Not found, but valid ref */
+ info->is_stash_ref = 0;
+ break;
+ case 1:
+ info->is_stash_ref = !strcmp(expanded_ref, ref_stash);
+ break;
+ default: /* Invalid or ambiguous */
+ free_stash_info(info);
+ }
+
+ free(expanded_ref);
+ return !(ret == 0 || ret == 1);
+}
+
+static int do_clear_stash(void)
+{
+ struct object_id obj;
+ if (get_oid(ref_stash, &obj))
+ return 0;
+
+ return delete_ref(NULL, ref_stash, &obj, 0);
+}
+
+static int clear_stash(int argc, const char **argv, const char *prefix)
+{
+ struct option options[] = {
+ OPT_END()
+ };
+
+ argc = parse_options(argc, argv, prefix, options,
+ git_stash_clear_usage,
+ PARSE_OPT_STOP_AT_NON_OPTION);
+
+ if (argc)
+ return error(_("git stash clear with parameters is "
+ "unimplemented"));
+
+ return do_clear_stash();
+}
+
+static int reset_tree(struct object_id *i_tree, int update, int reset)
+{
+ int nr_trees = 1;
+ struct unpack_trees_options opts;
+ struct tree_desc t[MAX_UNPACK_TREES];
+ struct tree *tree;
+ struct lock_file lock_file = LOCK_INIT;
+
+ read_cache_preload(NULL);
+ if (refresh_cache(REFRESH_QUIET))
+ return -1;
+
+ hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
+
+ memset(&opts, 0, sizeof(opts));
+
+ tree = parse_tree_indirect(i_tree);
+ if (parse_tree(tree))
+ return -1;
+
+ init_tree_desc(t, tree->buffer, tree->size);
+
+ opts.head_idx = 1;
+ opts.src_index = &the_index;
+ opts.dst_index = &the_index;
+ opts.merge = 1;
+ opts.reset = reset;
+ opts.update = update;
+ opts.fn = oneway_merge;
+
+ if (unpack_trees(nr_trees, t, &opts))
+ return -1;
+
+ if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+ return error(_("unable to write new index file"));
+
+ return 0;
+}
+
+static int diff_tree_binary(struct strbuf *out, struct object_id *w_commit)
+{
+ struct child_process cp = CHILD_PROCESS_INIT;
+ const char *w_commit_hex = oid_to_hex(w_commit);
+
+ /*
+ * Diff-tree would not be very hard to replace with a native function,
+ * however it should be done together with apply_cached.
+ */
+ cp.git_cmd = 1;
+ argv_array_pushl(&cp.args, "diff-tree", "--binary", NULL);
+ argv_array_pushf(&cp.args, "%s^2^..%s^2", w_commit_hex, w_commit_hex);
+
+ return pipe_command(&cp, NULL, 0, out, 0, NULL, 0);
+}
+
+static int apply_cached(struct strbuf *out)
+{
+ struct child_process cp = CHILD_PROCESS_INIT;
+
+ /*
+ * Apply currently only reads either from stdin or a file, thus
+ * apply_all_patches would have to be updated to optionally take a
+ * buffer.
+ */
+ cp.git_cmd = 1;
+ argv_array_pushl(&cp.args, "apply", "--cached", NULL);
+ return pipe_command(&cp, out->buf, out->len, NULL, 0, NULL, 0);
+}
+
+static int reset_head(void)
+{
+ struct child_process cp = CHILD_PROCESS_INIT;
+
+ /*
+ * Reset is overall quite simple, however there is no current public
+ * API for resetting.
+ */
+ cp.git_cmd = 1;
+ argv_array_push(&cp.args, "reset");
+
+ return run_command(&cp);
+}
+
+static void add_diff_to_buf(struct diff_queue_struct *q,
+ struct diff_options *options,
+ void *data)
+{
+ int i;
+
+ for (i = 0; i < q->nr; i++) {
+ strbuf_addstr(data, q->queue[i]->one->path);
+
+ /* NUL-terminate: will be fed to update-index -z */
+ strbuf_addch(data, '\0');
+ }
+}
+
+static int get_newly_staged(struct strbuf *out, struct object_id *c_tree)
+{
+ struct child_process cp = CHILD_PROCESS_INIT;
+ const char *c_tree_hex = oid_to_hex(c_tree);
+
+ /*
+ * diff-index is very similar to diff-tree above, and should be
+ * converted together with update_index.
+ */
+ cp.git_cmd = 1;
+ argv_array_pushl(&cp.args, "diff-index", "--cached", "--name-only",
+ "--diff-filter=A", NULL);
+ argv_array_push(&cp.args, c_tree_hex);
+ return pipe_command(&cp, NULL, 0, out, 0, NULL, 0);
+}
+
+static int update_index(struct strbuf *out)
+{
+ struct child_process cp = CHILD_PROCESS_INIT;
+
+ /*
+ * Update-index is very complicated and may need to have a public
+ * function exposed in order to remove this forking.
+ */
+ cp.git_cmd = 1;
+ argv_array_pushl(&cp.args, "update-index", "--add", "--stdin", NULL);
+ return pipe_command(&cp, out->buf, out->len, NULL, 0, NULL, 0);
+}
+
+static int restore_untracked(struct object_id *u_tree)
+{
+ int res;
+ struct child_process cp = CHILD_PROCESS_INIT;
+
+ /*
+ * We need to run restore files from a given index, but without
+ * affecting the current index, so we use GIT_INDEX_FILE with
+ * run_command to fork processes that will not interfere.
+ */
+ cp.git_cmd = 1;
+ argv_array_push(&cp.args, "read-tree");
+ argv_array_push(&cp.args, oid_to_hex(u_tree));
+ argv_array_pushf(&cp.env_array, "GIT_INDEX_FILE=%s",
+ stash_index_path.buf);
+ if (run_command(&cp)) {
+ remove_path(stash_index_path.buf);
+ return -1;
+ }
+
+ child_process_init(&cp);
+ cp.git_cmd = 1;
+ argv_array_pushl(&cp.args, "checkout-index", "--all", NULL);
+ argv_array_pushf(&cp.env_array, "GIT_INDEX_FILE=%s",
+ stash_index_path.buf);
+
+ res = run_command(&cp);
+ remove_path(stash_index_path.buf);
+ return res;
+}
+
+static int do_apply_stash(const char *prefix, struct stash_info *info,
+ int index, int quiet)
+{
+ int ret;
+ int has_index = index;
+ struct merge_options o;
+ struct object_id c_tree;
+ struct object_id index_tree;
+ struct commit *result;
+ const struct object_id *bases[1];
+
+ read_cache_preload(NULL);
+ if (refresh_cache(REFRESH_QUIET))
+ return -1;
+
+ if (write_cache_as_tree(&c_tree, 0, NULL))
+ return error(_("cannot apply a stash in the middle of a merge"));
+
+ if (index) {
+ if (oideq(&info->b_tree, &info->i_tree) ||
+ oideq(&c_tree, &info->i_tree)) {
+ has_index = 0;
+ } else {
+ struct strbuf out = STRBUF_INIT;
+
+ if (diff_tree_binary(&out, &info->w_commit)) {
+ strbuf_release(&out);
+ return error(_("could not generate diff %s^!."),
+ oid_to_hex(&info->w_commit));
+ }
+
+ ret = apply_cached(&out);
+ strbuf_release(&out);
+ if (ret)
+ return error(_("conflicts in index."
+ "Try without --index."));
+
+ discard_cache();
+ read_cache();
+ if (write_cache_as_tree(&index_tree, 0, NULL))
+ return error(_("could not save index tree"));
+
+ reset_head();
+ }
+ }
+
+ if (info->has_u && restore_untracked(&info->u_tree))
+ return error(_("could not restore untracked files from stash"));
+
+ init_merge_options(&o, the_repository);
+
+ o.branch1 = "Updated upstream";
+ o.branch2 = "Stashed changes";
+
+ if (oideq(&info->b_tree, &c_tree))
+ o.branch1 = "Version stash was based on";
+
+ if (quiet)
+ o.verbosity = 0;
+
+ if (o.verbosity >= 3)
+ printf_ln(_("Merging %s with %s"), o.branch1, o.branch2);
+
+ bases[0] = &info->b_tree;
+
+ ret = merge_recursive_generic(&o, &c_tree, &info->w_tree, 1, bases,
+ &result);
+ if (ret) {
+ rerere(0);
+
+ if (index)
+ fprintf_ln(stderr, _("Index was not unstashed."));
+
+ return ret;
+ }
+
+ if (has_index) {
+ if (reset_tree(&index_tree, 0, 0))
+ return -1;
+ } else {
+ struct strbuf out = STRBUF_INIT;
+
+ if (get_newly_staged(&out, &c_tree)) {
+ strbuf_release(&out);
+ return -1;
+ }
+
+ if (reset_tree(&c_tree, 0, 1)) {
+ strbuf_release(&out);
+ return -1;
+ }
+
+ ret = update_index(&out);
+ strbuf_release(&out);
+ if (ret)
+ return -1;
+
+ discard_cache();
+ }
+
+ if (quiet) {
+ if (refresh_cache(REFRESH_QUIET))
+ warning("could not refresh index");
+ } else {
+ struct child_process cp = CHILD_PROCESS_INIT;
+
+ /*
+ * Status is quite simple and could be replaced with calls to
+ * wt_status in the future, but it adds complexities which may
+ * require more tests.
+ */
+ cp.git_cmd = 1;
+ cp.dir = prefix;
+ argv_array_push(&cp.args, "status");
+ run_command(&cp);
+ }
+
+ return 0;
+}
+
+static int apply_stash(int argc, const char **argv, const char *prefix)
+{
+ int ret;
+ int quiet = 0;
+ int index = 0;
+ struct stash_info info;
+ struct option options[] = {
+ OPT__QUIET(&quiet, N_("be quiet, only report errors")),
+ OPT_BOOL(0, "index", &index,
+ N_("attempt to recreate the index")),
+ OPT_END()
+ };
+
+ argc = parse_options(argc, argv, prefix, options,
+ git_stash_apply_usage, 0);
+
+ if (get_stash_info(&info, argc, argv))
+ return -1;
+
+ ret = do_apply_stash(prefix, &info, index, quiet);
+ free_stash_info(&info);
+ return ret;
+}
+
+static int do_drop_stash(struct stash_info *info, int quiet)
+{
+ int ret;
+ struct child_process cp_reflog = CHILD_PROCESS_INIT;
+ struct child_process cp = CHILD_PROCESS_INIT;
+
+ /*
+ * reflog does not provide a simple function for deleting refs. One will
+ * need to be added to avoid implementing too much reflog code here
+ */
+
+ cp_reflog.git_cmd = 1;
+ argv_array_pushl(&cp_reflog.args, "reflog", "delete", "--updateref",
+ "--rewrite", NULL);
+ argv_array_push(&cp_reflog.args, info->revision.buf);
+ ret = run_command(&cp_reflog);
+ if (!ret) {
+ if (!quiet)
+ printf_ln(_("Dropped %s (%s)"), info->revision.buf,
+ oid_to_hex(&info->w_commit));
+ } else {
+ return error(_("%s: Could not drop stash entry"),
+ info->revision.buf);
+ }
+
+ /*
+ * This could easily be replaced by get_oid, but currently it will throw
+ * a fatal error when a reflog is empty, which we can not recover from.
+ */
+ cp.git_cmd = 1;
+ /* Even though --quiet is specified, rev-parse still outputs the hash */
+ cp.no_stdout = 1;
+ argv_array_pushl(&cp.args, "rev-parse", "--verify", "--quiet", NULL);
+ argv_array_pushf(&cp.args, "%s@{0}", ref_stash);
+ ret = run_command(&cp);
+
+ /* do_clear_stash if we just dropped the last stash entry */
+ if (ret)
+ do_clear_stash();
+
+ return 0;
+}
+
+static void assert_stash_ref(struct stash_info *info)
+{
+ if (!info->is_stash_ref) {
+ error(_("'%s' is not a stash reference"), info->revision.buf);
+ free_stash_info(info);
+ exit(1);
+ }
+}
+
+static int drop_stash(int argc, const char **argv, const char *prefix)
+{
+ int ret;
+ int quiet = 0;
+ struct stash_info info;
+ struct option options[] = {
+ OPT__QUIET(&quiet, N_("be quiet, only report errors")),
+ OPT_END()
+ };
+
+ argc = parse_options(argc, argv, prefix, options,
+ git_stash_drop_usage, 0);
+
+ if (get_stash_info(&info, argc, argv))
+ return -1;
+
+ assert_stash_ref(&info);
+
+ ret = do_drop_stash(&info, quiet);
+ free_stash_info(&info);
+ return ret;
+}
+
+static int pop_stash(int argc, const char **argv, const char *prefix)
+{
+ int ret;
+ int index = 0;
+ int quiet = 0;
+ struct stash_info info;
+ struct option options[] = {
+ OPT__QUIET(&quiet, N_("be quiet, only report errors")),
+ OPT_BOOL(0, "index", &index,
+ N_("attempt to recreate the index")),
+ OPT_END()
+ };
+
+ argc = parse_options(argc, argv, prefix, options,
+ git_stash_pop_usage, 0);
+
+ if (get_stash_info(&info, argc, argv))
+ return -1;
+
+ assert_stash_ref(&info);
+ if ((ret = do_apply_stash(prefix, &info, index, quiet)))
+ printf_ln(_("The stash entry is kept in case "
+ "you need it again."));
+ else
+ ret = do_drop_stash(&info, quiet);
+
+ free_stash_info(&info);
+ return ret;
+}
+
+static int branch_stash(int argc, const char **argv, const char *prefix)
+{
+ int ret;
+ const char *branch = NULL;
+ struct stash_info info;
+ struct child_process cp = CHILD_PROCESS_INIT;
+ struct option options[] = {
+ OPT_END()
+ };
+
+ argc = parse_options(argc, argv, prefix, options,
+ git_stash_branch_usage, 0);
+
+ if (!argc) {
+ fprintf_ln(stderr, _("No branch name specified"));
+ return -1;
+ }
+
+ branch = argv[0];
+
+ if (get_stash_info(&info, argc - 1, argv + 1))
+ return -1;
+
+ cp.git_cmd = 1;
+ argv_array_pushl(&cp.args, "checkout", "-b", NULL);
+ argv_array_push(&cp.args, branch);
+ argv_array_push(&cp.args, oid_to_hex(&info.b_commit));
+ ret = run_command(&cp);
+ if (!ret)
+ ret = do_apply_stash(prefix, &info, 1, 0);
+ if (!ret && info.is_stash_ref)
+ ret = do_drop_stash(&info, 0);
+
+ free_stash_info(&info);
+
+ return ret;
+}
+
+static int list_stash(int argc, const char **argv, const char *prefix)
+{
+ struct child_process cp = CHILD_PROCESS_INIT;
+ struct option options[] = {
+ OPT_END()
+ };
+
+ argc = parse_options(argc, argv, prefix, options,
+ git_stash_list_usage,
+ PARSE_OPT_KEEP_UNKNOWN);
+
+ if (!ref_exists(ref_stash))
+ return 0;
+
+ cp.git_cmd = 1;
+ argv_array_pushl(&cp.args, "log", "--format=%gd: %gs", "-g",
+ "--first-parent", "-m", NULL);
+ argv_array_pushv(&cp.args, argv);
+ argv_array_push(&cp.args, ref_stash);
+ argv_array_push(&cp.args, "--");
+ return run_command(&cp);
+}
+
+static int show_stat = 1;
+static int show_patch;
+
+static int git_stash_config(const char *var, const char *value, void *cb)
+{
+ if (!strcmp(var, "stash.showstat")) {
+ show_stat = git_config_bool(var, value);
+ return 0;
+ }
+ if (!strcmp(var, "stash.showpatch")) {
+ show_patch = git_config_bool(var, value);
+ return 0;
+ }
+ return git_default_config(var, value, cb);
+}
+
+static int show_stash(int argc, const char **argv, const char *prefix)
+{
+ int i;
+ int opts = 0;
+ int ret = 0;
+ struct stash_info info;
+ struct rev_info rev;
+ struct argv_array stash_args = ARGV_ARRAY_INIT;
+ struct option options[] = {
+ OPT_END()
+ };
+
+ init_diff_ui_defaults();
+ git_config(git_diff_ui_config, NULL);
+ init_revisions(&rev, prefix);
+
+ for (i = 1; i < argc; i++) {
+ if (argv[i][0] != '-')
+ argv_array_push(&stash_args, argv[i]);
+ else
+ opts++;
+ }
+
+ ret = get_stash_info(&info, stash_args.argc, stash_args.argv);
+ argv_array_clear(&stash_args);
+ if (ret)
+ return -1;
+
+ /*
+ * The config settings are applied only if there are not passed
+ * any options.
+ */
+ if (!opts) {
+ git_config(git_stash_config, NULL);
+ if (show_stat)
+ rev.diffopt.output_format = DIFF_FORMAT_DIFFSTAT;
+
+ if (show_patch)
+ rev.diffopt.output_format |= DIFF_FORMAT_PATCH;
+
+ if (!show_stat && !show_patch) {
+ free_stash_info(&info);
+ return 0;
+ }
+ }
+
+ argc = setup_revisions(argc, argv, &rev, NULL);
+ if (argc > 1) {
+ free_stash_info(&info);
+ usage_with_options(git_stash_show_usage, options);
+ }
+ if (!rev.diffopt.output_format) {
+ rev.diffopt.output_format = DIFF_FORMAT_PATCH;
+ diff_setup_done(&rev.diffopt);
+ }
+
+ rev.diffopt.flags.recursive = 1;
+ setup_diff_pager(&rev.diffopt);
+ diff_tree_oid(&info.b_commit, &info.w_commit, "", &rev.diffopt);
+ log_tree_diff_flush(&rev);
+
+ free_stash_info(&info);
+ return diff_result_code(&rev.diffopt, 0);
+}
+
+static int do_store_stash(const struct object_id *w_commit, const char *stash_msg,
+ int quiet)
+{
+ if (!stash_msg)
+ stash_msg = "Created via \"git stash store\".";
+
+ if (update_ref(stash_msg, ref_stash, w_commit, NULL,
+ REF_FORCE_CREATE_REFLOG,
+ quiet ? UPDATE_REFS_QUIET_ON_ERR :
+ UPDATE_REFS_MSG_ON_ERR)) {
+ if (!quiet) {
+ fprintf_ln(stderr, _("Cannot update %s with %s"),
+ ref_stash, oid_to_hex(w_commit));
+ }
+ return -1;
+ }
+
+ return 0;
+}
+
+static int store_stash(int argc, const char **argv, const char *prefix)
+{
+ int quiet = 0;
+ const char *stash_msg = NULL;
+ struct object_id obj;
+ struct object_context dummy;
+ struct option options[] = {
+ OPT__QUIET(&quiet, N_("be quiet")),
+ OPT_STRING('m', "message", &stash_msg, "message",
+ N_("stash message")),
+ OPT_END()
+ };
+
+ argc = parse_options(argc, argv, prefix, options,
+ git_stash_store_usage,
+ PARSE_OPT_KEEP_UNKNOWN);
+
+ if (argc != 1) {
+ if (!quiet)
+ fprintf_ln(stderr, _("\"git stash store\" requires one "
+ "<commit> argument"));
+ return -1;
+ }
+
+ if (get_oid_with_context(the_repository,
+ argv[0], quiet ? GET_OID_QUIETLY : 0, &obj,
+ &dummy)) {
+ if (!quiet)
+ fprintf_ln(stderr, _("Cannot update %s with %s"),
+ ref_stash, argv[0]);
+ return -1;
+ }
+
+ return do_store_stash(&obj, stash_msg, quiet);
+}
+
+static void add_pathspecs(struct argv_array *args,
+ const struct pathspec *ps) {
+ int i;
+
+ for (i = 0; i < ps->nr; i++)
+ argv_array_push(args, ps->items[i].original);
+}
+
+/*
+ * `untracked_files` will be filled with the names of untracked files.
+ * The return value is:
+ *
+ * = 0 if there are not any untracked files
+ * > 0 if there are untracked files
+ */
+static int get_untracked_files(const struct pathspec *ps, int include_untracked,
+ struct strbuf *untracked_files)
+{
+ int i;
+ int max_len;
+ int found = 0;
+ char *seen;
+ struct dir_struct dir;
+
+ memset(&dir, 0, sizeof(dir));
+ if (include_untracked != INCLUDE_ALL_FILES)
+ setup_standard_excludes(&dir);
+
+ seen = xcalloc(ps->nr, 1);
+
+ max_len = fill_directory(&dir, the_repository->index, ps);
+ for (i = 0; i < dir.nr; i++) {
+ struct dir_entry *ent = dir.entries[i];
+ if (dir_path_match(&the_index, ent, ps, max_len, seen)) {
+ found++;
+ strbuf_addstr(untracked_files, ent->name);
+ /* NUL-terminate: will be fed to update-index -z */
+ strbuf_addch(untracked_files, '\0');
+ }
+ free(ent);
+ }
+
+ free(seen);
+ free(dir.entries);
+ free(dir.ignored);
+ clear_directory(&dir);
+ return found;
+}
+
+/*
+ * The return value of `check_changes_tracked_files()` can be:
+ *
+ * < 0 if there was an error
+ * = 0 if there are no changes.
+ * > 0 if there are changes.
+ */
+static int check_changes_tracked_files(const struct pathspec *ps)
+{
+ int result;
+ struct rev_info rev;
+ struct object_id dummy;
+ int ret = 0;
+
+ /* No initial commit. */
+ if (get_oid("HEAD", &dummy))
+ return -1;
+
+ if (read_cache() < 0)
+ return -1;
+
+ init_revisions(&rev, NULL);
+ copy_pathspec(&rev.prune_data, ps);
+
+ rev.diffopt.flags.quick = 1;
+ rev.diffopt.flags.ignore_submodules = 1;
+ rev.abbrev = 0;
+
+ add_head_to_pending(&rev);
+ diff_setup_done(&rev.diffopt);
+
+ result = run_diff_index(&rev, 1);
+ if (diff_result_code(&rev.diffopt, result)) {
+ ret = 1;
+ goto done;
+ }
+
+ object_array_clear(&rev.pending);
+ result = run_diff_files(&rev, 0);
+ if (diff_result_code(&rev.diffopt, result)) {
+ ret = 1;
+ goto done;
+ }
+
+done:
+ clear_pathspec(&rev.prune_data);
+ return ret;
+}
+
+/*
+ * The function will fill `untracked_files` with the names of untracked files
+ * It will return 1 if there were any changes and 0 if there were not.
+ */
+static int check_changes(const struct pathspec *ps, int include_untracked,
+ struct strbuf *untracked_files)
+{
+ int ret = 0;
+ if (check_changes_tracked_files(ps))
+ ret = 1;
+
+ if (include_untracked && get_untracked_files(ps, include_untracked,
+ untracked_files))
+ ret = 1;
+
+ return ret;
+}
+
+static int save_untracked_files(struct stash_info *info, struct strbuf *msg,
+ struct strbuf files)
+{
+ int ret = 0;
+ struct strbuf untracked_msg = STRBUF_INIT;
+ struct child_process cp_upd_index = CHILD_PROCESS_INIT;
+ struct index_state istate = { NULL };
+
+ cp_upd_index.git_cmd = 1;
+ argv_array_pushl(&cp_upd_index.args, "update-index", "-z", "--add",
+ "--remove", "--stdin", NULL);
+ argv_array_pushf(&cp_upd_index.env_array, "GIT_INDEX_FILE=%s",
+ stash_index_path.buf);
+
+ strbuf_addf(&untracked_msg, "untracked files on %s\n", msg->buf);
+ if (pipe_command(&cp_upd_index, files.buf, files.len, NULL, 0,
+ NULL, 0)) {
+ ret = -1;
+ goto done;
+ }
+
+ if (write_index_as_tree(&info->u_tree, &istate, stash_index_path.buf, 0,
+ NULL)) {
+ ret = -1;
+ goto done;
+ }
+
+ if (commit_tree(untracked_msg.buf, untracked_msg.len,
+ &info->u_tree, NULL, &info->u_commit, NULL, NULL)) {
+ ret = -1;
+ goto done;
+ }
+
+done:
+ discard_index(&istate);
+ strbuf_release(&untracked_msg);
+ remove_path(stash_index_path.buf);
+ return ret;
+}
+
+static int stash_patch(struct stash_info *info, const struct pathspec *ps,
+ struct strbuf *out_patch, int quiet)
+{
+ int ret = 0;
+ struct child_process cp_read_tree = CHILD_PROCESS_INIT;
+ struct child_process cp_add_i = CHILD_PROCESS_INIT;
+ struct child_process cp_diff_tree = CHILD_PROCESS_INIT;
+ struct index_state istate = { NULL };
+
+ remove_path(stash_index_path.buf);
+
+ cp_read_tree.git_cmd = 1;
+ argv_array_pushl(&cp_read_tree.args, "read-tree", "HEAD", NULL);
+ argv_array_pushf(&cp_read_tree.env_array, "GIT_INDEX_FILE=%s",
+ stash_index_path.buf);
+ if (run_command(&cp_read_tree)) {
+ ret = -1;
+ goto done;
+ }
+
+ /* Find out what the user wants. */
+ cp_add_i.git_cmd = 1;
+ argv_array_pushl(&cp_add_i.args, "add--interactive", "--patch=stash",
+ "--", NULL);
+ add_pathspecs(&cp_add_i.args, ps);
+ argv_array_pushf(&cp_add_i.env_array, "GIT_INDEX_FILE=%s",
+ stash_index_path.buf);
+ if (run_command(&cp_add_i)) {
+ ret = -1;
+ goto done;
+ }
+
+ /* State of the working tree. */
+ if (write_index_as_tree(&info->w_tree, &istate, stash_index_path.buf, 0,
+ NULL)) {
+ ret = -1;
+ goto done;
+ }
+
+ cp_diff_tree.git_cmd = 1;
+ argv_array_pushl(&cp_diff_tree.args, "diff-tree", "-p", "HEAD",
+ oid_to_hex(&info->w_tree), "--", NULL);
+ if (pipe_command(&cp_diff_tree, NULL, 0, out_patch, 0, NULL, 0)) {
+ ret = -1;
+ goto done;
+ }
+
+ if (!out_patch->len) {
+ if (!quiet)
+ fprintf_ln(stderr, _("No changes selected"));
+ ret = 1;
+ }
+
+done:
+ discard_index(&istate);
+ remove_path(stash_index_path.buf);
+ return ret;
+}
+
+static int stash_working_tree(struct stash_info *info, const struct pathspec *ps)
+{
+ int ret = 0;
+ struct rev_info rev;
+ struct child_process cp_upd_index = CHILD_PROCESS_INIT;
+ struct strbuf diff_output = STRBUF_INIT;
+ struct index_state istate = { NULL };
+
+ init_revisions(&rev, NULL);
+ copy_pathspec(&rev.prune_data, ps);
+
+ set_alternate_index_output(stash_index_path.buf);
+ if (reset_tree(&info->i_tree, 0, 0)) {
+ ret = -1;
+ goto done;
+ }
+ set_alternate_index_output(NULL);
+
+ rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
+ rev.diffopt.format_callback = add_diff_to_buf;
+ rev.diffopt.format_callback_data = &diff_output;
+
+ if (read_cache_preload(&rev.diffopt.pathspec) < 0) {
+ ret = -1;
+ goto done;
+ }
+
+ add_pending_object(&rev, parse_object(the_repository, &info->b_commit),
+ "");
+ if (run_diff_index(&rev, 0)) {
+ ret = -1;
+ goto done;
+ }
+
+ cp_upd_index.git_cmd = 1;
+ argv_array_pushl(&cp_upd_index.args, "update-index", "-z", "--add",
+ "--remove", "--stdin", NULL);
+ argv_array_pushf(&cp_upd_index.env_array, "GIT_INDEX_FILE=%s",
+ stash_index_path.buf);
+
+ if (pipe_command(&cp_upd_index, diff_output.buf, diff_output.len,
+ NULL, 0, NULL, 0)) {
+ ret = -1;
+ goto done;
+ }
+
+ if (write_index_as_tree(&info->w_tree, &istate, stash_index_path.buf, 0,
+ NULL)) {
+ ret = -1;
+ goto done;
+ }
+
+done:
+ discard_index(&istate);
+ UNLEAK(rev);
+ object_array_clear(&rev.pending);
+ clear_pathspec(&rev.prune_data);
+ strbuf_release(&diff_output);
+ remove_path(stash_index_path.buf);
+ return ret;
+}
+
+static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_buf,
+ int include_untracked, int patch_mode,
+ struct stash_info *info, struct strbuf *patch,
+ int quiet)
+{
+ int ret = 0;
+ int flags = 0;
+ int untracked_commit_option = 0;
+ const char *head_short_sha1 = NULL;
+ const char *branch_ref = NULL;
+ const char *branch_name = "(no branch)";
+ struct commit *head_commit = NULL;
+ struct commit_list *parents = NULL;
+ struct strbuf msg = STRBUF_INIT;
+ struct strbuf commit_tree_label = STRBUF_INIT;
+ struct strbuf untracked_files = STRBUF_INIT;
+
+ prepare_fallback_ident("git stash", "git@stash");
+
+ read_cache_preload(NULL);
+ refresh_cache(REFRESH_QUIET);
+
+ if (get_oid("HEAD", &info->b_commit)) {
+ if (!quiet)
+ fprintf_ln(stderr, _("You do not have "
+ "the initial commit yet"));
+ ret = -1;
+ goto done;
+ } else {
+ head_commit = lookup_commit(the_repository, &info->b_commit);
+ }
+
+ if (!check_changes(ps, include_untracked, &untracked_files)) {
+ ret = 1;
+ goto done;
+ }
+
+ branch_ref = resolve_ref_unsafe("HEAD", 0, NULL, &flags);
+ if (flags & REF_ISSYMREF)
+ branch_name = strrchr(branch_ref, '/') + 1;
+ head_short_sha1 = find_unique_abbrev(&head_commit->object.oid,
+ DEFAULT_ABBREV);
+ strbuf_addf(&msg, "%s: %s ", branch_name, head_short_sha1);
+ pp_commit_easy(CMIT_FMT_ONELINE, head_commit, &msg);
+
+ strbuf_addf(&commit_tree_label, "index on %s\n", msg.buf);
+ commit_list_insert(head_commit, &parents);
+ if (write_cache_as_tree(&info->i_tree, 0, NULL) ||
+ commit_tree(commit_tree_label.buf, commit_tree_label.len,
+ &info->i_tree, parents, &info->i_commit, NULL, NULL)) {
+ if (!quiet)
+ fprintf_ln(stderr, _("Cannot save the current "
+ "index state"));
+ ret = -1;
+ goto done;
+ }
+
+ if (include_untracked) {
+ if (save_untracked_files(info, &msg, untracked_files)) {
+ if (!quiet)
+ fprintf_ln(stderr, _("Cannot save "
+ "the untracked files"));
+ ret = -1;
+ goto done;
+ }
+ untracked_commit_option = 1;
+ }
+ if (patch_mode) {
+ ret = stash_patch(info, ps, patch, quiet);
+ if (ret < 0) {
+ if (!quiet)
+ fprintf_ln(stderr, _("Cannot save the current "
+ "worktree state"));
+ goto done;
+ } else if (ret > 0) {
+ goto done;
+ }
+ } else {
+ if (stash_working_tree(info, ps)) {
+ if (!quiet)
+ fprintf_ln(stderr, _("Cannot save the current "
+ "worktree state"));
+ ret = -1;
+ goto done;
+ }
+ }
+
+ if (!stash_msg_buf->len)
+ strbuf_addf(stash_msg_buf, "WIP on %s", msg.buf);
+ else
+ strbuf_insertf(stash_msg_buf, 0, "On %s: ", branch_name);
+
+ /*
+ * `parents` will be empty after calling `commit_tree()`, so there is
+ * no need to call `free_commit_list()`
+ */
+ parents = NULL;
+ if (untracked_commit_option)
+ commit_list_insert(lookup_commit(the_repository,
+ &info->u_commit),
+ &parents);
+ commit_list_insert(lookup_commit(the_repository, &info->i_commit),
+ &parents);
+ commit_list_insert(head_commit, &parents);
+
+ if (commit_tree(stash_msg_buf->buf, stash_msg_buf->len, &info->w_tree,
+ parents, &info->w_commit, NULL, NULL)) {
+ if (!quiet)
+ fprintf_ln(stderr, _("Cannot record "
+ "working tree state"));
+ ret = -1;
+ goto done;
+ }
+
+done:
+ strbuf_release(&commit_tree_label);
+ strbuf_release(&msg);
+ strbuf_release(&untracked_files);
+ return ret;
+}
+
+static int create_stash(int argc, const char **argv, const char *prefix)
+{
+ int ret = 0;
+ struct strbuf stash_msg_buf = STRBUF_INIT;
+ struct stash_info info;
+ struct pathspec ps;
+
+ /* Starting with argv[1], since argv[0] is "create" */
+ strbuf_join_argv(&stash_msg_buf, argc - 1, ++argv, ' ');
+
+ memset(&ps, 0, sizeof(ps));
+ if (!check_changes_tracked_files(&ps))
+ return 0;
+
+ ret = do_create_stash(&ps, &stash_msg_buf, 0, 0, &info,
+ NULL, 0);
+ if (!ret)
+ printf_ln("%s", oid_to_hex(&info.w_commit));
+
+ strbuf_release(&stash_msg_buf);
+ return ret;
+}
+
+static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int quiet,
+ int keep_index, int patch_mode, int include_untracked)
+{
+ int ret = 0;
+ struct stash_info info;
+ struct strbuf patch = STRBUF_INIT;
+ struct strbuf stash_msg_buf = STRBUF_INIT;
+ struct strbuf untracked_files = STRBUF_INIT;
+
+ if (patch_mode && keep_index == -1)
+ keep_index = 1;
+
+ if (patch_mode && include_untracked) {
+ fprintf_ln(stderr, _("Can't use --patch and --include-untracked"
+ " or --all at the same time"));
+ ret = -1;
+ goto done;
+ }
+
+ read_cache_preload(NULL);
+ if (!include_untracked && ps->nr) {
+ int i;
+ char *ps_matched = xcalloc(ps->nr, 1);
+
+ for (i = 0; i < active_nr; i++)
+ ce_path_match(&the_index, active_cache[i], ps,
+ ps_matched);
+
+ if (report_path_error(ps_matched, ps, NULL)) {
+ fprintf_ln(stderr, _("Did you forget to 'git add'?"));
+ ret = -1;
+ free(ps_matched);
+ goto done;
+ }
+ free(ps_matched);
+ }
+
+ if (refresh_cache(REFRESH_QUIET)) {
+ ret = -1;
+ goto done;
+ }
+
+ if (!check_changes(ps, include_untracked, &untracked_files)) {
+ if (!quiet)
+ printf_ln(_("No local changes to save"));
+ goto done;
+ }
+
+ if (!reflog_exists(ref_stash) && do_clear_stash()) {
+ ret = -1;
+ if (!quiet)
+ fprintf_ln(stderr, _("Cannot initialize stash"));
+ goto done;
+ }
+
+ if (stash_msg)
+ strbuf_addstr(&stash_msg_buf, stash_msg);
+ if (do_create_stash(ps, &stash_msg_buf, include_untracked, patch_mode,
+ &info, &patch, quiet)) {
+ ret = -1;
+ goto done;
+ }
+
+ if (do_store_stash(&info.w_commit, stash_msg_buf.buf, 1)) {
+ ret = -1;
+ if (!quiet)
+ fprintf_ln(stderr, _("Cannot save the current status"));
+ goto done;
+ }
+
+ if (!quiet)
+ printf_ln(_("Saved working directory and index state %s"),
+ stash_msg_buf.buf);
+
+ if (!patch_mode) {
+ if (include_untracked && !ps->nr) {
+ struct child_process cp = CHILD_PROCESS_INIT;
+
+ cp.git_cmd = 1;
+ argv_array_pushl(&cp.args, "clean", "--force",
+ "--quiet", "-d", NULL);
+ if (include_untracked == INCLUDE_ALL_FILES)
+ argv_array_push(&cp.args, "-x");
+ if (run_command(&cp)) {
+ ret = -1;
+ goto done;
+ }
+ }
+ discard_cache();
+ if (ps->nr) {
+ struct child_process cp_add = CHILD_PROCESS_INIT;
+ struct child_process cp_diff = CHILD_PROCESS_INIT;
+ struct child_process cp_apply = CHILD_PROCESS_INIT;
+ struct strbuf out = STRBUF_INIT;
+
+ cp_add.git_cmd = 1;
+ argv_array_push(&cp_add.args, "add");
+ if (!include_untracked)
+ argv_array_push(&cp_add.args, "-u");
+ if (include_untracked == INCLUDE_ALL_FILES)
+ argv_array_push(&cp_add.args, "--force");
+ argv_array_push(&cp_add.args, "--");
+ add_pathspecs(&cp_add.args, ps);
+ if (run_command(&cp_add)) {
+ ret = -1;
+ goto done;
+ }
+
+ cp_diff.git_cmd = 1;
+ argv_array_pushl(&cp_diff.args, "diff-index", "-p",
+ "--cached", "--binary", "HEAD", "--",
+ NULL);
+ add_pathspecs(&cp_diff.args, ps);
+ if (pipe_command(&cp_diff, NULL, 0, &out, 0, NULL, 0)) {
+ ret = -1;
+ goto done;
+ }
+
+ cp_apply.git_cmd = 1;
+ argv_array_pushl(&cp_apply.args, "apply", "--index",
+ "-R", NULL);
+ if (pipe_command(&cp_apply, out.buf, out.len, NULL, 0,
+ NULL, 0)) {
+ ret = -1;
+ goto done;
+ }
+ } else {
+ struct child_process cp = CHILD_PROCESS_INIT;
+ cp.git_cmd = 1;
+ argv_array_pushl(&cp.args, "reset", "--hard", "-q",
+ NULL);
+ if (run_command(&cp)) {
+ ret = -1;
+ goto done;
+ }
+ }
+
+ if (keep_index == 1 && !is_null_oid(&info.i_tree)) {
+ struct child_process cp_ls = CHILD_PROCESS_INIT;
+ struct child_process cp_checkout = CHILD_PROCESS_INIT;
+ struct strbuf out = STRBUF_INIT;
+
+ if (reset_tree(&info.i_tree, 0, 1)) {
+ ret = -1;
+ goto done;
+ }
+
+ cp_ls.git_cmd = 1;
+ argv_array_pushl(&cp_ls.args, "ls-files", "-z",
+ "--modified", "--", NULL);
+
+ add_pathspecs(&cp_ls.args, ps);
+ if (pipe_command(&cp_ls, NULL, 0, &out, 0, NULL, 0)) {
+ ret = -1;
+ goto done;
+ }
+
+ cp_checkout.git_cmd = 1;
+ argv_array_pushl(&cp_checkout.args, "checkout-index",
+ "-z", "--force", "--stdin", NULL);
+ if (pipe_command(&cp_checkout, out.buf, out.len, NULL,
+ 0, NULL, 0)) {
+ ret = -1;
+ goto done;
+ }
+ }
+ goto done;
+ } else {
+ struct child_process cp = CHILD_PROCESS_INIT;
+
+ cp.git_cmd = 1;
+ argv_array_pushl(&cp.args, "apply", "-R", NULL);
+
+ if (pipe_command(&cp, patch.buf, patch.len, NULL, 0, NULL, 0)) {
+ if (!quiet)
+ fprintf_ln(stderr, _("Cannot remove "
+ "worktree changes"));
+ ret = -1;
+ goto done;
+ }
+
+ if (keep_index < 1) {
+ struct child_process cp = CHILD_PROCESS_INIT;
+
+ cp.git_cmd = 1;
+ argv_array_pushl(&cp.args, "reset", "-q", "--", NULL);
+ add_pathspecs(&cp.args, ps);
+ if (run_command(&cp)) {
+ ret = -1;
+ goto done;
+ }
+ }
+ goto done;
+ }
+
+done:
+ strbuf_release(&stash_msg_buf);
+ return ret;
+}
+
+static int push_stash(int argc, const char **argv, const char *prefix)
+{
+ int keep_index = -1;
+ int patch_mode = 0;
+ int include_untracked = 0;
+ int quiet = 0;
+ const char *stash_msg = NULL;
+ struct pathspec ps;
+ struct option options[] = {
+ OPT_BOOL('k', "keep-index", &keep_index,
+ N_("keep index")),
+ OPT_BOOL('p', "patch", &patch_mode,
+ N_("stash in patch mode")),
+ OPT__QUIET(&quiet, N_("quiet mode")),
+ OPT_BOOL('u', "include-untracked", &include_untracked,
+ N_("include untracked files in stash")),
+ OPT_SET_INT('a', "all", &include_untracked,
+ N_("include ignore files"), 2),
+ OPT_STRING('m', "message", &stash_msg, N_("message"),
+ N_("stash message")),
+ OPT_END()
+ };
+
+ if (argc)
+ argc = parse_options(argc, argv, prefix, options,
+ git_stash_push_usage,
+ 0);
+
+ parse_pathspec(&ps, 0, PATHSPEC_PREFER_FULL | PATHSPEC_PREFIX_ORIGIN,
+ prefix, argv);
+ return do_push_stash(&ps, stash_msg, quiet, keep_index, patch_mode,
+ include_untracked);
+}
+
+static int save_stash(int argc, const char **argv, const char *prefix)
+{
+ int keep_index = -1;
+ int patch_mode = 0;
+ int include_untracked = 0;
+ int quiet = 0;
+ int ret = 0;
+ const char *stash_msg = NULL;
+ struct pathspec ps;
+ struct strbuf stash_msg_buf = STRBUF_INIT;
+ struct option options[] = {
+ OPT_BOOL('k', "keep-index", &keep_index,
+ N_("keep index")),
+ OPT_BOOL('p', "patch", &patch_mode,
+ N_("stash in patch mode")),
+ OPT__QUIET(&quiet, N_("quiet mode")),
+ OPT_BOOL('u', "include-untracked", &include_untracked,
+ N_("include untracked files in stash")),
+ OPT_SET_INT('a', "all", &include_untracked,
+ N_("include ignore files"), 2),
+ OPT_STRING('m', "message", &stash_msg, "message",
+ N_("stash message")),
+ OPT_END()
+ };
+
+ argc = parse_options(argc, argv, prefix, options,
+ git_stash_save_usage,
+ PARSE_OPT_KEEP_DASHDASH);
+
+ if (argc)
+ stash_msg = strbuf_join_argv(&stash_msg_buf, argc, argv, ' ');
+
+ memset(&ps, 0, sizeof(ps));
+ ret = do_push_stash(&ps, stash_msg, quiet, keep_index,
+ patch_mode, include_untracked);
+
+ strbuf_release(&stash_msg_buf);
+ return ret;
+}
+
+static int use_builtin_stash(void)
+{
+ struct child_process cp = CHILD_PROCESS_INIT;
+ struct strbuf out = STRBUF_INIT;
+ int ret, env = git_env_bool("GIT_TEST_STASH_USE_BUILTIN", -1);
+
+ if (env != -1)
+ return env;
+
+ argv_array_pushl(&cp.args,
+ "config", "--bool", "stash.usebuiltin", NULL);
+ cp.git_cmd = 1;
+ if (capture_command(&cp, &out, 6)) {
+ strbuf_release(&out);
+ return 1;
+ }
+
+ strbuf_trim(&out);
+ ret = !strcmp("true", out.buf);
+ strbuf_release(&out);
+ return ret;
+}
+
+int cmd_stash(int argc, const char **argv, const char *prefix)
+{
+ int i = -1;
+ pid_t pid = getpid();
+ const char *index_file;
+ struct argv_array args = ARGV_ARRAY_INIT;
+
+ struct option options[] = {
+ OPT_END()
+ };
+
+ if (!use_builtin_stash()) {
+ const char *path = mkpath("%s/git-legacy-stash",
+ git_exec_path());
+
+ if (sane_execvp(path, (char **)argv) < 0)
+ die_errno(_("could not exec %s"), path);
+ else
+ BUG("sane_execvp() returned???");
+ }
+
+ prefix = setup_git_directory();
+ trace_repo_setup(prefix);
+ setup_work_tree();
+
+ git_config(git_diff_basic_config, NULL);
+
+ argc = parse_options(argc, argv, prefix, options, git_stash_usage,
+ PARSE_OPT_KEEP_UNKNOWN | PARSE_OPT_KEEP_DASHDASH);
+
+ index_file = get_index_file();
+ strbuf_addf(&stash_index_path, "%s.stash.%" PRIuMAX, index_file,
+ (uintmax_t)pid);
+
+ if (!argc)
+ return !!push_stash(0, NULL, prefix);
+ else if (!strcmp(argv[0], "apply"))
+ return !!apply_stash(argc, argv, prefix);
+ else if (!strcmp(argv[0], "clear"))
+ return !!clear_stash(argc, argv, prefix);
+ else if (!strcmp(argv[0], "drop"))
+ return !!drop_stash(argc, argv, prefix);
+ else if (!strcmp(argv[0], "pop"))
+ return !!pop_stash(argc, argv, prefix);
+ else if (!strcmp(argv[0], "branch"))
+ return !!branch_stash(argc, argv, prefix);
+ else if (!strcmp(argv[0], "list"))
+ return !!list_stash(argc, argv, prefix);
+ else if (!strcmp(argv[0], "show"))
+ return !!show_stash(argc, argv, prefix);
+ else if (!strcmp(argv[0], "store"))
+ return !!store_stash(argc, argv, prefix);
+ else if (!strcmp(argv[0], "create"))
+ return !!create_stash(argc, argv, prefix);
+ else if (!strcmp(argv[0], "push"))
+ return !!push_stash(argc, argv, prefix);
+ else if (!strcmp(argv[0], "save"))
+ return !!save_stash(argc, argv, prefix);
+ else if (*argv[0] != '-')
+ usage_msg_opt(xstrfmt(_("unknown subcommand: %s"), argv[0]),
+ git_stash_usage, options);
+
+ if (strcmp(argv[0], "-p")) {
+ while (++i < argc && strcmp(argv[i], "--")) {
+ /*
+ * `akpqu` is a string which contains all short options,
+ * except `-m` which is verified separately.
+ */
+ if ((strlen(argv[i]) == 2) && *argv[i] == '-' &&
+ strchr("akpqu", argv[i][1]))
+ continue;
+
+ if (!strcmp(argv[i], "--all") ||
+ !strcmp(argv[i], "--keep-index") ||
+ !strcmp(argv[i], "--no-keep-index") ||
+ !strcmp(argv[i], "--patch") ||
+ !strcmp(argv[i], "--quiet") ||
+ !strcmp(argv[i], "--include-untracked"))
+ continue;
+
+ /*
+ * `-m` and `--message=` are verified separately because
+ * they need to be immediately followed by a string
+ * (i.e.`-m"foobar"` or `--message="foobar"`).
+ */
+ if (starts_with(argv[i], "-m") ||
+ starts_with(argv[i], "--message="))
+ continue;
+
+ usage_with_options(git_stash_usage, options);
+ }
+ }
+
+ argv_array_push(&args, "push");
+ argv_array_pushv(&args, argv);
+ return !!push_stash(args.argc, args.argv, prefix);
+}
if (entries < 0)
die("cache corrupted");
+ the_index.updated_skipworktree = 1;
+
/*
* Custom copy of parse_options() because we want to handle
* filename arguments as they come.
struct strbuf sb_git = STRBUF_INIT, sb_repo = STRBUF_INIT;
struct strbuf sb = STRBUF_INIT;
const char *name;
- struct stat st;
struct child_process cp = CHILD_PROCESS_INIT;
struct argv_array child_env = ARGV_ARRAY_INIT;
- int counter = 0, len, ret;
+ unsigned int counter = 0;
+ int len, ret;
struct strbuf symref = STRBUF_INIT;
struct commit *commit = NULL;
int is_branch = 0;
if (safe_create_leading_directories_const(sb_repo.buf))
die_errno(_("could not create leading directories of '%s'"),
sb_repo.buf);
- while (!stat(sb_repo.buf, &st)) {
+
+ while (mkdir(sb_repo.buf, 0777)) {
counter++;
+ if ((errno != EEXIST) || !counter /* overflow */)
+ die_errno(_("could not create directory of '%s'"),
+ sb_repo.buf);
strbuf_setlen(&sb_repo, len);
strbuf_addf(&sb_repo, "%d", counter);
}
atexit(remove_junk);
sigchain_push_common(remove_junk_on_signal);
- if (mkdir(sb_repo.buf, 0777))
- die_errno(_("could not create directory of '%s'"), sb_repo.buf);
junk_git_dir = xstrdup(sb_repo.buf);
is_junk = 1;
struct cache_time timestamp;
unsigned name_hash_initialized : 1,
initialized : 1,
- drop_cache_tree : 1;
+ drop_cache_tree : 1,
+ updated_workdir : 1,
+ updated_skipworktree : 1;
struct hashmap name_hash;
struct hashmap dir_hash;
struct object_id oid;
extern const char *core_partial_clone_filter_default;
extern int repository_format_worktree_config;
+/*
+ * You _have_ to initialize a `struct repository_format` using
+ * `= REPOSITORY_FORMAT_INIT` before calling `read_repository_format()`.
+ */
struct repository_format {
int version;
int precious_objects;
struct string_list unknown_extensions;
};
+/*
+ * Always use this to initialize a `struct repository_format`
+ * to a well-defined, default state before calling
+ * `read_repository()`.
+ */
+#define REPOSITORY_FORMAT_INIT \
+{ \
+ .version = -1, \
+ .is_bare = -1, \
+ .hash_algo = GIT_HASH_SHA1, \
+ .unknown_extensions = STRING_LIST_INIT_DUP, \
+}
+
/*
* Read the repository format characteristics from the config file "path" into
- * "format" struct. Returns the numeric version. On error, -1 is returned,
- * format->version is set to -1, and all other fields in the struct are
- * undefined.
+ * "format" struct. Returns the numeric version. On error, or if no version is
+ * found in the configuration, -1 is returned, format->version is set to -1,
+ * and all other fields in the struct are set to the default configuration
+ * (REPOSITORY_FORMAT_INIT). Always initialize the struct using
+ * REPOSITORY_FORMAT_INIT before calling this function.
*/
int read_repository_format(struct repository_format *format, const char *path);
+/*
+ * Free the memory held onto by `format`, but not the struct itself.
+ * (No need to use this after `read_repository_format()` fails.)
+ */
+void clear_repository_format(struct repository_format *format);
+
/*
* Verify that the repository described by repository_format is something we
* can read. If it is, return 0. Otherwise, return -1, and "err" will describe
};
extern int get_oid(const char *str, struct object_id *oid);
+extern int get_oidf(struct object_id *oid, const char *fmt, ...);
extern int get_oid_commit(const char *str, struct object_id *oid);
extern int get_oid_committish(const char *str, struct object_id *oid);
extern int get_oid_tree(const char *str, struct object_id *oid);
extern const char *git_pager(int stdout_is_tty);
extern int is_terminal_dumb(void);
extern int git_ident_config(const char *, const char *, void *);
+/*
+ * Prepare an ident to fall back on if the user didn't configure it.
+ */
+void prepare_fallback_ident(const char *name, const char *email);
extern void reset_ident_date(void);
struct ident_split {
Documentation)
sudo apt-get -q update
sudo apt-get -q -y install asciidoc xmlto
+
+ test -n "$ALREADY_HAVE_ASCIIDOCTOR" ||
+ gem install --version 1.5.8 asciidoctor
;;
esac
. ${0%/*}/lib.sh
-test -n "$ALREADY_HAVE_ASCIIDOCTOR" ||
-gem install asciidoctor
+filter_log () {
+ sed -e '/^GIT_VERSION = /d' \
+ -e '/^ \* new asciidoc flags$/d' \
+ "$1"
+}
make check-builtins
make check-docs
# Build docs with AsciiDoc
-make doc > >(tee stdout.log) 2> >(tee stderr.log >&2)
-! test -s stderr.log
+make doc > >(tee stdout.log) 2> >(tee stderr.raw >&2)
+cat stderr.raw
+filter_log stderr.raw >stderr.log
+test ! -s stderr.log
test -s Documentation/git.html
test -s Documentation/git.xml
test -s Documentation/git.1
grep '<meta name="generator" content="AsciiDoc ' Documentation/git.html
-rm -f stdout.log stderr.log
+rm -f stdout.log stderr.log stderr.raw
check_unignored_build_artifacts
# Build docs with AsciiDoctor
make clean
-make USE_ASCIIDOCTOR=1 doc > >(tee stdout.log) 2> >(tee stderr.log >&2)
-sed '/^GIT_VERSION = / d' stderr.log
-! test -s stderr.log
+make USE_ASCIIDOCTOR=1 doc > >(tee stdout.log) 2> >(tee stderr.raw >&2)
+cat stderr.raw
+filter_log stderr.raw >stderr.log
+test ! -s stderr.log
test -s Documentation/git.html
grep '<meta name="generator" content="Asciidoctor ' Documentation/git.html
-rm -f stdout.log stderr.log
+rm -f stdout.log stderr.log stderr.raw
check_unignored_build_artifacts
save_good_tree
}
ret = !wildmatch(pattern.buf + prefix, text.buf + prefix,
- icase ? WM_CASEFOLD : 0);
+ WM_PATHNAME | (icase ? WM_CASEFOLD : 0));
if (!ret && !already_tried_absolute) {
/*
ifeq ($(filter no-error,$(DEVOPTS)),)
-CFLAGS += -Werror
+DEVELOPER_CFLAGS += -Werror
endif
ifneq ($(filter pedantic,$(DEVOPTS)),)
-CFLAGS += -pedantic
+DEVELOPER_CFLAGS += -pedantic
# don't warn for each N_ use
-CFLAGS += -DUSE_PARENS_AROUND_GETTEXT_N=0
-endif
-CFLAGS += -Wall
-CFLAGS += -Wdeclaration-after-statement
-CFLAGS += -Wformat-security
-CFLAGS += -Wno-format-zero-length
-CFLAGS += -Wold-style-definition
-CFLAGS += -Woverflow
-CFLAGS += -Wpointer-arith
-CFLAGS += -Wstrict-prototypes
-CFLAGS += -Wunused
-CFLAGS += -Wvla
+DEVELOPER_CFLAGS += -DUSE_PARENS_AROUND_GETTEXT_N=0
+endif
+DEVELOPER_CFLAGS += -Wall
+DEVELOPER_CFLAGS += -Wdeclaration-after-statement
+DEVELOPER_CFLAGS += -Wformat-security
+DEVELOPER_CFLAGS += -Wno-format-zero-length
+DEVELOPER_CFLAGS += -Wold-style-definition
+DEVELOPER_CFLAGS += -Woverflow
+DEVELOPER_CFLAGS += -Wpointer-arith
+DEVELOPER_CFLAGS += -Wstrict-prototypes
+DEVELOPER_CFLAGS += -Wunused
+DEVELOPER_CFLAGS += -Wvla
ifndef COMPILER_FEATURES
COMPILER_FEATURES := $(shell ./detect-compiler $(CC))
endif
ifneq ($(filter clang4,$(COMPILER_FEATURES)),)
-CFLAGS += -Wtautological-constant-out-of-range-compare
+DEVELOPER_CFLAGS += -Wtautological-constant-out-of-range-compare
endif
ifneq ($(or $(filter gcc6,$(COMPILER_FEATURES)),$(filter clang4,$(COMPILER_FEATURES))),)
-CFLAGS += -Wextra
+DEVELOPER_CFLAGS += -Wextra
# if a function is public, there should be a prototype and the right
# header file should be included. If not, it should be static.
-CFLAGS += -Wmissing-prototypes
+DEVELOPER_CFLAGS += -Wmissing-prototypes
ifeq ($(filter extra-all,$(DEVOPTS)),)
# These are disabled because we have these all over the place.
-CFLAGS += -Wno-empty-body
-CFLAGS += -Wno-missing-field-initializers
-CFLAGS += -Wno-sign-compare
-CFLAGS += -Wno-unused-parameter
+DEVELOPER_CFLAGS += -Wno-empty-body
+DEVELOPER_CFLAGS += -Wno-missing-field-initializers
+DEVELOPER_CFLAGS += -Wno-sign-compare
+DEVELOPER_CFLAGS += -Wno-unused-parameter
endif
endif
# not worth fixing since newer compilers correctly stop complaining
ifneq ($(filter gcc4,$(COMPILER_FEATURES)),)
ifeq ($(filter gcc5,$(COMPILER_FEATURES)),)
-CFLAGS += -Wno-uninitialized
+DEVELOPER_CFLAGS += -Wno-uninitialized
endif
endif
--- /dev/null
+@@
+expression str;
+identifier x, flexname;
+@@
+- FLEX_ALLOC_MEM(x, flexname, str, strlen(str));
++ FLEX_ALLOC_STR(x, flexname, str);
+
+@@
+expression str;
+identifier x, ptrname;
+@@
+- FLEXPTR_ALLOC_MEM(x, ptrname, str, strlen(str));
++ FLEXPTR_ALLOC_STR(x, ptrname, str);
__git_compute_all_commands ()
{
test -n "$__git_all_commands" ||
- __git_all_commands=$(git --list-cmds=main,others,alias,nohelpers)
+ __git_all_commands=$(__git --list-cmds=main,others,alias,nohelpers)
}
# Lists all set config variables starting with the given section prefix,
}
__git_mergetools_common="diffuse diffmerge ecmerge emerge kdiff3 meld opendiff
- tkdiff vimdiff gvimdiff xxdiff araxis p4merge bc codecompare
+ tkdiff vimdiff gvimdiff xxdiff araxis p4merge bc
+ codecompare smerge
"
_git_difftool ()
esac
if test -n "$GIT_TESTING_ALL_COMMAND_LIST"
then
- __gitcomp "$GIT_TESTING_ALL_COMMAND_LIST $(git --list-cmds=alias,list-guide) gitk"
+ __gitcomp "$GIT_TESTING_ALL_COMMAND_LIST $(__git --list-cmds=alias,list-guide) gitk"
else
- __gitcomp "$(git --list-cmds=main,nohelpers,alias,list-guide) gitk"
+ __gitcomp "$(__git --list-cmds=main,nohelpers,alias,list-guide) gitk"
fi
}
then
__gitcomp "$GIT_TESTING_PORCELAIN_COMMAND_LIST"
else
- __gitcomp "$(git --list-cmds=list-mainporcelain,others,nohelpers,alias,list-complete,config)"
+ __gitcomp "$(__git --list-cmds=list-mainporcelain,others,nohelpers,alias,list-complete,config)"
fi
;;
esac
git subtree merge --prefix=<prefix> <commit>
git subtree pull --prefix=<prefix> <repository> <ref>
git subtree push --prefix=<prefix> <repository> <ref>
-git subtree split --prefix=<prefix> <commit...>
+git subtree split --prefix=<prefix> <commit>
--
h,help show the help
q quiet
fi
}
+ensure_single_rev () {
+ if test $# -ne 1
+ then
+ die "You must provide exactly one revision. Got: '$@'"
+ fi
+}
while test $# -gt 0
do
then
revs=$(git rev-parse $default --revs-only "$@") || exit $?
dirs=$(git rev-parse --no-revs --no-flags "$@") || exit $?
+ ensure_single_rev $revs
if test -n "$dirs"
then
die "Error: Use --prefix instead of bare filenames."
}
cmd_add_commit () {
- revs=$(git rev-parse $default --revs-only "$@") || exit $?
- set -- $revs
- rev="$1"
+ rev=$(git rev-parse $default --revs-only "$@") || exit $?
+ ensure_single_rev $rev
debug "Adding $dir as '$rev'..."
git read-tree --prefix="$dir" $rev || exit $?
}
cmd_merge () {
- revs=$(git rev-parse $default --revs-only "$@") || exit $?
+ rev=$(git rev-parse $default --revs-only "$@") || exit $?
+ ensure_single_rev $rev
ensure_clean
- set -- $revs
- if test $# -ne 1
- then
- die "You must provide exactly one revision. Got: '$revs'"
- fi
- rev="$1"
-
if test -n "$squash"
then
first_split="$(find_latest_squash "$dir")"
if (start_async(&async))
return 0; /* error was already reported */
- if (strbuf_read(&nbuf, async.out, len) < 0) {
+ if (strbuf_read(&nbuf, async.out, 0) < 0) {
err = error(_("read from external filter '%s' failed"), cmd);
}
if (close(async.out)) {
return 1;
}
-static int parse_submodule_opt(struct diff_options *options, const char *value)
-{
- if (parse_submodule_params(options, value))
- die(_("Failed to parse --submodule option parameter: '%s'"),
- value);
- return 1;
-}
-
static const char diff_status_letters[] = {
DIFF_STATUS_ADDED,
DIFF_STATUS_COPIED,
return 1;
}
+static int diff_opt_anchored(const struct option *opt,
+ const char *arg, int unset)
+{
+ struct diff_options *options = opt->value;
+
+ BUG_ON_OPT_NEG(unset);
+ options->xdl_opts = DIFF_WITH_ALG(options, PATIENCE_DIFF);
+ ALLOC_GROW(options->anchors, options->anchors_nr + 1,
+ options->anchors_alloc);
+ options->anchors[options->anchors_nr++] = xstrdup(arg);
+ return 0;
+}
+
+static int diff_opt_binary(const struct option *opt,
+ const char *arg, int unset)
+{
+ struct diff_options *options = opt->value;
+
+ BUG_ON_OPT_NEG(unset);
+ BUG_ON_OPT_ARG(arg);
+ enable_patch_output(&options->output_format);
+ options->flags.binary = 1;
+ return 0;
+}
+
static int diff_opt_break_rewrites(const struct option *opt,
const char *arg, int unset)
{
return 0;
}
+static int diff_opt_color_words(const struct option *opt,
+ const char *arg, int unset)
+{
+ struct diff_options *options = opt->value;
+
+ BUG_ON_OPT_NEG(unset);
+ options->use_color = 1;
+ options->word_diff = DIFF_WORDS_COLOR;
+ options->word_regex = arg;
+ return 0;
+}
+
static int diff_opt_compact_summary(const struct option *opt,
const char *arg, int unset)
{
return 0;
}
+static int diff_opt_diff_algorithm(const struct option *opt,
+ const char *arg, int unset)
+{
+ struct diff_options *options = opt->value;
+ long value = parse_algorithm_value(arg);
+
+ BUG_ON_OPT_NEG(unset);
+ if (value < 0)
+ return error(_("option diff-algorithm accepts \"myers\", "
+ "\"minimal\", \"patience\" and \"histogram\""));
+
+ /* clear out previous settings */
+ DIFF_XDL_CLR(options, NEED_MINIMAL);
+ options->xdl_opts &= ~XDF_DIFF_ALGORITHM_MASK;
+ options->xdl_opts |= value;
+ return 0;
+}
+
static int diff_opt_dirstat(const struct option *opt,
const char *arg, int unset)
{
return 0;
}
+static int diff_opt_follow(const struct option *opt,
+ const char *arg, int unset)
+{
+ struct diff_options *options = opt->value;
+
+ BUG_ON_OPT_ARG(arg);
+ if (unset) {
+ options->flags.follow_renames = 0;
+ options->flags.default_follow_renames = 0;
+ } else {
+ options->flags.follow_renames = 1;
+ }
+ return 0;
+}
+
+static int diff_opt_ignore_submodules(const struct option *opt,
+ const char *arg, int unset)
+{
+ struct diff_options *options = opt->value;
+
+ BUG_ON_OPT_NEG(unset);
+ if (!arg)
+ arg = "all";
+ options->flags.override_submodule_config = 1;
+ handle_ignore_submodules_arg(options, arg);
+ return 0;
+}
+
static enum parse_opt_result diff_opt_output(struct parse_opt_ctx_t *ctx,
const struct option *opt,
const char *arg, int unset)
return 0;
}
+static int diff_opt_patience(const struct option *opt,
+ const char *arg, int unset)
+{
+ struct diff_options *options = opt->value;
+ int i;
+
+ BUG_ON_OPT_NEG(unset);
+ BUG_ON_OPT_ARG(arg);
+ options->xdl_opts = DIFF_WITH_ALG(options, PATIENCE_DIFF);
+ /*
+ * Both --patience and --anchored use PATIENCE_DIFF
+ * internally, so remove any anchors previously
+ * specified.
+ */
+ for (i = 0; i < options->anchors_nr; i++)
+ free(options->anchors[i]);
+ options->anchors_nr = 0;
+ return 0;
+}
+
static int diff_opt_relative(const struct option *opt,
const char *arg, int unset)
{
return 0;
}
+static int diff_opt_submodule(const struct option *opt,
+ const char *arg, int unset)
+{
+ struct diff_options *options = opt->value;
+
+ BUG_ON_OPT_NEG(unset);
+ if (!arg)
+ arg = "log";
+ if (parse_submodule_params(options, arg))
+ return error(_("failed to parse --submodule option parameter: '%s'"),
+ arg);
+ return 0;
+}
+
+static int diff_opt_textconv(const struct option *opt,
+ const char *arg, int unset)
+{
+ struct diff_options *options = opt->value;
+
+ BUG_ON_OPT_ARG(arg);
+ if (unset) {
+ options->flags.allow_textconv = 0;
+ } else {
+ options->flags.allow_textconv = 1;
+ options->flags.textconv_set_via_cmdline = 1;
+ }
+ return 0;
+}
+
static int diff_opt_unified(const struct option *opt,
const char *arg, int unset)
{
return 0;
}
+static int diff_opt_word_diff(const struct option *opt,
+ const char *arg, int unset)
+{
+ struct diff_options *options = opt->value;
+
+ BUG_ON_OPT_NEG(unset);
+ if (arg) {
+ if (!strcmp(arg, "plain"))
+ options->word_diff = DIFF_WORDS_PLAIN;
+ else if (!strcmp(arg, "color")) {
+ options->use_color = 1;
+ options->word_diff = DIFF_WORDS_COLOR;
+ }
+ else if (!strcmp(arg, "porcelain"))
+ options->word_diff = DIFF_WORDS_PORCELAIN;
+ else if (!strcmp(arg, "none"))
+ options->word_diff = DIFF_WORDS_NONE;
+ else
+ return error(_("bad --word-diff argument: %s"), arg);
+ } else {
+ if (options->word_diff == DIFF_WORDS_NONE)
+ options->word_diff = DIFF_WORDS_PLAIN;
+ }
+ return 0;
+}
+
+static int diff_opt_word_diff_regex(const struct option *opt,
+ const char *arg, int unset)
+{
+ struct diff_options *options = opt->value;
+
+ BUG_ON_OPT_NEG(unset);
+ if (options->word_diff == DIFF_WORDS_NONE)
+ options->word_diff = DIFF_WORDS_PLAIN;
+ options->word_regex = arg;
+ return 0;
+}
+
static void prep_parse_options(struct diff_options *options)
{
struct option parseopts[] = {
OPT_CALLBACK_F(0, "compact-summary", options, NULL,
N_("generate compact summary in diffstat"),
PARSE_OPT_NOARG, diff_opt_compact_summary),
+ OPT_CALLBACK_F(0, "binary", options, NULL,
+ N_("output a binary diff that can be applied"),
+ PARSE_OPT_NONEG | PARSE_OPT_NOARG, diff_opt_binary),
+ OPT_BOOL(0, "full-index", &options->flags.full_index,
+ N_("show full pre- and post-image object names on the \"index\" lines")),
+ OPT_COLOR_FLAG(0, "color", &options->use_color,
+ N_("show colored diff")),
OPT_CALLBACK_F(0, "output-indicator-new",
&options->output_indicators[OUTPUT_INDICATOR_NEW],
N_("<char>"),
0, PARSE_OPT_NONEG),
OPT_BOOL(0, "rename-empty", &options->flags.rename_empty,
N_("use empty blobs as rename source")),
+ OPT_CALLBACK_F(0, "follow", options, NULL,
+ N_("continue listing the history of a file beyond renames"),
+ PARSE_OPT_NOARG, diff_opt_follow),
OPT_GROUP(N_("Diff algorithm options")),
OPT_BIT(0, "minimal", &options->xdl_opts,
OPT_BIT_F(0, "ignore-blank-lines", &options->xdl_opts,
N_("ignore changes whose lines are all blank"),
XDF_IGNORE_BLANK_LINES, PARSE_OPT_NONEG),
+ OPT_BIT(0, "indent-heuristic", &options->xdl_opts,
+ N_("heuristic to shift diff hunk boundaries for easy reading"),
+ XDF_INDENT_HEURISTIC),
+ OPT_CALLBACK_F(0, "patience", options, NULL,
+ N_("generate diff using the \"patience diff\" algorithm"),
+ PARSE_OPT_NONEG | PARSE_OPT_NOARG,
+ diff_opt_patience),
+ OPT_BITOP(0, "histogram", &options->xdl_opts,
+ N_("generate diff using the \"histogram diff\" algorithm"),
+ XDF_HISTOGRAM_DIFF, XDF_DIFF_ALGORITHM_MASK),
+ OPT_CALLBACK_F(0, "diff-algorithm", options, N_("<algorithm>"),
+ N_("choose a diff algorithm"),
+ PARSE_OPT_NONEG, diff_opt_diff_algorithm),
+ OPT_CALLBACK_F(0, "anchored", options, N_("<text>"),
+ N_("generate diff using the \"anchored diff\" algorithm"),
+ PARSE_OPT_NONEG, diff_opt_anchored),
+ OPT_CALLBACK_F(0, "word-diff", options, N_("<mode>"),
+ N_("show word diff, using <mode> to delimit changed words"),
+ PARSE_OPT_NONEG | PARSE_OPT_OPTARG, diff_opt_word_diff),
+ OPT_CALLBACK_F(0, "word-diff-regex", options, N_("<regex>"),
+ N_("use <regex> to decide what a word is"),
+ PARSE_OPT_NONEG, diff_opt_word_diff_regex),
+ OPT_CALLBACK_F(0, "color-words", options, N_("<regex>"),
+ N_("equivalent to --word-diff=color --word-diff-regex=<regex>"),
+ PARSE_OPT_NONEG | PARSE_OPT_OPTARG, diff_opt_color_words),
OPT_GROUP(N_("Diff other options")),
OPT_CALLBACK_F(0, "relative", options, N_("<prefix>"),
N_("when run from subdir, exclude changes outside and show relative paths"),
PARSE_OPT_NONEG | PARSE_OPT_OPTARG,
diff_opt_relative),
+ OPT_BOOL('a', "text", &options->flags.text,
+ N_("treat all files as text")),
+ OPT_BOOL('R', NULL, &options->flags.reverse_diff,
+ N_("swap two inputs, reverse the diff")),
+ OPT_BOOL(0, "exit-code", &options->flags.exit_with_status,
+ N_("exit with 1 if there were differences, 0 otherwise")),
+ OPT_BOOL(0, "quiet", &options->flags.quick,
+ N_("disable all output of the program")),
+ OPT_BOOL(0, "ext-diff", &options->flags.allow_external,
+ N_("allow an external diff helper to be executed")),
+ OPT_CALLBACK_F(0, "textconv", options, NULL,
+ N_("run external text conversion filters when comparing binary files"),
+ PARSE_OPT_NOARG, diff_opt_textconv),
+ OPT_CALLBACK_F(0, "ignore-submodules", options, N_("<when>"),
+ N_("ignore changes to submodules in the diff generation"),
+ PARSE_OPT_NONEG | PARSE_OPT_OPTARG,
+ diff_opt_ignore_submodules),
+ OPT_CALLBACK_F(0, "submodule", options, N_("<format>"),
+ N_("specify how differences in submodules are shown"),
+ PARSE_OPT_NONEG | PARSE_OPT_OPTARG,
+ diff_opt_submodule),
{ OPTION_CALLBACK, 0, "output", options, N_("<file>"),
N_("Output to a specific file"),
PARSE_OPT_NONEG, NULL, 0, diff_opt_output },
if (ac)
return ac;
- /* xdiff options */
- if (!strcmp(arg, "--indent-heuristic"))
- DIFF_XDL_SET(options, INDENT_HEURISTIC);
- else if (!strcmp(arg, "--no-indent-heuristic"))
- DIFF_XDL_CLR(options, INDENT_HEURISTIC);
- else if (!strcmp(arg, "--patience")) {
- int i;
- options->xdl_opts = DIFF_WITH_ALG(options, PATIENCE_DIFF);
- /*
- * Both --patience and --anchored use PATIENCE_DIFF
- * internally, so remove any anchors previously
- * specified.
- */
- for (i = 0; i < options->anchors_nr; i++)
- free(options->anchors[i]);
- options->anchors_nr = 0;
- } else if (!strcmp(arg, "--histogram"))
- options->xdl_opts = DIFF_WITH_ALG(options, HISTOGRAM_DIFF);
- else if ((argcount = parse_long_opt("diff-algorithm", av, &optarg))) {
- long value = parse_algorithm_value(optarg);
- if (value < 0)
- return error("option diff-algorithm accepts \"myers\", "
- "\"minimal\", \"patience\" and \"histogram\"");
- /* clear out previous settings */
- DIFF_XDL_CLR(options, NEED_MINIMAL);
- options->xdl_opts &= ~XDF_DIFF_ALGORITHM_MASK;
- options->xdl_opts |= value;
- return argcount;
- } else if (skip_prefix(arg, "--anchored=", &arg)) {
- options->xdl_opts = DIFF_WITH_ALG(options, PATIENCE_DIFF);
- ALLOC_GROW(options->anchors, options->anchors_nr + 1,
- options->anchors_alloc);
- options->anchors[options->anchors_nr++] = xstrdup(arg);
- }
-
/* flags options */
- else if (!strcmp(arg, "--binary")) {
- enable_patch_output(&options->output_format);
- options->flags.binary = 1;
- }
- else if (!strcmp(arg, "--full-index"))
- options->flags.full_index = 1;
- else if (!strcmp(arg, "-a") || !strcmp(arg, "--text"))
- options->flags.text = 1;
- else if (!strcmp(arg, "-R"))
- options->flags.reverse_diff = 1;
- else if (!strcmp(arg, "--follow"))
- options->flags.follow_renames = 1;
- else if (!strcmp(arg, "--no-follow")) {
- options->flags.follow_renames = 0;
- options->flags.default_follow_renames = 0;
- } else if (skip_to_optional_arg_default(arg, "--color", &arg, "always")) {
- int value = git_config_colorbool(NULL, arg);
- if (value < 0)
- return error("option `color' expects \"always\", \"auto\", or \"never\"");
- options->use_color = value;
- }
- else if (!strcmp(arg, "--no-color"))
- options->use_color = 0;
- else if (!strcmp(arg, "--color-moved")) {
+ if (!strcmp(arg, "--color-moved")) {
if (diff_color_moved_default)
options->color_moved = diff_color_moved_default;
if (options->color_moved == COLOR_MOVED_NO)
if (cm & COLOR_MOVED_WS_ERROR)
return -1;
options->color_moved_ws_handling = cm;
- } else if (skip_to_optional_arg_default(arg, "--color-words", &options->word_regex, NULL)) {
- options->use_color = 1;
- options->word_diff = DIFF_WORDS_COLOR;
- }
- else if (!strcmp(arg, "--word-diff")) {
- if (options->word_diff == DIFF_WORDS_NONE)
- options->word_diff = DIFF_WORDS_PLAIN;
- }
- else if (skip_prefix(arg, "--word-diff=", &arg)) {
- if (!strcmp(arg, "plain"))
- options->word_diff = DIFF_WORDS_PLAIN;
- else if (!strcmp(arg, "color")) {
- options->use_color = 1;
- options->word_diff = DIFF_WORDS_COLOR;
- }
- else if (!strcmp(arg, "porcelain"))
- options->word_diff = DIFF_WORDS_PORCELAIN;
- else if (!strcmp(arg, "none"))
- options->word_diff = DIFF_WORDS_NONE;
- else
- die("bad --word-diff argument: %s", arg);
- }
- else if ((argcount = parse_long_opt("word-diff-regex", av, &optarg))) {
- if (options->word_diff == DIFF_WORDS_NONE)
- options->word_diff = DIFF_WORDS_PLAIN;
- options->word_regex = optarg;
- return argcount;
- }
- else if (!strcmp(arg, "--exit-code"))
- options->flags.exit_with_status = 1;
- else if (!strcmp(arg, "--quiet"))
- options->flags.quick = 1;
- else if (!strcmp(arg, "--ext-diff"))
- options->flags.allow_external = 1;
- else if (!strcmp(arg, "--no-ext-diff"))
- options->flags.allow_external = 0;
- else if (!strcmp(arg, "--textconv")) {
- options->flags.allow_textconv = 1;
- options->flags.textconv_set_via_cmdline = 1;
- } else if (!strcmp(arg, "--no-textconv"))
- options->flags.allow_textconv = 0;
- else if (skip_to_optional_arg_default(arg, "--ignore-submodules", &arg, "all")) {
- options->flags.override_submodule_config = 1;
- handle_ignore_submodules_arg(options, arg);
- } else if (skip_to_optional_arg_default(arg, "--submodule", &arg, "log"))
- return parse_submodule_opt(options, arg);
- else if (skip_prefix(arg, "--ws-error-highlight=", &arg))
+ } else if (skip_prefix(arg, "--ws-error-highlight=", &arg))
return parse_ws_error_highlight_opt(options, arg);
else if (!strcmp(arg, "--ita-invisible-in-index"))
options->ita_invisible_in_index = 1;
if (args->stateless_rpc) {
send_sideband(fd, -1, buf->buf, buf->len, LARGE_PACKET_MAX);
packet_flush(fd);
- } else
- write_or_die(fd, buf->buf, buf->len);
+ } else {
+ if (write_in_full(fd, buf->buf, buf->len) < 0)
+ die_errno(_("unable to write to remote"));
+ }
}
static void insert_one_alternate_object(struct fetch_negotiator *negotiator,
/* Send request */
packet_buf_flush(&req_buf);
- write_or_die(fd_out, req_buf.buf, req_buf.len);
+ if (write_in_full(fd_out, req_buf.buf, req_buf.len) < 0)
+ die_errno(_("unable to write request to remote"));
strbuf_release(&req_buf);
return ret;
+++ /dev/null
-#!/bin/sh
-#
-# Copyright (c) 2005 Junio C Hamano.
-#
-
-SUBDIRECTORY_OK=Yes
-OPTIONS_KEEPDASHDASH=
-OPTIONS_STUCKLONG=t
-OPTIONS_SPEC="\
-git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] [<upstream>] [<branch>]
-git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] --root [<branch>]
-git rebase --continue | --abort | --skip | --edit-todo
---
- Available options are
-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
-r,rebase-merges? try to rebase merges instead of skipping them
-p,preserve-merges! try to recreate merges instead of ignoring them
-s,strategy=! use the given merge strategy
-X,strategy-option=! pass the argument through to the merge strategy
-no-ff! cherry-pick all commits, even if unchanged
-f,force-rebase! cherry-pick all commits, even if unchanged
-m,merge! use merging strategies to rebase
-i,interactive! let the user edit the list of commits to rebase
-x,exec=! add exec lines after each commit of the editable list
-k,keep-empty preserve empty commits during rebase
-allow-empty-message allow rebasing commits with empty messages
-stat! display a diffstat of what changed upstream
-n,no-stat! do not show diffstat of what changed upstream
-verify allow pre-rebase hook to run
-rerere-autoupdate allow rerere to update index with resolved conflicts
-root! rebase all reachable commits up to the root(s)
-autosquash move commits that begin with squash!/fixup! under -i
-signoff add a Signed-off-by: line to each commit
-committer-date-is-author-date! passed to 'git am'
-ignore-date! passed to 'git am'
-whitespace=! passed to 'git apply'
-ignore-whitespace! passed to 'git apply'
-C=! passed to 'git apply'
-S,gpg-sign? GPG-sign commits
- Actions:
-continue! continue
-abort! abort and check out the original branch
-skip! skip current patch and continue
-edit-todo! edit the todo list during an interactive rebase
-quit! abort but keep HEAD where it is
-show-current-patch! show the patch file being applied or merged
-reschedule-failed-exec automatically reschedule failed exec commands
-"
-. git-sh-setup
-set_reflog_action rebase
-require_work_tree_exists
-cd_to_toplevel
-
-LF='
-'
-ok_to_skip_pre_rebase=
-
-squash_onto=
-unset onto
-unset restrict_revision
-cmd=
-strategy=
-strategy_opts=
-do_merge=
-merge_dir="$GIT_DIR"/rebase-merge
-apply_dir="$GIT_DIR"/rebase-apply
-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=
-git_format_patch_opt=
-rebase_root=
-force_rebase=
-allow_rerere_autoupdate=
-# Non-empty if a rebase was in progress when 'git rebase' was invoked
-in_progress=
-# One of {am, merge, interactive}
-type=
-# One of {"$GIT_DIR"/rebase-apply, "$GIT_DIR"/rebase-merge}
-state_dir=
-# One of {'', continue, skip, abort}, as parsed from command line
-action=
-rebase_merges=
-rebase_cousins=
-preserve_merges=
-autosquash=
-keep_empty=
-allow_empty_message=--allow-empty-message
-signoff=
-reschedule_failed_exec=
-test "$(git config --bool rebase.autosquash)" = "true" && autosquash=t
-case "$(git config --bool commit.gpgsign)" in
-true) gpg_sign_opt=-S ;;
-*) gpg_sign_opt= ;;
-esac
-test "$(git config --bool rebase.reschedulefailedexec)" = "true" &&
-reschedule_failed_exec=--reschedule-failed-exec
-. git-rebase--common
-
-read_basic_state () {
- test -f "$state_dir/head-name" &&
- test -f "$state_dir/onto" &&
- head_name=$(cat "$state_dir"/head-name) &&
- onto=$(cat "$state_dir"/onto) &&
- # We always write to orig-head, but interactive rebase used to write to
- # head. Fall back to reading from head to cover for the case that the
- # user upgraded git with an ongoing interactive rebase.
- if test -f "$state_dir"/orig-head
- then
- orig_head=$(cat "$state_dir"/orig-head)
- else
- orig_head=$(cat "$state_dir"/head)
- fi &&
- test -f "$state_dir"/quiet && GIT_QUIET=t
- test -f "$state_dir"/verbose && verbose=t
- test -f "$state_dir"/strategy && strategy="$(cat "$state_dir"/strategy)"
- test -f "$state_dir"/strategy_opts &&
- strategy_opts="$(cat "$state_dir"/strategy_opts)"
- test -f "$state_dir"/allow_rerere_autoupdate &&
- allow_rerere_autoupdate="$(cat "$state_dir"/allow_rerere_autoupdate)"
- test -f "$state_dir"/gpg_sign_opt &&
- gpg_sign_opt="$(cat "$state_dir"/gpg_sign_opt)"
- test -f "$state_dir"/signoff && {
- signoff="$(cat "$state_dir"/signoff)"
- force_rebase=t
- }
- test -f "$state_dir"/reschedule-failed-exec &&
- reschedule_failed_exec=t
-}
-
-finish_rebase () {
- rm -f "$(git rev-parse --git-path REBASE_HEAD)"
- apply_autostash &&
- { git gc --auto || true; } &&
- rm -rf "$state_dir"
-}
-
-run_interactive () {
- GIT_CHERRY_PICK_HELP="$resolvemsg"
- export GIT_CHERRY_PICK_HELP
-
- test -n "$keep_empty" && keep_empty="--keep-empty"
- test -n "$rebase_merges" && rebase_merges="--rebase-merges"
- test -n "$rebase_cousins" && rebase_cousins="--rebase-cousins"
- test -n "$autosquash" && autosquash="--autosquash"
- test -n "$verbose" && verbose="--verbose"
- test -n "$force_rebase" && force_rebase="--no-ff"
- test -n "$restrict_revision" && \
- restrict_revision="--restrict-revision=^$restrict_revision"
- test -n "$upstream" && upstream="--upstream=$upstream"
- test -n "$onto" && onto="--onto=$onto"
- test -n "$squash_onto" && squash_onto="--squash-onto=$squash_onto"
- test -n "$onto_name" && onto_name="--onto-name=$onto_name"
- test -n "$head_name" && head_name="--head-name=$head_name"
- test -n "$strategy" && strategy="--strategy=$strategy"
- test -n "$strategy_opts" && strategy_opts="--strategy-opts=$strategy_opts"
- test -n "$switch_to" && switch_to="--switch-to=$switch_to"
- test -n "$cmd" && cmd="--cmd=$cmd"
- test -n "$action" && action="--$action"
-
- exec git rebase--interactive "$action" "$keep_empty" "$rebase_merges" "$rebase_cousins" \
- "$upstream" "$onto" "$squash_onto" "$restrict_revision" \
- "$allow_empty_message" "$autosquash" "$verbose" \
- "$force_rebase" "$onto_name" "$head_name" "$strategy" \
- "$strategy_opts" "$cmd" "$switch_to" \
- "$allow_rerere_autoupdate" "$gpg_sign_opt" "$signoff" \
- "$reschedule_failed_exec"
-}
-
-run_specific_rebase () {
- if [ "$interactive_rebase" = implied ]; then
- GIT_SEQUENCE_EDITOR=:
- export GIT_SEQUENCE_EDITOR
- autosquash=
- fi
-
- if test -n "$interactive_rebase" -a -z "$preserve_merges"
- then
- run_interactive
- else
- . git-rebase--$type
-
- if test -z "$preserve_merges"
- then
- git_rebase__$type
- else
- git_rebase__preserve_merges
- fi
- fi
-
- ret=$?
- if test $ret -eq 0
- then
- finish_rebase
- elif test $ret -eq 2 # special exit status for rebase -p
- then
- apply_autostash &&
- rm -rf "$state_dir" &&
- die "Nothing to do"
- fi
- exit $ret
-}
-
-run_pre_rebase_hook () {
- if test -z "$ok_to_skip_pre_rebase" &&
- test -x "$(git rev-parse --git-path hooks/pre-rebase)"
- then
- "$(git rev-parse --git-path hooks/pre-rebase)" ${1+"$@"} ||
- die "$(gettext "The pre-rebase hook refused to rebase.")"
- fi
-}
-
-test -f "$apply_dir"/applying &&
- die "$(gettext "It looks like 'git am' is in progress. Cannot rebase.")"
-
-if test -d "$apply_dir"
-then
- type=am
- state_dir="$apply_dir"
-elif test -d "$merge_dir"
-then
- type=interactive
- if test -d "$merge_dir"/rewritten
- then
- type=preserve-merges
- interactive_rebase=explicit
- preserve_merges=t
- elif test -f "$merge_dir"/interactive
- then
- interactive_rebase=explicit
- fi
- state_dir="$merge_dir"
-fi
-test -n "$type" && in_progress=t
-
-total_argc=$#
-while test $# != 0
-do
- case "$1" in
- --no-verify)
- ok_to_skip_pre_rebase=yes
- ;;
- --verify)
- ok_to_skip_pre_rebase=
- ;;
- --continue|--skip|--abort|--quit|--edit-todo|--show-current-patch)
- test $total_argc -eq 2 || usage
- action=${1##--}
- ;;
- --onto=*)
- onto="${1#--onto=}"
- ;;
- --exec=*)
- cmd="${cmd}exec ${1#--exec=}${LF}"
- test -z "$interactive_rebase" && interactive_rebase=implied
- ;;
- --interactive)
- interactive_rebase=explicit
- ;;
- --keep-empty)
- keep_empty=yes
- ;;
- --allow-empty-message)
- allow_empty_message=--allow-empty-message
- ;;
- --no-keep-empty)
- keep_empty=
- ;;
- --rebase-merges)
- rebase_merges=t
- test -z "$interactive_rebase" && interactive_rebase=implied
- ;;
- --rebase-merges=*)
- rebase_merges=t
- case "${1#*=}" in
- rebase-cousins) rebase_cousins=t;;
- no-rebase-cousins) rebase_cousins=;;
- *) die "Unknown mode: $1";;
- esac
- test -z "$interactive_rebase" && interactive_rebase=implied
- ;;
- --preserve-merges)
- preserve_merges=t
- test -z "$interactive_rebase" && interactive_rebase=implied
- ;;
- --autosquash)
- autosquash=t
- ;;
- --no-autosquash)
- autosquash=
- ;;
- --fork-point)
- fork_point=t
- ;;
- --no-fork-point)
- fork_point=
- ;;
- --merge)
- do_merge=t
- ;;
- --strategy-option=*)
- strategy_opts="$strategy_opts $(git rev-parse --sq-quote "--${1#--strategy-option=}" | sed -e s/^.//)"
- do_merge=t
- test -z "$strategy" && strategy=recursive
- ;;
- --strategy=*)
- strategy="${1#--strategy=}"
- do_merge=t
- ;;
- --no-stat)
- diffstat=
- ;;
- --stat)
- diffstat=t
- ;;
- --autostash)
- autostash=true
- ;;
- --no-autostash)
- autostash=false
- ;;
- --verbose)
- verbose=t
- diffstat=t
- GIT_QUIET=
- ;;
- --quiet)
- GIT_QUIET=t
- git_am_opt="$git_am_opt -q"
- verbose=
- diffstat=
- ;;
- --whitespace=*)
- git_am_opt="$git_am_opt --whitespace=${1#--whitespace=}"
- case "${1#--whitespace=}" in
- fix|strip)
- force_rebase=t
- ;;
- warn|nowarn|error|error-all)
- ;; # okay, known whitespace option
- *)
- die "fatal: Invalid whitespace option: '${1#*=}'"
- ;;
- esac
- ;;
- --ignore-whitespace)
- git_am_opt="$git_am_opt $1"
- ;;
- --signoff)
- signoff=--signoff
- ;;
- --no-signoff)
- signoff=
- ;;
- --committer-date-is-author-date|--ignore-date)
- git_am_opt="$git_am_opt $1"
- force_rebase=t
- ;;
- -C*[!0-9]*)
- die "fatal: switch \`C' expects a numerical value"
- ;;
- -C*)
- git_am_opt="$git_am_opt $1"
- ;;
- --root)
- rebase_root=t
- ;;
- --force-rebase|--no-ff)
- force_rebase=t
- ;;
- --rerere-autoupdate|--no-rerere-autoupdate)
- allow_rerere_autoupdate="$1"
- ;;
- --gpg-sign)
- gpg_sign_opt=-S
- ;;
- --gpg-sign=*)
- gpg_sign_opt="-S${1#--gpg-sign=}"
- ;;
- --reschedule-failed-exec)
- reschedule_failed_exec=--reschedule-failed-exec
- ;;
- --no-reschedule-failed-exec)
- reschedule_failed_exec=
- ;;
- --)
- shift
- break
- ;;
- *)
- usage
- ;;
- esac
- shift
-done
-test $# -gt 2 && usage
-
-if test -n "$action"
-then
- test -z "$in_progress" && die "$(gettext "No rebase in progress?")"
- # Only interactive rebase uses detailed reflog messages
- if test -n "$interactive_rebase" && test "$GIT_REFLOG_ACTION" = rebase
- then
- GIT_REFLOG_ACTION="rebase -i ($action)"
- export GIT_REFLOG_ACTION
- fi
-fi
-
-if test "$action" = "edit-todo" && test -z "$interactive_rebase"
-then
- die "$(gettext "The --edit-todo action can only be used during interactive rebase.")"
-fi
-
-case "$action" in
-continue)
- # Sanity check
- git rev-parse --verify HEAD >/dev/null ||
- die "$(gettext "Cannot read HEAD")"
- git update-index --ignore-submodules --refresh &&
- git diff-files --quiet --ignore-submodules || {
- echo "$(gettext "You must edit all merge conflicts and then
-mark them as resolved using git add")"
- exit 1
- }
- read_basic_state
- run_specific_rebase
- ;;
-skip)
- output git reset --hard HEAD || exit $?
- read_basic_state
- run_specific_rebase
- ;;
-abort)
- git rerere clear
- read_basic_state
- case "$head_name" in
- refs/*)
- git symbolic-ref -m "rebase: aborting" HEAD $head_name ||
- die "$(eval_gettext "Could not move back to \$head_name")"
- ;;
- esac
- output git reset --hard $orig_head
- finish_rebase
- exit
- ;;
-quit)
- exec rm -rf "$state_dir"
- ;;
-edit-todo)
- run_specific_rebase
- ;;
-show-current-patch)
- run_specific_rebase
- die "BUG: run_specific_rebase is not supposed to return here"
- ;;
-esac
-
-# Make sure no rebase is in progress
-if test -n "$in_progress"
-then
- state_dir_base=${state_dir##*/}
- cmd_live_rebase="git rebase (--continue | --abort | --skip)"
- cmd_clear_stale_rebase="rm -fr \"$state_dir\""
- die "
-$(eval_gettext 'It seems that there is already a $state_dir_base directory, and
-I wonder if you are in the middle of another rebase. If that is the
-case, please try
- $cmd_live_rebase
-If that is not the case, please
- $cmd_clear_stale_rebase
-and run me again. I am stopping in case you still have something
-valuable there.')"
-fi
-
-if test -n "$rebase_root" && test -z "$onto"
-then
- test -z "$interactive_rebase" && interactive_rebase=implied
-fi
-
-if test -n "$keep_empty"
-then
- test -z "$interactive_rebase" && interactive_rebase=implied
-fi
-
-actually_interactive=
-if test -n "$interactive_rebase"
-then
- if test -z "$preserve_merges"
- then
- type=interactive
- else
- type=preserve-merges
- fi
- actually_interactive=t
- state_dir="$merge_dir"
-elif test -n "$do_merge"
-then
- interactive_rebase=implied
- type=interactive
- state_dir="$merge_dir"
-else
- type=am
- state_dir="$apply_dir"
-fi
-
-if test -t 2 && test -z "$GIT_QUIET"
-then
- git_format_patch_opt="$git_format_patch_opt --progress"
-fi
-
-incompatible_opts=$(echo " $git_am_opt " | \
- sed -e 's/ -q / /g' -e 's/^ \(.*\) $/\1/')
-if test -n "$incompatible_opts"
-then
- if test -n "$actually_interactive" || test "$do_merge"
- then
- die "$(gettext "fatal: cannot combine am options with either interactive or merge options")"
- fi
-fi
-
-if test -n "$signoff"
-then
- test -n "$preserve_merges" &&
- die "$(gettext "fatal: cannot combine '--signoff' with '--preserve-merges'")"
- git_am_opt="$git_am_opt $signoff"
- force_rebase=t
-fi
-
-if test -n "$preserve_merges"
-then
- # Note: incompatibility with --signoff handled in signoff block above
- # Note: incompatibility with --interactive is just a strong warning;
- # git-rebase.txt caveats with "unless you know what you are doing"
- test -n "$rebase_merges" &&
- die "$(gettext "fatal: cannot combine '--preserve-merges' with '--rebase-merges'")"
-
- test -n "$reschedule_failed_exec" &&
- die "$(gettext "error: cannot combine '--preserve-merges' with '--reschedule-failed-exec'")"
-fi
-
-if test -n "$rebase_merges"
-then
- test -n "$strategy_opts" &&
- die "$(gettext "fatal: cannot combine '--rebase-merges' with '--strategy-option'")"
- test -n "$strategy" &&
- die "$(gettext "fatal: cannot combine '--rebase-merges' with '--strategy'")"
-fi
-
-if test -z "$rebase_root"
-then
- case "$#" in
- 0)
- if ! upstream_name=$(git rev-parse --symbolic-full-name \
- --verify -q @{upstream} 2>/dev/null)
- then
- . git-parse-remote
- error_on_missing_default_upstream "rebase" "rebase" \
- "against" "git rebase $(gettext '<branch>')"
- fi
-
- test "$fork_point" = auto && fork_point=t
- ;;
- *) upstream_name="$1"
- if test "$upstream_name" = "-"
- then
- upstream_name="@{-1}"
- fi
- shift
- ;;
- esac
- upstream=$(peel_committish "${upstream_name}") ||
- die "$(eval_gettext "invalid upstream '\$upstream_name'")"
- upstream_arg="$upstream_name"
-else
- if test -z "$onto"
- then
- empty_tree=$(git hash-object -t tree /dev/null)
- onto=$(git commit-tree $empty_tree </dev/null)
- squash_onto="$onto"
- fi
- unset upstream_name
- unset upstream
- test $# -gt 1 && usage
- upstream_arg=--root
-fi
-
-# Make sure the branch to rebase onto is valid.
-onto_name=${onto-"$upstream_name"}
-case "$onto_name" in
-*...*)
- if left=${onto_name%...*} right=${onto_name#*...} &&
- onto=$(git merge-base --all ${left:-HEAD} ${right:-HEAD})
- then
- case "$onto" in
- ?*"$LF"?*)
- die "$(eval_gettext "\$onto_name: there are more than one merge bases")"
- ;;
- '')
- die "$(eval_gettext "\$onto_name: there is no merge base")"
- ;;
- esac
- else
- die "$(eval_gettext "\$onto_name: there is no merge base")"
- fi
- ;;
-*)
- onto=$(peel_committish "$onto_name") ||
- die "$(eval_gettext "Does not point to a valid commit: \$onto_name")"
- ;;
-esac
-
-# If the branch to rebase is given, that is the branch we will rebase
-# $branch_name -- branch/commit being rebased, or HEAD (already detached)
-# $orig_head -- commit object name of tip of the branch before rebasing
-# $head_name -- refs/heads/<that-branch> or "detached HEAD"
-switch_to=
-case "$#" in
-1)
- # Is it "rebase other $branchname" or "rebase other $commit"?
- branch_name="$1"
- switch_to="$1"
-
- # Is it a local branch?
- if git show-ref --verify --quiet -- "refs/heads/$branch_name" &&
- orig_head=$(git rev-parse -q --verify "refs/heads/$branch_name")
- then
- head_name="refs/heads/$branch_name"
- # If not is it a valid ref (branch or commit)?
- elif orig_head=$(git rev-parse -q --verify "$branch_name")
- then
- head_name="detached HEAD"
-
- else
- die "$(eval_gettext "fatal: no such branch/commit '\$branch_name'")"
- fi
- ;;
-0)
- # Do not need to switch branches, we are already on it.
- if branch_name=$(git symbolic-ref -q HEAD)
- then
- head_name=$branch_name
- branch_name=$(expr "z$branch_name" : 'zrefs/heads/\(.*\)')
- else
- head_name="detached HEAD"
- branch_name=HEAD
- fi
- orig_head=$(git rev-parse --verify HEAD) || exit
- ;;
-*)
- die "BUG: unexpected number of arguments left to parse"
- ;;
-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
- restrict_revision=$new_upstream
- fi
-fi
-
-if test "$autostash" = true && ! (require_clean_work_tree) 2>/dev/null
-then
- stash_sha1=$(git stash create "autostash") ||
- die "$(gettext 'Cannot autostash')"
-
- mkdir -p "$state_dir" &&
- echo $stash_sha1 >"$state_dir/autostash" &&
- stash_abbrev=$(git rev-parse --short $stash_sha1) &&
- echo "$(eval_gettext 'Created autostash: $stash_abbrev')" &&
- git reset --hard
-fi
-
-require_clean_work_tree "rebase" "$(gettext "Please commit or stash them.")"
-
-# Now we are rebasing commits $upstream..$orig_head (or with --root,
-# everything leading up to $orig_head) on top of $onto
-
-# Check if we are already based on $onto with linear history,
-# but this should be done only when upstream and onto are the same
-# and if this is not an interactive rebase.
-mb=$(git merge-base "$onto" "$orig_head")
-if test -z "$actually_interactive" && test "$upstream" = "$onto" &&
- test "$mb" = "$onto" && test -z "$restrict_revision" &&
- # linear history?
- ! (git rev-list --parents "$onto".."$orig_head" | sane_grep " .* ") > /dev/null
-then
- if test -z "$force_rebase"
- then
- # Lazily switch to the target branch if needed...
- test -z "$switch_to" ||
- GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $switch_to" \
- git checkout -q "$switch_to" --
- if test "$branch_name" = "HEAD" &&
- ! git symbolic-ref -q HEAD
- then
- say "$(eval_gettext "HEAD is up to date.")"
- else
- say "$(eval_gettext "Current branch \$branch_name is up to date.")"
- fi
- finish_rebase
- exit 0
- else
- if test "$branch_name" = "HEAD" &&
- ! git symbolic-ref -q HEAD
- then
- say "$(eval_gettext "HEAD is up to date, rebase forced.")"
- else
- say "$(eval_gettext "Current branch \$branch_name is up to date, rebase forced.")"
- fi
- fi
-fi
-
-# If a hook exists, give it a chance to interrupt
-run_pre_rebase_hook "$upstream_arg" "$@"
-
-if test -n "$diffstat"
-then
- if test -n "$verbose"
- then
- if test -z "$mb"
- then
- echo "$(eval_gettext "Changes to \$onto:")"
- else
- echo "$(eval_gettext "Changes from \$mb to \$onto:")"
- fi
- fi
- mb_tree="${mb:-$(git hash-object -t tree /dev/null)}"
- # We want color (if set), but no pager
- GIT_PAGER='' git diff --stat --summary "$mb_tree" "$onto"
-fi
-
-if test -z "$actually_interactive" && test "$mb" = "$orig_head"
-then
- say "$(eval_gettext "Fast-forwarded \$branch_name to \$onto_name.")"
- GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $onto_name" \
- git checkout -q "$onto^0" || die "could not detach HEAD"
- # If the $onto is a proper descendant of the tip of the branch, then
- # we just fast-forwarded.
- git update-ref ORIG_HEAD $orig_head
- move_to_original_branch
- finish_rebase
- exit 0
-fi
-
-test -n "$interactive_rebase" && run_specific_rebase
-
-# Detach HEAD and reset the tree
-say "$(gettext "First, rewinding head to replay your work on top of it...")"
-
-GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $onto_name" \
- git checkout -q "$onto^0" || die "could not detach HEAD"
-git update-ref ORIG_HEAD $orig_head
-
-if test -n "$rebase_root"
-then
- revisions="$onto..$orig_head"
-else
- revisions="${restrict_revision-$upstream}..$orig_head"
-fi
-
-run_specific_rebase
fi
}
+maybe_quiet () {
+ case "$1" in
+ --keep-stdout)
+ shift
+ if test -n "$GIT_QUIET"
+ then
+ "$@" 2>/dev/null
+ else
+ "$@"
+ fi
+ ;;
+ *)
+ if test -n "$GIT_QUIET"
+ then
+ "$@" >/dev/null 2>&1
+ else
+ "$@"
+ fi
+ ;;
+ esac
+}
+
create_stash () {
prepare_fallback_ident
done
git update-index -q --refresh
- if no_changes "$@"
+ if maybe_quiet no_changes "$@"
then
exit 0
fi
# state of the base commit
- if b_commit=$(git rev-parse --verify HEAD)
+ if b_commit=$(maybe_quiet --keep-stdout git rev-parse --verify HEAD)
then
head=$(git rev-list --oneline -n 1 HEAD --)
+ elif test -n "$GIT_QUIET"
+ then
+ exit 1
else
die "$(gettext "You do not have the initial commit yet")"
fi
test -n "$untracked" || git ls-files --error-unmatch -- "$@" >/dev/null || exit 1
git update-index -q --refresh
- if no_changes "$@"
+ if maybe_quiet no_changes "$@"
then
say "$(gettext "No local changes to save")"
exit 0
while test $# != 0
do
case "$1" in
+ -q|--quiet)
+ GIT_QUIET=t
+ ;;
--)
shift
break
fi
tools="$tools gvimdiff diffuse diffmerge ecmerge"
tools="$tools p4merge araxis bc codecompare"
+ tools="$tools smerge"
fi
case "${VISUAL:-$EDITOR}" in
*vim*)
case "$1" in
-h)
echo "$LONG_USAGE"
+ case "$0" in *git-legacy-stash) exit 129;; esac
exit
esac
fi
# is not reachable from a ref.
is_tip_reachable "$sm_path" "$sha1" ||
fetch_in_submodule "$sm_path" $depth ||
- say "$(eval_gettext "Unable to fetch in submodule path '\$displaypath'")"
+ say "$(eval_gettext "Unable to fetch in submodule path '\$displaypath'; trying to directly fetch \$sha1:")"
# Now we tried the usual fetch, but $sha1 may
# not be reachable from any of the refs
{
struct string_list list = STRING_LIST_INIT_DUP;
int i;
+ int nongit;
+
+ /*
+ * Set up the repository so we can pick up any repo-level config (like
+ * completion.commands).
+ */
+ setup_git_directory_gently(&nongit);
while (*spec) {
const char *sep = strchrnul(spec, ',');
{ "show-index", cmd_show_index },
{ "show-ref", cmd_show_ref, RUN_SETUP },
{ "stage", cmd_add, RUN_SETUP | NEED_WORK_TREE },
+ /*
+ * NEEDSWORK: Until the builtin stash is thoroughly robust and no
+ * longer needs redirection to the stash shell script this is kept as
+ * is, then should be changed to RUN_SETUP | NEED_WORK_TREE
+ */
+ { "stash", cmd_stash },
{ "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
{ "stripspace", cmd_stripspace },
{ "submodule--helper", cmd_submodule__helper, RUN_SETUP | SUPPORT_SUPER_PREFIX | NO_PARSEOPT },
# Bulgarian translation of gitk po-file.
-# Copyright (C) 2014, 2015 Alexander Shopov <ash@kambanaria.org>.
+# Copyright (C) 2014, 2015, 2019 Alexander Shopov <ash@kambanaria.org>.
# This file is distributed under the same license as the git package.
-# Alexander Shopov <ash@kambanaria.org>, 2014, 2015.
+# Alexander Shopov <ash@kambanaria.org>, 2014, 2015, 2019.
#
#
msgid ""
msgstr ""
"Project-Id-Version: gitk master\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-12-19 11:48+0200\n"
-"PO-Revision-Date: 2015-12-19 11:49+0200\n"
+"POT-Creation-Date: 2019-03-04 11:27+0100\n"
+"PO-Revision-Date: 2019-03-04 11:39+0100\n"
"Last-Translator: Alexander Shopov <ash@kambanaria.org>\n"
"Language-Team: Bulgarian <dict@fsa-bg.org>\n"
"Language: bg\n"
msgid "Couldn't get list of unmerged files:"
msgstr "Списъкът с неслети файлове не може да бъде получен:"
-#: gitk:212 gitk:2399
+#: gitk:212 gitk:2403
msgid "Color words"
msgstr "Оцветяване на думите"
-#: gitk:217 gitk:2399 gitk:8239 gitk:8272
+#: gitk:217 gitk:2403 gitk:8249 gitk:8282
msgid "Markup words"
msgstr "Отбелязване на думите"
#: gitk:324
msgid "Error parsing revisions:"
-msgstr "Грешка при разбор на версиите:"
+msgstr "Грешка при анализ на версиите:"
#: gitk:380
msgid "Error executing --argscmd command:"
msgid "Reading"
msgstr "Прочитане"
-#: gitk:496 gitk:4544
+#: gitk:496 gitk:4549
msgid "Reading commits..."
msgstr "Прочитане на подаванията…"
-#: gitk:499 gitk:1637 gitk:4547
+#: gitk:499 gitk:1641 gitk:4552
msgid "No commits selected"
msgstr "Не са избрани подавания"
-#: gitk:1445 gitk:4064 gitk:12469
+#: gitk:1449 gitk:4069 gitk:12583
msgid "Command line"
msgstr "Команден ред"
-#: gitk:1511
+#: gitk:1515
msgid "Can't parse git log output:"
msgstr "Изходът от „git log“ не може да се анализира:"
-#: gitk:1740
+#: gitk:1744
msgid "No commit information available"
msgstr "Липсва информация за подавания"
-#: gitk:1903 gitk:1932 gitk:4334 gitk:9702 gitk:11274 gitk:11554
+#: gitk:1907 gitk:1936 gitk:4339 gitk:9789 gitk:11388 gitk:11668
msgid "OK"
msgstr "Добре"
-#: gitk:1934 gitk:4336 gitk:9215 gitk:9294 gitk:9424 gitk:9473 gitk:9704
-#: gitk:11275 gitk:11555
+#: gitk:1938 gitk:4341 gitk:9225 gitk:9304 gitk:9434 gitk:9520 gitk:9791
+#: gitk:11389 gitk:11669
msgid "Cancel"
msgstr "Отказ"
-#: gitk:2083
+#: gitk:2087
msgid "&Update"
msgstr "&Обновяване"
-#: gitk:2084
+#: gitk:2088
msgid "&Reload"
msgstr "&Презареждане"
-#: gitk:2085
+#: gitk:2089
msgid "Reread re&ferences"
-msgstr "&Наново прочитане на настройките"
+msgstr "&Наново прочитане"
-#: gitk:2086
+#: gitk:2090
msgid "&List references"
msgstr "&Изброяване на указателите"
-#: gitk:2088
+#: gitk:2092
msgid "Start git &gui"
msgstr "&Стартиране на „git gui“"
-#: gitk:2090
+#: gitk:2094
msgid "&Quit"
msgstr "&Спиране на програмата"
-#: gitk:2082
+#: gitk:2086
msgid "&File"
msgstr "&Файл"
-#: gitk:2094
+#: gitk:2098
msgid "&Preferences"
msgstr "&Настройки"
-#: gitk:2093
+#: gitk:2097
msgid "&Edit"
msgstr "&Редактиране"
-#: gitk:2098
+#: gitk:2102
msgid "&New view..."
msgstr "&Нов изглед…"
-#: gitk:2099
+#: gitk:2103
msgid "&Edit view..."
msgstr "&Редактиране на изгледа…"
-#: gitk:2100
+#: gitk:2104
msgid "&Delete view"
msgstr "&Изтриване на изгледа"
-#: gitk:2102
+#: gitk:2106
msgid "&All files"
msgstr "&Всички файлове"
-#: gitk:2097
+#: gitk:2101
msgid "&View"
msgstr "&Изглед"
-#: gitk:2107 gitk:2117
+#: gitk:2111 gitk:2121
msgid "&About gitk"
msgstr "&Относно gitk"
-#: gitk:2108 gitk:2122
+#: gitk:2112 gitk:2126
msgid "&Key bindings"
msgstr "&Клавишни комбинации"
-#: gitk:2106 gitk:2121
+#: gitk:2110 gitk:2125
msgid "&Help"
msgstr "Помо&щ"
-#: gitk:2199 gitk:8671
+#: gitk:2203 gitk:8681
msgid "SHA1 ID:"
msgstr "SHA1:"
-#: gitk:2243
+#: gitk:2247
msgid "Row"
msgstr "Ред"
-#: gitk:2281
+#: gitk:2285
msgid "Find"
msgstr "Търсене"
-#: gitk:2309
+#: gitk:2313
msgid "commit"
msgstr "подаване"
-#: gitk:2313 gitk:2315 gitk:4706 gitk:4729 gitk:4753 gitk:6774 gitk:6846
-#: gitk:6931
+#: gitk:2317 gitk:2319 gitk:4711 gitk:4734 gitk:4758 gitk:6779 gitk:6851
+#: gitk:6936
msgid "containing:"
msgstr "съдържащо:"
-#: gitk:2316 gitk:3545 gitk:3550 gitk:4782
+#: gitk:2320 gitk:3550 gitk:3555 gitk:4787
msgid "touching paths:"
-msgstr "заÑ\81Ñ\8fгаÑ\89о пътищата:"
+msgstr "в пътищата:"
-#: gitk:2317 gitk:4796
+#: gitk:2321 gitk:4801
msgid "adding/removing string:"
msgstr "добавящо/премахващо низ"
-#: gitk:2318 gitk:4798
+#: gitk:2322 gitk:4803
msgid "changing lines matching:"
msgstr "променящо редове напасващи:"
-#: gitk:2327 gitk:2329 gitk:4785
+#: gitk:2331 gitk:2333 gitk:4790
msgid "Exact"
msgstr "Точно"
-#: gitk:2329 gitk:4873 gitk:6742
+#: gitk:2333 gitk:4878 gitk:6747
msgid "IgnCase"
msgstr "Без регистър"
-#: gitk:2329 gitk:4755 gitk:4871 gitk:6738
+#: gitk:2333 gitk:4760 gitk:4876 gitk:6743
msgid "Regexp"
msgstr "Рег. израз"
-#: gitk:2331 gitk:2332 gitk:4893 gitk:4923 gitk:4930 gitk:6867 gitk:6935
+#: gitk:2335 gitk:2336 gitk:4898 gitk:4928 gitk:4935 gitk:6872 gitk:6940
msgid "All fields"
msgstr "Всички полета"
-#: gitk:2332 gitk:4890 gitk:4923 gitk:6805
+#: gitk:2336 gitk:4895 gitk:4928 gitk:6810
msgid "Headline"
msgstr "Първи ред"
-#: gitk:2333 gitk:4890 gitk:6805 gitk:6935 gitk:7408
+#: gitk:2337 gitk:4895 gitk:6810 gitk:6940 gitk:7413
msgid "Comments"
msgstr "Коментари"
-#: gitk:2333 gitk:4890 gitk:4895 gitk:4930 gitk:6805 gitk:7343 gitk:8849
-#: gitk:8864
+#: gitk:2337 gitk:4895 gitk:4900 gitk:4935 gitk:6810 gitk:7348 gitk:8859
+#: gitk:8874
msgid "Author"
msgstr "Автор"
-#: gitk:2333 gitk:4890 gitk:6805 gitk:7345
+#: gitk:2337 gitk:4895 gitk:6810 gitk:7350
msgid "Committer"
msgstr "Подаващ"
-#: gitk:2367
+#: gitk:2371
msgid "Search"
msgstr "Търсене"
-#: gitk:2375
+#: gitk:2379
msgid "Diff"
msgstr "Разлики"
-#: gitk:2377
+#: gitk:2381
msgid "Old version"
msgstr "Стара версия"
-#: gitk:2379
+#: gitk:2383
msgid "New version"
msgstr "Нова версия"
-#: gitk:2382
+#: gitk:2386
msgid "Lines of context"
msgstr "Контекст в редове"
-#: gitk:2392
+#: gitk:2396
msgid "Ignore space change"
msgstr "Празните знаци без значение"
-#: gitk:2396 gitk:2398 gitk:7978 gitk:8225
+#: gitk:2400 gitk:2402 gitk:7983 gitk:8235
msgid "Line diff"
msgstr "Поредови разлики"
-#: gitk:2463
+#: gitk:2467
msgid "Patch"
msgstr "Кръпка"
-#: gitk:2465
+#: gitk:2469
msgid "Tree"
msgstr "Дърво"
-#: gitk:2635 gitk:2656
+#: gitk:2639 gitk:2660
msgid "Diff this -> selected"
msgstr "Разлики между това и избраното"
-#: gitk:2636 gitk:2657
+#: gitk:2640 gitk:2661
msgid "Diff selected -> this"
msgstr "Разлики между избраното и това"
-#: gitk:2637 gitk:2658
+#: gitk:2641 gitk:2662
msgid "Make patch"
msgstr "Създаване на кръпка"
-#: gitk:2638 gitk:9273
+#: gitk:2642 gitk:9283
msgid "Create tag"
msgstr "Създаване на етикет"
-#: gitk:2639
+#: gitk:2643
msgid "Copy commit summary"
msgstr "Копиране на информацията за подаване"
-#: gitk:2640 gitk:9404
+#: gitk:2644 gitk:9414
msgid "Write commit to file"
msgstr "Запазване на подаването във файл"
-#: gitk:2641 gitk:9461
+#: gitk:2645
msgid "Create new branch"
msgstr "Създаване на нов клон"
-#: gitk:2642
+#: gitk:2646
msgid "Cherry-pick this commit"
msgstr "Отбиране на това подаване"
-#: gitk:2643
+#: gitk:2647
msgid "Reset HEAD branch to here"
msgstr "Привеждане на върха на клона към текущото подаване"
-#: gitk:2644
+#: gitk:2648
msgid "Mark this commit"
msgstr "Отбелязване на това подаване"
-#: gitk:2645
+#: gitk:2649
msgid "Return to mark"
msgstr "Връщане към отбелязаното подаване"
-#: gitk:2646
+#: gitk:2650
msgid "Find descendant of this and mark"
msgstr "Откриване и отбелязване на наследниците"
-#: gitk:2647
+#: gitk:2651
msgid "Compare with marked commit"
msgstr "Сравнение с отбелязаното подаване"
-#: gitk:2648 gitk:2659
+#: gitk:2652 gitk:2663
msgid "Diff this -> marked commit"
msgstr "Разлики между това и отбелязаното"
-#: gitk:2649 gitk:2660
+#: gitk:2653 gitk:2664
msgid "Diff marked commit -> this"
msgstr "Разлики между отбелязаното и това"
-#: gitk:2650
+#: gitk:2654
msgid "Revert this commit"
msgstr "Отмяна на това подаване"
-#: gitk:2666
+#: gitk:2670
msgid "Check out this branch"
msgstr "Изтегляне на този клон"
-#: gitk:2667
+#: gitk:2671
+msgid "Rename this branch"
+msgstr "Преименуване на този клон"
+
+#: gitk:2672
msgid "Remove this branch"
msgstr "Изтриване на този клон"
-#: gitk:2668
+#: gitk:2673
msgid "Copy branch name"
msgstr "Копиране на името на клона"
-#: gitk:2675
+#: gitk:2680
msgid "Highlight this too"
msgstr "Отбелязване и на това"
-#: gitk:2676
+#: gitk:2681
msgid "Highlight this only"
msgstr "Отбелязване само на това"
-#: gitk:2677
+#: gitk:2682
msgid "External diff"
msgstr "Външна програма за разлики"
-#: gitk:2678
+#: gitk:2683
msgid "Blame parent commit"
msgstr "Анотиране на родителското подаване"
-#: gitk:2679
+#: gitk:2684
msgid "Copy path"
msgstr "Копиране на пътя"
-#: gitk:2686
+#: gitk:2691
msgid "Show origin of this line"
msgstr "Показване на произхода на този ред"
-#: gitk:2687
+#: gitk:2692
msgid "Run git gui blame on this line"
msgstr "Изпълнение на „git gui blame“ върху този ред"
-#: gitk:3031
+#: gitk:3036
msgid "About gitk"
msgstr "Относно gitk"
-#: gitk:3033
+#: gitk:3038
msgid ""
"\n"
"Gitk - a commit viewer for git\n"
"\n"
"Използвайте и разпространявайте при условията на ОПЛ на ГНУ"
-#: gitk:3041 gitk:3108 gitk:9890
+#: gitk:3046 gitk:3113 gitk:10004
msgid "Close"
msgstr "Затваряне"
-#: gitk:3062
+#: gitk:3067
msgid "Gitk key bindings"
msgstr "Клавишни комбинации"
-#: gitk:3065
+#: gitk:3070
msgid "Gitk key bindings:"
msgstr "Клавишни комбинации:"
-#: gitk:3067
+#: gitk:3072
#, tcl-format
msgid "<%s-Q>\t\tQuit"
msgstr "<%s-Q>\t\tСпиране на програмата"
-#: gitk:3068
+#: gitk:3073
#, tcl-format
msgid "<%s-W>\t\tClose window"
msgstr "<%s-W>\t\tЗатваряне на прозореца"
-#: gitk:3069
+#: gitk:3074
msgid "<Home>\t\tMove to first commit"
msgstr "<Home>\t\tКъм първото подаване"
-#: gitk:3070
+#: gitk:3075
msgid "<End>\t\tMove to last commit"
msgstr "<End>\t\tКъм последното подаване"
-#: gitk:3071
+#: gitk:3076
msgid "<Up>, p, k\tMove up one commit"
msgstr "<Up>, p, k\tЕдно подаване нагоре"
-#: gitk:3072
+#: gitk:3077
msgid "<Down>, n, j\tMove down one commit"
msgstr "<Down>, n, j\tЕдно подаване надолу"
-#: gitk:3073
+#: gitk:3078
msgid "<Left>, z, h\tGo back in history list"
msgstr "<Left>, z, h\tНазад в историята"
-#: gitk:3074
+#: gitk:3079
msgid "<Right>, x, l\tGo forward in history list"
msgstr "<Right>, x, l\tНапред в историята"
-#: gitk:3075
+#: gitk:3080
#, tcl-format
msgid "<%s-n>\tGo to n-th parent of current commit in history list"
msgstr "<%s-n>\tКъм n-тия родител на текущото подаване в историята"
-#: gitk:3076
+#: gitk:3081
msgid "<PageUp>\tMove up one page in commit list"
msgstr "<PageUp>\tСтраница нагоре в списъка с подаванията"
-#: gitk:3077
+#: gitk:3082
msgid "<PageDown>\tMove down one page in commit list"
msgstr "<PageDown>\tСтраница надолу в списъка с подаванията"
-#: gitk:3078
+#: gitk:3083
#, tcl-format
msgid "<%s-Home>\tScroll to top of commit list"
msgstr "<%s-Home>\tКъм началото на списъка с подаванията"
-#: gitk:3079
+#: gitk:3084
#, tcl-format
msgid "<%s-End>\tScroll to bottom of commit list"
msgstr "<%s-End>\tКъм края на списъка с подаванията"
-#: gitk:3080
+#: gitk:3085
#, tcl-format
msgid "<%s-Up>\tScroll commit list up one line"
msgstr "<%s-Up>\tРед нагоре в списъка с подавания"
-#: gitk:3081
+#: gitk:3086
#, tcl-format
msgid "<%s-Down>\tScroll commit list down one line"
msgstr "<%s-Down>\tРед надолу в списъка с подавания"
-#: gitk:3082
+#: gitk:3087
#, tcl-format
msgid "<%s-PageUp>\tScroll commit list up one page"
msgstr "<%s-PageUp>\tСтраница нагоре в списъка с подавания"
-#: gitk:3083
+#: gitk:3088
#, tcl-format
msgid "<%s-PageDown>\tScroll commit list down one page"
msgstr "<%s-PageDown>\tСтраница надолу в списъка с подавания"
-#: gitk:3084
+#: gitk:3089
msgid "<Shift-Up>\tFind backwards (upwards, later commits)"
msgstr "<Shift-Up>\tТърсене назад (визуално нагоре, исторически — последващи)"
-#: gitk:3085
+#: gitk:3090
msgid "<Shift-Down>\tFind forwards (downwards, earlier commits)"
msgstr ""
"<Shift-Down>\tТърсене напред (визуално надолу, исторически — предхождащи)"
-#: gitk:3086
+#: gitk:3091
msgid "<Delete>, b\tScroll diff view up one page"
msgstr "<Delete>, b\tСтраница нагоре в изгледа за разлики"
-#: gitk:3087
+#: gitk:3092
msgid "<Backspace>\tScroll diff view up one page"
msgstr "<Backspace>\tСтраница надолу в изгледа за разлики"
-#: gitk:3088
+#: gitk:3093
msgid "<Space>\t\tScroll diff view down one page"
msgstr "<Space>\t\tСтраница надолу в изгледа за разлики"
-#: gitk:3089
+#: gitk:3094
msgid "u\t\tScroll diff view up 18 lines"
msgstr "u\t\t18 реда нагоре в изгледа за разлики"
-#: gitk:3090
+#: gitk:3095
msgid "d\t\tScroll diff view down 18 lines"
msgstr "d\t\t18 реда надолу в изгледа за разлики"
-#: gitk:3091
+#: gitk:3096
#, tcl-format
msgid "<%s-F>\t\tFind"
msgstr "<%s-F>\t\tТърсене"
-#: gitk:3092
+#: gitk:3097
#, tcl-format
msgid "<%s-G>\t\tMove to next find hit"
msgstr "<%s-G>\t\tКъм следващата поява"
-#: gitk:3093
+#: gitk:3098
msgid "<Return>\tMove to next find hit"
msgstr "<Return>\tКъм следващата поява"
-#: gitk:3094
+#: gitk:3099
msgid "g\t\tGo to commit"
msgstr "g\t\tКъм последното подаване"
-#: gitk:3095
+#: gitk:3100
msgid "/\t\tFocus the search box"
msgstr "/\t\tФокус върху полето за търсене"
-#: gitk:3096
+#: gitk:3101
msgid "?\t\tMove to previous find hit"
msgstr "?\t\tКъм предишната поява"
-#: gitk:3097
+#: gitk:3102
msgid "f\t\tScroll diff view to next file"
msgstr "f\t\tСледващ файл в изгледа за разлики"
-#: gitk:3098
+#: gitk:3103
#, tcl-format
msgid "<%s-S>\t\tSearch for next hit in diff view"
msgstr "<%s-S>\t\tТърсене на следващата поява в изгледа за разлики"
-#: gitk:3099
+#: gitk:3104
#, tcl-format
msgid "<%s-R>\t\tSearch for previous hit in diff view"
msgstr "<%s-R>\t\tТърсене на предишната поява в изгледа за разлики"
-#: gitk:3100
+#: gitk:3105
#, tcl-format
msgid "<%s-KP+>\tIncrease font size"
msgstr "<%s-KP+>\tПо-голям размер на шрифта"
-#: gitk:3101
+#: gitk:3106
#, tcl-format
msgid "<%s-plus>\tIncrease font size"
msgstr "<%s-plus>\tПо-голям размер на шрифта"
-#: gitk:3102
+#: gitk:3107
#, tcl-format
msgid "<%s-KP->\tDecrease font size"
msgstr "<%s-KP->\tПо-малък размер на шрифта"
-#: gitk:3103
+#: gitk:3108
#, tcl-format
msgid "<%s-minus>\tDecrease font size"
msgstr "<%s-minus>\tПо-малък размер на шрифта"
-#: gitk:3104
+#: gitk:3109
msgid "<F5>\t\tUpdate"
msgstr "<F5>\t\tОбновяване"
-#: gitk:3569 gitk:3578
+#: gitk:3574 gitk:3583
#, tcl-format
msgid "Error creating temporary directory %s:"
msgstr "Грешка при създаването на временната директория „%s“:"
-#: gitk:3591
+#: gitk:3596
#, tcl-format
msgid "Error getting \"%s\" from %s:"
msgstr "Грешка при получаването на „%s“ от %s:"
-#: gitk:3654
+#: gitk:3659
msgid "command failed:"
msgstr "неуспешно изпълнение на команда:"
-#: gitk:3803
+#: gitk:3808
msgid "No such commit"
msgstr "Такова подаване няма"
-#: gitk:3817
+#: gitk:3822
msgid "git gui blame: command failed:"
msgstr "„git gui blame“: неуспешно изпълнение на команда:"
-#: gitk:3848
+#: gitk:3853
#, tcl-format
msgid "Couldn't read merge head: %s"
msgstr "Върхът за сливане не може да бъде прочетен: %s"
-#: gitk:3856
+#: gitk:3861
#, tcl-format
msgid "Error reading index: %s"
msgstr "Грешка при прочитане на индекса: %s"
-#: gitk:3881
+#: gitk:3886
#, tcl-format
msgid "Couldn't start git blame: %s"
msgstr "Командата „git blame“ не може да бъде стартирана: %s"
-#: gitk:3884 gitk:6773
+#: gitk:3889 gitk:6778
msgid "Searching"
msgstr "Търсене"
-#: gitk:3916
+#: gitk:3921
#, tcl-format
msgid "Error running git blame: %s"
msgstr "Грешка при изпълнението на „git blame“: %s"
-#: gitk:3944
+#: gitk:3949
#, tcl-format
msgid "That line comes from commit %s, which is not in this view"
msgstr "Този ред идва от подаването %s, което не е в изгледа"
-#: gitk:3958
+#: gitk:3963
msgid "External diff viewer failed:"
msgstr "Неуспешно изпълнение на външната програма за разлики:"
-#: gitk:4062
+#: gitk:4067
msgid "All files"
msgstr "Всички файлове"
-#: gitk:4086
+#: gitk:4091
msgid "View"
msgstr "Изглед"
-#: gitk:4089
+#: gitk:4094
msgid "Gitk view definition"
msgstr "Дефиниция на изглед в Gitk"
-#: gitk:4093
+#: gitk:4098
msgid "Remember this view"
msgstr "Запазване на този изглед"
-#: gitk:4094
+#: gitk:4099
msgid "References (space separated list):"
msgstr "Указатели (списък с разделител интервал):"
-#: gitk:4095
+#: gitk:4100
msgid "Branches & tags:"
msgstr "Клони и етикети:"
-#: gitk:4096
+#: gitk:4101
msgid "All refs"
msgstr "Всички указатели"
-#: gitk:4097
+#: gitk:4102
msgid "All (local) branches"
msgstr "Всички (локални) клони"
-#: gitk:4098
+#: gitk:4103
msgid "All tags"
msgstr "Всички етикети"
-#: gitk:4099
+#: gitk:4104
msgid "All remote-tracking branches"
msgstr "Всички следящи клони"
-#: gitk:4100
+#: gitk:4105
msgid "Commit Info (regular expressions):"
msgstr "Информация за подаване (рег. изр.):"
-#: gitk:4101
+#: gitk:4106
msgid "Author:"
msgstr "Автор:"
-#: gitk:4102
+#: gitk:4107
msgid "Committer:"
msgstr "Подал:"
-#: gitk:4103
+#: gitk:4108
msgid "Commit Message:"
msgstr "Съобщение при подаване:"
-#: gitk:4104
+#: gitk:4109
msgid "Matches all Commit Info criteria"
msgstr "Съвпадение по всички характеристики на подаването"
-#: gitk:4105
+#: gitk:4110
msgid "Matches no Commit Info criteria"
msgstr "Не съвпада по никоя от характеристиките на подаването"
-#: gitk:4106
+#: gitk:4111
msgid "Changes to Files:"
msgstr "Промени по файловете:"
-#: gitk:4107
+#: gitk:4112
msgid "Fixed String"
msgstr "Дословен низ"
-#: gitk:4108
+#: gitk:4113
msgid "Regular Expression"
msgstr "Регулярен израз"
-#: gitk:4109
+#: gitk:4114
msgid "Search string:"
msgstr "Низ за търсене:"
-#: gitk:4110
+#: gitk:4115
msgid ""
"Commit Dates (\"2 weeks ago\", \"2009-03-17 15:27:38\", \"March 17, 2009 "
"15:27:38\"):"
"Дата на подаване („2 weeks ago“ (преди 2 седмици), „2009-03-17 15:27:38“, "
"„March 17, 2009 15:27:38“):"
-#: gitk:4111
+#: gitk:4116
msgid "Since:"
msgstr "От:"
-#: gitk:4112
+#: gitk:4117
msgid "Until:"
msgstr "До:"
-#: gitk:4113
+#: gitk:4118
msgid "Limit and/or skip a number of revisions (positive integer):"
msgstr ""
"Ограничаване и/или прескачане на определен брой версии (неотрицателно цяло "
"число):"
-#: gitk:4114
+#: gitk:4119
msgid "Number to show:"
msgstr "Брой показани:"
-#: gitk:4115
+#: gitk:4120
msgid "Number to skip:"
msgstr "Брой прескочени:"
-#: gitk:4116
+#: gitk:4121
msgid "Miscellaneous options:"
msgstr "Разни:"
-#: gitk:4117
+#: gitk:4122
msgid "Strictly sort by date"
msgstr "Подреждане по дата"
-#: gitk:4118
+#: gitk:4123
msgid "Mark branch sides"
msgstr "Отбелязване на страните по клона"
-#: gitk:4119
+#: gitk:4124
msgid "Limit to first parent"
msgstr "Само първия родител"
-#: gitk:4120
+#: gitk:4125
msgid "Simple history"
msgstr "Опростена история"
-#: gitk:4121
+#: gitk:4126
msgid "Additional arguments to git log:"
msgstr "Допълнителни аргументи към „git log“:"
-#: gitk:4122
+#: gitk:4127
msgid "Enter files and directories to include, one per line:"
msgstr "Въведете файловете и директориите за включване, по елемент на ред"
-#: gitk:4123
+#: gitk:4128
msgid "Command to generate more commits to include:"
msgstr ""
"Команда за генерирането на допълнителни подавания, които да бъдат включени:"
-#: gitk:4247
+#: gitk:4252
msgid "Gitk: edit view"
msgstr "Gitk: редактиране на изглед"
-#: gitk:4255
+#: gitk:4260
msgid "-- criteria for selecting revisions"
msgstr "— критерии за избор на версии"
-#: gitk:4260
+#: gitk:4265
msgid "View Name"
msgstr "Име на изглед"
-#: gitk:4335
+#: gitk:4340
msgid "Apply (F5)"
msgstr "Прилагане (F5)"
-#: gitk:4373
+#: gitk:4378
msgid "Error in commit selection arguments:"
msgstr "Грешка в аргументите за избор на подавания:"
-#: gitk:4428 gitk:4481 gitk:4943 gitk:4957 gitk:6227 gitk:12410 gitk:12411
+#: gitk:4433 gitk:4486 gitk:4948 gitk:4962 gitk:6232 gitk:12524 gitk:12525
msgid "None"
msgstr "Няма"
-#: gitk:5040 gitk:5045
+#: gitk:5045 gitk:5050
msgid "Descendant"
msgstr "Наследник"
-#: gitk:5041
+#: gitk:5046
msgid "Not descendant"
msgstr "Не е наследник"
-#: gitk:5048 gitk:5053
+#: gitk:5053 gitk:5058
msgid "Ancestor"
msgstr "Предшественик"
-#: gitk:5049
+#: gitk:5054
msgid "Not ancestor"
msgstr "Не е предшественик"
-#: gitk:5343
+#: gitk:5348
msgid "Local changes checked in to index but not committed"
msgstr "Локални промени добавени към индекса, но неподадени"
-#: gitk:5379
+#: gitk:5384
msgid "Local uncommitted changes, not checked in to index"
msgstr "Локални промени извън индекса"
-#: gitk:7153
+#: gitk:7158
msgid "and many more"
msgstr "и още много"
-#: gitk:7156
+#: gitk:7161
msgid "many"
msgstr "много"
-#: gitk:7347
+#: gitk:7352
msgid "Tags:"
msgstr "Етикети:"
-#: gitk:7364 gitk:7370 gitk:8844
+#: gitk:7369 gitk:7375 gitk:8854
msgid "Parent"
msgstr "Родител"
-#: gitk:7375
+#: gitk:7380
msgid "Child"
msgstr "Дете"
-#: gitk:7384
+#: gitk:7389
msgid "Branch"
msgstr "Клон"
-#: gitk:7387
+#: gitk:7392
msgid "Follows"
msgstr "Следва"
-#: gitk:7390
+#: gitk:7395
msgid "Precedes"
msgstr "Предшества"
-#: gitk:7985
+#: gitk:7990
#, tcl-format
msgid "Error getting diffs: %s"
msgstr "Грешка при получаването на разликите: %s"
-#: gitk:8669
+#: gitk:8679
msgid "Goto:"
msgstr "Към ред:"
-#: gitk:8690
+#: gitk:8700
#, tcl-format
msgid "Short SHA1 id %s is ambiguous"
msgstr "Съкратената сума по SHA1 %s не е еднозначна"
-#: gitk:8697
+#: gitk:8707
#, tcl-format
msgid "Revision %s is not known"
msgstr "Непозната версия %s"
-#: gitk:8707
+#: gitk:8717
#, tcl-format
msgid "SHA1 id %s is not known"
msgstr "Непозната сума по SHA1 %s"
-#: gitk:8709
+#: gitk:8719
#, tcl-format
msgid "Revision %s is not in the current view"
msgstr "Версия %s не е в текущия изглед"
-#: gitk:8851 gitk:8866
+#: gitk:8861 gitk:8876
msgid "Date"
msgstr "Дата"
-#: gitk:8854
+#: gitk:8864
msgid "Children"
msgstr "Деца"
-#: gitk:8917
+#: gitk:8927
#, tcl-format
msgid "Reset %s branch to here"
msgstr "Зануляване на клона „%s“ към текущото подаване"
-#: gitk:8919
+#: gitk:8929
msgid "Detached head: can't reset"
msgstr "Несвързан връх: невъзможно зануляване"
-#: gitk:9024 gitk:9030
+#: gitk:9034 gitk:9040
msgid "Skipping merge commit "
msgstr "Пропускане на подаването на сливането"
-#: gitk:9039 gitk:9044
+#: gitk:9049 gitk:9054
msgid "Error getting patch ID for "
msgstr "Грешка при получаването на идентификатора на "
-#: gitk:9040 gitk:9045
+#: gitk:9050 gitk:9055
msgid " - stopping\n"
msgstr " — спиране\n"
-#: gitk:9050 gitk:9053 gitk:9061 gitk:9075 gitk:9084
+#: gitk:9060 gitk:9063 gitk:9071 gitk:9085 gitk:9094
msgid "Commit "
msgstr "Подаване"
-#: gitk:9054
+#: gitk:9064
msgid ""
" is the same patch as\n"
" "
" е същата кръпка като\n"
" "
-#: gitk:9062
+#: gitk:9072
msgid ""
" differs from\n"
" "
" се различава от\n"
" "
-#: gitk:9064
+#: gitk:9074
msgid ""
"Diff of commits:\n"
"\n"
"Разлика между подаванията:\n"
"\n"
-#: gitk:9076 gitk:9085
+#: gitk:9086 gitk:9095
#, tcl-format
msgid " has %s children - stopping\n"
msgstr " има %s деца — спиране\n"
-#: gitk:9104
+#: gitk:9114
#, tcl-format
msgid "Error writing commit to file: %s"
msgstr "Грешка при запазването на подаването във файл: %s"
-#: gitk:9110
+#: gitk:9120
#, tcl-format
msgid "Error diffing commits: %s"
msgstr "Грешка при изчисляването на разликите между подаванията: %s"
-#: gitk:9156
+#: gitk:9166
msgid "Top"
msgstr "Най-горе"
-#: gitk:9157
+#: gitk:9167
msgid "From"
msgstr "От"
-#: gitk:9162
+#: gitk:9172
msgid "To"
msgstr "До"
-#: gitk:9186
+#: gitk:9196
msgid "Generate patch"
msgstr "Генериране на кръпка"
-#: gitk:9188
+#: gitk:9198
msgid "From:"
msgstr "От:"
-#: gitk:9197
+#: gitk:9207
msgid "To:"
msgstr "До:"
-#: gitk:9206
+#: gitk:9216
msgid "Reverse"
msgstr "Обръщане"
-#: gitk:9208 gitk:9418
+#: gitk:9218 gitk:9428
msgid "Output file:"
msgstr "Запазване във файла:"
-#: gitk:9214
+#: gitk:9224
msgid "Generate"
msgstr "Генериране"
-#: gitk:9252
+#: gitk:9262
msgid "Error creating patch:"
msgstr "Грешка при създаването на кръпка:"
-#: gitk:9275 gitk:9406 gitk:9463
+#: gitk:9285 gitk:9416 gitk:9504
msgid "ID:"
msgstr "Идентификатор:"
-#: gitk:9284
+#: gitk:9294
msgid "Tag name:"
msgstr "Име на етикет:"
-#: gitk:9287
+#: gitk:9297
msgid "Tag message is optional"
msgstr "Съобщението за етикет е незадължително"
-#: gitk:9289
+#: gitk:9299
msgid "Tag message:"
msgstr "Съобщение за етикет:"
-#: gitk:9293 gitk:9472
+#: gitk:9303 gitk:9474
msgid "Create"
msgstr "Създаване"
-#: gitk:9311
+#: gitk:9321
msgid "No tag name specified"
msgstr "Липсва име на етикет"
-#: gitk:9315
+#: gitk:9325
#, tcl-format
msgid "Tag \"%s\" already exists"
msgstr "Етикетът „%s“ вече съществува"
-#: gitk:9325
+#: gitk:9335
msgid "Error creating tag:"
msgstr "Грешка при създаването на етикет:"
-#: gitk:9415
+#: gitk:9425
msgid "Command:"
msgstr "Команда:"
-#: gitk:9423
+#: gitk:9433
msgid "Write"
msgstr "Запазване"
-#: gitk:9441
+#: gitk:9451
msgid "Error writing commit:"
msgstr "Грешка при запазването на подаването:"
-#: gitk:9468
+#: gitk:9473
+msgid "Create branch"
+msgstr "Създаване на клон"
+
+#: gitk:9489
+#, tcl-format
+msgid "Rename branch %s"
+msgstr "Преименуване на клона „%s“"
+
+#: gitk:9490
+msgid "Rename"
+msgstr "Преименуване"
+
+#: gitk:9514
msgid "Name:"
msgstr "Име:"
-#: gitk:9491
+#: gitk:9538
msgid "Please specify a name for the new branch"
msgstr "Укажете име за новия клон"
-#: gitk:9496
+#: gitk:9543
#, tcl-format
msgid "Branch '%s' already exists. Overwrite?"
msgstr "Клонът „%s“ вече съществува. Да бъде ли презаписан?"
-#: gitk:9563
+#: gitk:9587
+msgid "Please specify a new name for the branch"
+msgstr "Укажете ново име за клона"
+
+#: gitk:9650
#, tcl-format
msgid "Commit %s is already included in branch %s -- really re-apply it?"
msgstr ""
"Подаването „%s“ вече е включено в клона „%s“ — да бъде ли приложено отново?"
-#: gitk:9568
+#: gitk:9655
msgid "Cherry-picking"
msgstr "Отбиране"
-#: gitk:9577
+#: gitk:9664
#, tcl-format
msgid ""
"Cherry-pick failed because of local changes to file '%s'.\n"
"Неуспешно отбиране, защото във файла „%s“ има локални промени.\n"
"Подайте, занулете или ги скатайте и пробвайте отново."
-#: gitk:9583
+#: gitk:9670
msgid ""
"Cherry-pick failed because of merge conflict.\n"
"Do you wish to run git citool to resolve it?"
"Неуспешно отбиране поради конфликти при сливане.\n"
"Искате ли да ги коригирате чрез „git citool“?"
-#: gitk:9599 gitk:9657
+#: gitk:9686 gitk:9744
msgid "No changes committed"
msgstr "Не са подадени промени"
-#: gitk:9626
+#: gitk:9713
#, tcl-format
msgid "Commit %s is not included in branch %s -- really revert it?"
msgstr "Подаването „%s“ не е включено в клона „%s“. Да бъде ли отменено?"
-#: gitk:9631
+#: gitk:9718
msgid "Reverting"
msgstr "Отмяна"
-#: gitk:9639
+#: gitk:9726
#, tcl-format
msgid ""
"Revert failed because of local changes to the following files:%s Please "
"commit, reset or stash your changes and try again."
msgstr ""
"Неуспешна отмяна, защото във файла „%s“ има локални промени.\n"
-"Подайте, занулете или ги скатайте и пробвайте отново.<"
+"Подайте, занулете или ги скатайте и пробвайте отново."
-#: gitk:9643
+#: gitk:9730
msgid ""
"Revert failed because of merge conflict.\n"
" Do you wish to run git citool to resolve it?"
"Неуспешно отмяна поради конфликти при сливане.\n"
"Искате ли да ги коригирате чрез „git citool“?"
-#: gitk:9686
+#: gitk:9773
msgid "Confirm reset"
msgstr "Потвърждаване на зануляването"
-#: gitk:9688
+#: gitk:9775
#, tcl-format
msgid "Reset branch %s to %s?"
msgstr "Да се занули ли клонът „%s“ към „%s“?"
-#: gitk:9690
+#: gitk:9777
msgid "Reset type:"
msgstr "Вид зануляване:"
-#: gitk:9693
+#: gitk:9780
msgid "Soft: Leave working tree and index untouched"
msgstr "Слабо: работното дърво и индекса остават същите"
-#: gitk:9696
+#: gitk:9783
msgid "Mixed: Leave working tree untouched, reset index"
msgstr "Смесено: работното дърво остава същото, индексът се занулява"
-#: gitk:9699
+#: gitk:9786
msgid ""
"Hard: Reset working tree and index\n"
"(discard ALL local changes)"
msgstr ""
"Силно: зануляване и на работното дърво, и на индекса\n"
-"(*ВСИЧКИ* локални промени ще бъдат безвъзвратно загубени)"
+"(ВСИЧКИ локални промени ще бъдат безвъзвратно загубени)"
-#: gitk:9716
+#: gitk:9803
msgid "Resetting"
msgstr "Зануляване"
-#: gitk:9776
+#: gitk:9876
+#, tcl-format
+msgid "A local branch named %s exists already"
+msgstr "Вече съществува локален клон „%s“."
+
+#: gitk:9884
msgid "Checking out"
msgstr "Изтегляне"
-#: gitk:9829
+#: gitk:9943
msgid "Cannot delete the currently checked-out branch"
msgstr "Текущо изтегленият клон не може да бъде изтрит"
-#: gitk:9835
+#: gitk:9949
#, tcl-format
msgid ""
"The commits on branch %s aren't on any other branch.\n"
"Really delete branch %s?"
msgstr ""
"Подаванията на клона „%s“ не са на никой друг клон.\n"
-"Ð\9dаиÑ\81Ñ\82ина ли да Ñ\81е изÑ\82Ñ\80ие клонÑ\8aÑ\82 „%s“?"
+"Ð\9dаиÑ\81Ñ\82ина ли иÑ\81каÑ\82е да изÑ\82Ñ\80иеÑ\82е клона „%s“?"
-#: gitk:9866
+#: gitk:9980
#, tcl-format
msgid "Tags and heads: %s"
msgstr "Етикети и върхове: %s"
-#: gitk:9883
+#: gitk:9997
msgid "Filter"
msgstr "Филтриране"
-#: gitk:10179
+#: gitk:10293
msgid ""
"Error reading commit topology information; branch and preceding/following "
"tag information will be incomplete."
"Грешка при прочитането на топологията на подаванията. Информацията за клона "
"и предшестващите/следващите етикети ще е непълна."
-#: gitk:11156
+#: gitk:11270
msgid "Tag"
msgstr "Етикет"
-#: gitk:11160
+#: gitk:11274
msgid "Id"
msgstr "Идентификатор"
-#: gitk:11243
+#: gitk:11357
msgid "Gitk font chooser"
msgstr "Избор на шрифт за Gitk"
-#: gitk:11260
+#: gitk:11374
msgid "B"
msgstr "Ч"
-#: gitk:11263
+#: gitk:11377
msgid "I"
msgstr "К"
-#: gitk:11381
+#: gitk:11495
msgid "Commit list display options"
msgstr "Настройки на списъка с подавания"
-#: gitk:11384
+#: gitk:11498
msgid "Maximum graph width (lines)"
msgstr "Максимална широчина на графа (в редове)"
-#: gitk:11388
+#: gitk:11502
#, no-tcl-format
msgid "Maximum graph width (% of pane)"
msgstr "Максимална широчина на графа (% от панела)"
-#: gitk:11391
+#: gitk:11505
msgid "Show local changes"
msgstr "Показване на локалните промени"
-#: gitk:11394
+#: gitk:11508
msgid "Auto-select SHA1 (length)"
msgstr "Автоматично избиране на SHA1 (дължина)"
-#: gitk:11398
+#: gitk:11512
msgid "Hide remote refs"
msgstr "Скриване на отдалечените указатели"
-#: gitk:11402
+#: gitk:11516
msgid "Diff display options"
msgstr "Настройки на показването на разликите"
-#: gitk:11404
+#: gitk:11518
msgid "Tab spacing"
msgstr "Широчина на табулатора"
-#: gitk:11407
+#: gitk:11521
msgid "Display nearby tags/heads"
msgstr "Извеждане на близките етикети и върхове"
-#: gitk:11410
+#: gitk:11524
msgid "Maximum # tags/heads to show"
msgstr "Максимален брой етикети/върхове за показване"
-#: gitk:11413
+#: gitk:11527
msgid "Limit diffs to listed paths"
msgstr "Разлика само в избраните пътища"
-#: gitk:11416
+#: gitk:11530
msgid "Support per-file encodings"
msgstr "Поддръжка на различни кодирания за всеки файл"
-#: gitk:11422 gitk:11569
+#: gitk:11536 gitk:11683
msgid "External diff tool"
msgstr "Външен инструмент за разлики"
-#: gitk:11423
+#: gitk:11537
msgid "Choose..."
msgstr "Избор…"
-#: gitk:11428
+#: gitk:11542
msgid "General options"
msgstr "Общи настройки"
-#: gitk:11431
+#: gitk:11545
msgid "Use themed widgets"
msgstr "Използване на тема за графичните обекти"
-#: gitk:11433
+#: gitk:11547
msgid "(change requires restart)"
msgstr "(промяната изисква рестартиране на Gitk)"
-#: gitk:11435
+#: gitk:11549
msgid "(currently unavailable)"
msgstr "(в момента недостъпно)"
-#: gitk:11446
+#: gitk:11560
msgid "Colors: press to choose"
msgstr "Цветове: избира се с натискане"
-#: gitk:11449
+#: gitk:11563
msgid "Interface"
msgstr "Интерфейс"
-#: gitk:11450
+#: gitk:11564
msgid "interface"
msgstr "интерфейс"
-#: gitk:11453
+#: gitk:11567
msgid "Background"
msgstr "Фон"
-#: gitk:11454 gitk:11484
+#: gitk:11568 gitk:11598
msgid "background"
msgstr "фон"
-#: gitk:11457
+#: gitk:11571
msgid "Foreground"
msgstr "Знаци"
-#: gitk:11458
+#: gitk:11572
msgid "foreground"
msgstr "знаци"
-#: gitk:11461
+#: gitk:11575
msgid "Diff: old lines"
msgstr "Разлика: стари редове"
-#: gitk:11462
+#: gitk:11576
msgid "diff old lines"
msgstr "разлика, стари редове"
-#: gitk:11466
+#: gitk:11580
msgid "Diff: new lines"
msgstr "Разлика: нови редове"
-#: gitk:11467
+#: gitk:11581
msgid "diff new lines"
msgstr "разлика, нови редове"
-#: gitk:11471
+#: gitk:11585
msgid "Diff: hunk header"
msgstr "Разлика: начало на парче"
-#: gitk:11473
+#: gitk:11587
msgid "diff hunk header"
msgstr "разлика, начало на парче"
-#: gitk:11477
+#: gitk:11591
msgid "Marked line bg"
msgstr "Фон на отбелязан ред"
-#: gitk:11479
+#: gitk:11593
msgid "marked line background"
msgstr "фон на отбелязан ред"
-#: gitk:11483
+#: gitk:11597
msgid "Select bg"
msgstr "Избор на фон"
-#: gitk:11492
+#: gitk:11606
msgid "Fonts: press to choose"
msgstr "Шрифтове: избира се с натискане"
-#: gitk:11494
+#: gitk:11608
msgid "Main font"
msgstr "Основен шрифт"
-#: gitk:11495
+#: gitk:11609
msgid "Diff display font"
msgstr "Шрифт за разликите"
-#: gitk:11496
+#: gitk:11610
msgid "User interface font"
msgstr "Шрифт на интерфейса"
-#: gitk:11518
+#: gitk:11632
msgid "Gitk preferences"
msgstr "Настройки на Gitk"
-#: gitk:11527
+#: gitk:11641
msgid "General"
msgstr "Общи"
-#: gitk:11528
+#: gitk:11642
msgid "Colors"
msgstr "Цветове"
-#: gitk:11529
+#: gitk:11643
msgid "Fonts"
msgstr "Шрифтове"
-#: gitk:11579
+#: gitk:11693
#, tcl-format
msgid "Gitk: choose color for %s"
msgstr "Gitk: избор на цвят на „%s“"
-#: gitk:12092
+#: gitk:12206
msgid ""
"Sorry, gitk cannot run with this version of Tcl/Tk.\n"
" Gitk requires at least Tcl/Tk 8.4."
"Тази версия на Tcl/Tk не се поддържа от Gitk.\n"
" Необходима ви е поне Tcl/Tk 8.4."
-#: gitk:12302
+#: gitk:12416
msgid "Cannot find a git repository here."
msgstr "Тук липсва хранилище на Git."
-#: gitk:12349
+#: gitk:12463
#, tcl-format
msgid "Ambiguous argument '%s': both revision and filename"
msgstr "Нееднозначен аргумент „%s“: има и такава версия, и такъв файл"
-#: gitk:12361
+#: gitk:12475
msgid "Bad arguments to gitk:"
msgstr "Неправилни аргументи на gitk:"
{
const char *cmd_list;
- /*
- * There's no actual repository setup at this point (and even
- * if there is, we don't really care; only global config
- * matters). If we accidentally set up a repository, it's ok
- * too since the caller (git --list-cmds=) should exit shortly
- * anyway.
- */
if (git_config_get_string_const("completion.commands", &cmd_list))
return;
const char *p = strchrnul(cmd_list, ' ');
strbuf_add(&sb, cmd_list, p - cmd_list);
- if (*cmd_list == '-')
- string_list_remove(list, cmd_list + 1, 0);
+ if (sb.buf[0] == '-')
+ string_list_remove(list, sb.buf + 1, 0);
else
string_list_insert(list, sb.buf);
strbuf_release(&sb);
process_http_object_request(obj_req->req);
obj_req->state = COMPLETE;
+ normalize_curl_result(&obj_req->req->curl_result,
+ obj_req->req->http_code,
+ obj_req->req->errorstr,
+ sizeof(obj_req->req->errorstr));
+
/* Use alternates if necessary */
if (missing_target(obj_req->req)) {
fetch_alternates(walker, alt->base);
char *data;
int i = 0;
+ normalize_curl_result(&slot->curl_result, slot->http_code,
+ curl_errorstr, sizeof(curl_errorstr));
+
if (alt_req->http_specific) {
if (slot->curl_result != CURLE_OK ||
!alt_req->buffer->len) {
req->localfile = -1;
}
- /*
- * we turned off CURLOPT_FAILONERROR to avoid losing a
- * persistent connection and got CURLE_OK.
- */
- if (req->http_code >= 300 && req->curl_result == CURLE_OK &&
- (starts_with(req->url, "http://") ||
- starts_with(req->url, "https://"))) {
- req->curl_result = CURLE_HTTP_RETURNED_ERROR;
- xsnprintf(req->errorstr, sizeof(req->errorstr),
- "HTTP request failed");
- }
+ normalize_curl_result(&req->curl_result, req->http_code,
+ req->errorstr, sizeof(req->errorstr));
if (obj_req->state == ABORTED) {
ret = error("Request for %s aborted", hex);
return strbuf_detach(&buf, NULL);
}
-static int handle_curl_result(struct slot_results *results)
+void normalize_curl_result(CURLcode *result, long http_code,
+ char *errorstr, size_t errorlen)
{
/*
* If we see a failing http code with CURLE_OK, we have turned off
* Likewise, if we see a redirect (30x code), that means we turned off
* redirect-following, and we should treat the result as an error.
*/
- if (results->curl_result == CURLE_OK &&
- results->http_code >= 300) {
- results->curl_result = CURLE_HTTP_RETURNED_ERROR;
+ if (*result == CURLE_OK && http_code >= 300) {
+ *result = CURLE_HTTP_RETURNED_ERROR;
/*
* Normally curl will already have put the "reason phrase"
* from the server into curl_errorstr; unfortunately without
* FAILONERROR it is lost, so we can give only the numeric
* status code.
*/
- xsnprintf(curl_errorstr, sizeof(curl_errorstr),
+ xsnprintf(errorstr, errorlen,
"The requested URL returned error: %ld",
- results->http_code);
+ http_code);
}
+}
+
+static int handle_curl_result(struct slot_results *results)
+{
+ normalize_curl_result(&results->curl_result, results->http_code,
+ curl_errorstr, sizeof(curl_errorstr));
if (results->curl_result == CURLE_OK) {
credential_approve(&http_auth);
#define missing_target(a) missing__target((a)->http_code, (a)->curl_result)
+/*
+ * Normalize curl results to handle CURL_FAILONERROR (or lack thereof). Failing
+ * http codes have their "result" converted to CURLE_HTTP_RETURNED_ERROR, and
+ * an appropriate string placed in the errorstr buffer (pass curl_errorstr if
+ * you don't have a custom buffer).
+ */
+void normalize_curl_result(CURLcode *result, long http_code, char *errorstr,
+ size_t errorlen);
+
/* Helpers for modifying and creating URLs */
extern void append_remote_object_url(struct strbuf *buf, const char *url,
const char *hex,
return set_ident(var, value);
}
+static void set_env_if(const char *key, const char *value, int *given, int bit)
+{
+ if ((*given & bit) || getenv(key))
+ return; /* nothing to do */
+ setenv(key, value, 0);
+ *given |= bit;
+}
+
+void prepare_fallback_ident(const char *name, const char *email)
+{
+ set_env_if("GIT_AUTHOR_NAME", name,
+ &author_ident_explicitly_given, IDENT_NAME_GIVEN);
+ set_env_if("GIT_AUTHOR_EMAIL", email,
+ &author_ident_explicitly_given, IDENT_MAIL_GIVEN);
+ set_env_if("GIT_COMMITTER_NAME", name,
+ &committer_ident_explicitly_given, IDENT_NAME_GIVEN);
+ set_env_if("GIT_COMMITTER_EMAIL", email,
+ &committer_ident_explicitly_given, IDENT_MAIL_GIVEN);
+}
+
static int buf_cmp(const char *a_begin, const char *a_end,
const char *b_begin, const char *b_end)
{
int line_log_print(struct rev_info *rev, struct commit *commit)
{
- struct line_log_data *range = lookup_line_range(rev, commit);
show_log(rev);
- dump_diff_hacky(rev, range);
+ if (!(rev->diffopt.output_format & DIFF_FORMAT_NO_OUTPUT)) {
+ struct line_log_data *range = lookup_line_range(rev, commit);
+ dump_diff_hacky(rev, range);
+ }
return 1;
}
--- /dev/null
+diff_cmd () {
+ "$merge_tool_path" mergetool "$LOCAL" "$REMOTE" -o "$MERGED"
+}
+
+merge_cmd () {
+ if $base_present
+ then
+ "$merge_tool_path" mergetool "$BASE" "$LOCAL" "$REMOTE" -o "$MERGED"
+ else
+ "$merge_tool_path" mergetool "$LOCAL" "$REMOTE" -o "$MERGED"
+ fi
+}
#include "sha1-lookup.h"
#include "midx.h"
#include "progress.h"
+#include "trace2.h"
#define MIDX_SIGNATURE 0x4d494458 /* "MIDX" */
#define MIDX_VERSION 1
midx_map = xmmap(NULL, midx_size, PROT_READ, MAP_PRIVATE, fd, 0);
- FLEX_ALLOC_MEM(m, object_dir, object_dir, strlen(object_dir));
+ FLEX_ALLOC_STR(m, object_dir, object_dir);
m->fd = fd;
m->data = midx_map;
m->data_len = midx_size;
m->pack_names[i]);
}
+ trace2_data_intmax("midx", the_repository, "load/num_packs", m->num_packs);
+ trace2_data_intmax("midx", the_repository, "load/num_objects", m->num_objects);
+
return m;
cleanup_fail:
va_end(ap);
}
+struct pair_pos_vs_id
+{
+ uint32_t pos;
+ uint32_t pack_int_id;
+};
+
+static int compare_pair_pos_vs_id(const void *_a, const void *_b)
+{
+ struct pair_pos_vs_id *a = (struct pair_pos_vs_id *)_a;
+ struct pair_pos_vs_id *b = (struct pair_pos_vs_id *)_b;
+
+ return b->pack_int_id - a->pack_int_id;
+}
+
+/*
+ * Limit calls to display_progress() for performance reasons.
+ * The interval here was arbitrarily chosen.
+ */
+#define SPARSE_PROGRESS_INTERVAL (1 << 12)
+#define midx_display_sparse_progress(progress, n) \
+ do { \
+ uint64_t _n = (n); \
+ if ((_n & (SPARSE_PROGRESS_INTERVAL - 1)) == 0) \
+ display_progress(progress, _n); \
+ } while (0)
+
int verify_midx_file(const char *object_dir)
{
+ struct pair_pos_vs_id *pairs = NULL;
uint32_t i;
struct progress *progress;
struct multi_pack_index *m = load_multi_pack_index(object_dir, 1);
if (!m)
return 0;
+ progress = start_progress(_("Looking for referenced packfiles"),
+ m->num_packs);
for (i = 0; i < m->num_packs; i++) {
if (prepare_midx_pack(m, i))
midx_report("failed to load pack in position %d", i);
+
+ display_progress(progress, i + 1);
}
+ stop_progress(&progress);
for (i = 0; i < 255; i++) {
uint32_t oid_fanout1 = ntohl(m->chunk_oid_fanout[i]);
i, oid_fanout1, oid_fanout2, i + 1);
}
+ progress = start_sparse_progress(_("Verifying OID order in MIDX"),
+ m->num_objects - 1);
for (i = 0; i < m->num_objects - 1; i++) {
struct object_id oid1, oid2;
if (oidcmp(&oid1, &oid2) >= 0)
midx_report(_("oid lookup out of order: oid[%d] = %s >= %s = oid[%d]"),
i, oid_to_hex(&oid1), oid_to_hex(&oid2), i + 1);
+
+ midx_display_sparse_progress(progress, i + 1);
}
+ stop_progress(&progress);
- progress = start_progress(_("Verifying object offsets"), m->num_objects);
+ /*
+ * Create an array mapping each object to its packfile id. Sort it
+ * to group the objects by packfile. Use this permutation to visit
+ * each of the objects and only require 1 packfile to be open at a
+ * time.
+ */
+ ALLOC_ARRAY(pairs, m->num_objects);
+ for (i = 0; i < m->num_objects; i++) {
+ pairs[i].pos = i;
+ pairs[i].pack_int_id = nth_midxed_pack_int_id(m, i);
+ }
+
+ progress = start_sparse_progress(_("Sorting objects by packfile"),
+ m->num_objects);
+ display_progress(progress, 0); /* TODO: Measure QSORT() progress */
+ QSORT(pairs, m->num_objects, compare_pair_pos_vs_id);
+ stop_progress(&progress);
+
+ progress = start_sparse_progress(_("Verifying object offsets"), m->num_objects);
for (i = 0; i < m->num_objects; i++) {
struct object_id oid;
struct pack_entry e;
off_t m_offset, p_offset;
- nth_midxed_object_oid(&oid, m, i);
+ if (i > 0 && pairs[i-1].pack_int_id != pairs[i].pack_int_id &&
+ m->packs[pairs[i-1].pack_int_id])
+ {
+ close_pack_fd(m->packs[pairs[i-1].pack_int_id]);
+ close_pack_index(m->packs[pairs[i-1].pack_int_id]);
+ }
+
+ nth_midxed_object_oid(&oid, m, pairs[i].pos);
+
if (!fill_midx_entry(&oid, &e, m)) {
midx_report(_("failed to load pack entry for oid[%d] = %s"),
- i, oid_to_hex(&oid));
+ pairs[i].pos, oid_to_hex(&oid));
continue;
}
if (m_offset != p_offset)
midx_report(_("incorrect object offset for oid[%d] = %s: %"PRIx64" != %"PRIx64),
- i, oid_to_hex(&oid), m_offset, p_offset);
+ pairs[i].pos, oid_to_hex(&oid), m_offset, p_offset);
- display_progress(progress, i + 1);
+ midx_display_sparse_progress(progress, i + 1);
}
stop_progress(&progress);
+ free(pairs);
+
return verify_midx_error;
}
}
}
-static int close_pack_fd(struct packed_git *p)
+int close_pack_fd(struct packed_git *p)
{
if (p->pack_fd < 0)
return 0;
*/
extern void close_pack_index(struct packed_git *);
+int close_pack_fd(struct packed_git *p);
+
extern uint32_t get_pack_fanout(struct packed_git *p, uint32_t value);
extern unsigned char *use_pack(struct packed_git *, struct pack_window **, off_t, unsigned long *);
#include "color.h"
#include "utf8.h"
+static int disallow_abbreviated_options;
+
#define OPT_SHORT 1
#define OPT_UNSET 2
return get_value(p, options, all_opts, flags ^ opt_flags);
}
+ if (disallow_abbreviated_options && (ambiguous_option || abbrev_option))
+ die("disallowed abbreviated or ambiguous option '%.*s'",
+ (int)(arg_end - arg), arg);
+
if (ambiguous_option) {
error(_("ambiguous option: %s "
"(could be --%s%s or --%s%s)"),
{
struct parse_opt_ctx_t ctx;
+ disallow_abbreviated_options =
+ git_env_bool("GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS", 0);
+
parse_options_start(&ctx, argc, argv, prefix, options, flags);
switch (parse_options_step(&ctx, options, usagestr)) {
case PARSE_OPT_HELP:
BUG("option callback does not expect an argument"); \
} while (0)
+/*
+ * Similar to the assertions above, but checks that "arg" is always non-NULL.
+ * This assertion also implies BUG_ON_OPT_NEG(), letting you declare both
+ * assertions in a single line.
+ */
+#define BUG_ON_OPT_NEG_NOARG(unset, arg) do { \
+ BUG_ON_OPT_NEG(unset); \
+ if(!(arg)) \
+ BUG("option callback expects an argument"); \
+} while(0)
+
/*----- incremental advanced APIs -----*/
enum parse_opt_result {
{ 1, 1, 0, "logs" },
{ 1, 1, 1, "logs/HEAD" },
{ 0, 1, 1, "logs/refs/bisect" },
+ { 0, 1, 1, "logs/refs/rewritten" },
+ { 0, 1, 1, "logs/refs/worktree" },
{ 0, 1, 0, "lost-found" },
{ 0, 1, 0, "objects" },
{ 0, 1, 0, "refs" },
{ 0, 1, 1, "refs/bisect" },
+ { 0, 1, 1, "refs/rewritten" },
{ 0, 1, 1, "refs/worktree" },
{ 0, 1, 0, "remotes" },
{ 0, 1, 0, "worktrees" },
void packet_flush(int fd)
{
packet_trace("0000", 4, 1);
- write_or_die(fd, "0000", 4);
+ if (write_in_full(fd, "0000", 4) < 0)
+ die_errno(_("unable to write flush packet"));
}
void packet_delim(int fd)
{
packet_trace("0001", 4, 1);
- write_or_die(fd, "0001", 4);
+ if (write_in_full(fd, "0001", 4) < 0)
+ die_errno(_("unable to write delim packet"));
}
int packet_flush_gently(int fd)
uint64_t total;
unsigned last_percent;
unsigned delay;
+ unsigned sparse;
struct throughput *throughput;
uint64_t start_ns;
};
}
static struct progress *start_progress_delay(const char *title, uint64_t total,
- unsigned delay)
+ unsigned delay, unsigned sparse)
{
struct progress *progress = malloc(sizeof(*progress));
if (!progress) {
progress->last_value = -1;
progress->last_percent = -1;
progress->delay = delay;
+ progress->sparse = sparse;
progress->throughput = NULL;
progress->start_ns = getnanotime();
set_progress_signal();
struct progress *start_delayed_progress(const char *title, uint64_t total)
{
- return start_progress_delay(title, total, 2);
+ return start_progress_delay(title, total, 2, 0);
}
struct progress *start_progress(const char *title, uint64_t total)
{
- return start_progress_delay(title, total, 0);
+ return start_progress_delay(title, total, 0, 0);
+}
+
+/*
+ * Here "sparse" means that the caller might use some sampling criteria to
+ * decide when to call display_progress() rather than calling it for every
+ * integer value in[0 .. total). In particular, the caller might not call
+ * display_progress() for the last value in the range.
+ *
+ * When "sparse" is set, stop_progress() will automatically force the done
+ * message to show 100%.
+ */
+struct progress *start_sparse_progress(const char *title, uint64_t total)
+{
+ return start_progress_delay(title, total, 0, 1);
+}
+
+struct progress *start_delayed_sparse_progress(const char *title,
+ uint64_t total)
+{
+ return start_progress_delay(title, total, 2, 1);
+}
+
+static void finish_if_sparse(struct progress *progress)
+{
+ if (progress &&
+ progress->sparse &&
+ progress->last_value != progress->total)
+ display_progress(progress, progress->total);
}
void stop_progress(struct progress **p_progress)
{
+ finish_if_sparse(*p_progress);
+
stop_progress_msg(p_progress, _("done"));
}
void display_throughput(struct progress *progress, uint64_t total);
int display_progress(struct progress *progress, uint64_t n);
struct progress *start_progress(const char *title, uint64_t total);
+struct progress *start_sparse_progress(const char *title, uint64_t total);
struct progress *start_delayed_progress(const char *title, uint64_t total);
+struct progress *start_delayed_sparse_progress(const char *title,
+ uint64_t total);
void stop_progress(struct progress **progress);
void stop_progress_msg(struct progress **progress, const char *msg);
enum protocol_version get_protocol_version_config(void)
{
const char *value;
+ enum protocol_version retval = protocol_v0;
+ const char *git_test_k = "GIT_TEST_PROTOCOL_VERSION";
+ const char *git_test_v = getenv(git_test_k);
+
if (!git_config_get_string_const("protocol.version", &value)) {
enum protocol_version version = parse_protocol_version(value);
die("unknown value for config 'protocol.version': %s",
value);
- return version;
+ retval = version;
+ }
+
+ if (git_test_v && *git_test_v) {
+ enum protocol_version env = parse_protocol_version(git_test_v);
+
+ if (env == protocol_unknown_version)
+ die("unknown value for %s: %s", git_test_k, git_test_v);
+ if (retval < env)
+ retval = env;
}
- return protocol_v0;
+ return retval;
}
enum protocol_version determine_protocol_version_server(void)
#include "commit.h"
#include "blob.h"
#include "resolve-undo.h"
+#include "run-command.h"
#include "strbuf.h"
#include "varint.h"
#include "split-index.h"
if (ret)
return ret;
if (flags & COMMIT_LOCK)
- return commit_locked_index(lock);
- return close_lock_file_gently(lock);
+ ret = commit_locked_index(lock);
+ else
+ ret = close_lock_file_gently(lock);
+
+ run_hook_le(NULL, "post-index-change",
+ istate->updated_workdir ? "1" : "0",
+ istate->updated_skipworktree ? "1" : "0", NULL);
+ istate->updated_workdir = 0;
+ istate->updated_skipworktree = 0;
+
+ return ret;
}
static int write_split_index(struct index_state *istate,
#include "cache.h"
#include "commit.h"
-#include "rebase-interactive.h"
#include "sequencer.h"
+#include "rebase-interactive.h"
#include "strbuf.h"
+#include "commit-slab.h"
+#include "config.h"
+
+enum missing_commit_check_level {
+ MISSING_COMMIT_CHECK_IGNORE = 0,
+ MISSING_COMMIT_CHECK_WARN,
+ MISSING_COMMIT_CHECK_ERROR
+};
+
+static enum missing_commit_check_level get_missing_commit_check_level(void)
+{
+ const char *value;
+
+ if (git_config_get_value("rebase.missingcommitscheck", &value) ||
+ !strcasecmp("ignore", value))
+ return MISSING_COMMIT_CHECK_IGNORE;
+ if (!strcasecmp("warn", value))
+ return MISSING_COMMIT_CHECK_WARN;
+ if (!strcasecmp("error", value))
+ return MISSING_COMMIT_CHECK_ERROR;
+ warning(_("unrecognized setting %s for option "
+ "rebase.missingCommitsCheck. Ignoring."), value);
+ return MISSING_COMMIT_CHECK_IGNORE;
+}
-void append_todo_help(unsigned edit_todo, unsigned keep_empty,
+void append_todo_help(unsigned keep_empty, int command_count,
+ const char *shortrevisions, const char *shortonto,
struct strbuf *buf)
{
const char *msg = _("\nCommands:\n"
". specified). Use -c <commit> to reword the commit message.\n"
"\n"
"These lines can be re-ordered; they are executed from top to bottom.\n");
+ unsigned edit_todo = !(shortrevisions && shortonto);
+
+ if (!edit_todo) {
+ strbuf_addch(buf, '\n');
+ strbuf_commented_addf(buf, Q_("Rebase %s onto %s (%d command)",
+ "Rebase %s onto %s (%d commands)",
+ command_count),
+ shortrevisions, shortonto, command_count);
+ }
strbuf_add_commented_lines(buf, msg, strlen(msg));
}
}
-int edit_todo_list(struct repository *r, unsigned flags)
+int edit_todo_list(struct repository *r, struct todo_list *todo_list,
+ struct todo_list *new_todo, const char *shortrevisions,
+ const char *shortonto, unsigned flags)
{
- struct strbuf buf = STRBUF_INIT;
const char *todo_file = rebase_path_todo();
+ unsigned initial = shortrevisions && shortonto;
- if (strbuf_read_file(&buf, todo_file, 0) < 0)
- return error_errno(_("could not read '%s'."), todo_file);
+ /* If the user is editing the todo list, we first try to parse
+ * it. If there is an error, we do not return, because the user
+ * might want to fix it in the first place. */
+ if (!initial)
+ todo_list_parse_insn_buffer(r, todo_list->buf.buf, todo_list);
- strbuf_stripspace(&buf, 1);
- if (write_message(buf.buf, buf.len, todo_file, 0)) {
- strbuf_release(&buf);
- return -1;
- }
+ if (todo_list_write_to_file(r, todo_list, todo_file, shortrevisions, shortonto,
+ -1, flags | TODO_LIST_SHORTEN_IDS | TODO_LIST_APPEND_TODO_HELP))
+ return error_errno(_("could not write '%s'"), todo_file);
+
+ if (initial && copy_file(rebase_path_todo_backup(), todo_file, 0666))
+ return error(_("could not copy '%s' to '%s'."), todo_file,
+ rebase_path_todo_backup());
- strbuf_release(&buf);
+ if (launch_sequence_editor(todo_file, &new_todo->buf, NULL))
+ return -2;
- transform_todos(r, flags | TODO_LIST_SHORTEN_IDS);
+ strbuf_stripspace(&new_todo->buf, 1);
+ if (initial && new_todo->buf.len == 0)
+ return -3;
- if (strbuf_read_file(&buf, todo_file, 0) < 0)
- return error_errno(_("could not read '%s'."), todo_file);
+ /* For the initial edit, the todo list gets parsed in
+ * complete_action(). */
+ if (!initial)
+ return todo_list_parse_insn_buffer(r, new_todo->buf.buf, new_todo);
- append_todo_help(1, 0, &buf);
- if (write_message(buf.buf, buf.len, todo_file, 0)) {
- strbuf_release(&buf);
- return -1;
+ return 0;
+}
+
+define_commit_slab(commit_seen, unsigned char);
+/*
+ * Check if the user dropped some commits by mistake
+ * Behaviour determined by rebase.missingCommitsCheck.
+ * Check if there is an unrecognized command or a
+ * bad SHA-1 in a command.
+ */
+int todo_list_check(struct todo_list *old_todo, struct todo_list *new_todo)
+{
+ enum missing_commit_check_level check_level = get_missing_commit_check_level();
+ struct strbuf missing = STRBUF_INIT;
+ int res = 0, i;
+ struct commit_seen commit_seen;
+
+ init_commit_seen(&commit_seen);
+
+ if (check_level == MISSING_COMMIT_CHECK_IGNORE)
+ goto leave_check;
+
+ /* Mark the commits in git-rebase-todo as seen */
+ for (i = 0; i < new_todo->nr; i++) {
+ struct commit *commit = new_todo->items[i].commit;
+ if (commit)
+ *commit_seen_at(&commit_seen, commit) = 1;
}
- strbuf_release(&buf);
+ /* Find commits in git-rebase-todo.backup yet unseen */
+ for (i = old_todo->nr - 1; i >= 0; i--) {
+ struct todo_item *item = old_todo->items + i;
+ struct commit *commit = item->commit;
+ if (commit && !*commit_seen_at(&commit_seen, commit)) {
+ strbuf_addf(&missing, " - %s %.*s\n",
+ find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV),
+ item->arg_len,
+ todo_item_get_arg(old_todo, item));
+ *commit_seen_at(&commit_seen, commit) = 1;
+ }
+ }
- if (launch_sequence_editor(todo_file, NULL, NULL))
- return -1;
+ /* Warn about missing commits */
+ if (!missing.len)
+ goto leave_check;
- transform_todos(r, flags & ~(TODO_LIST_SHORTEN_IDS));
+ if (check_level == MISSING_COMMIT_CHECK_ERROR)
+ res = 1;
- return 0;
+ fprintf(stderr,
+ _("Warning: some commits may have been dropped accidentally.\n"
+ "Dropped commits (newer to older):\n"));
+
+ /* Make the list user-friendly and display */
+ fputs(missing.buf, stderr);
+ strbuf_release(&missing);
+
+ fprintf(stderr, _("To avoid this message, use \"drop\" to "
+ "explicitly remove a commit.\n\n"
+ "Use 'git config rebase.missingCommitsCheck' to change "
+ "the level of warnings.\n"
+ "The possible behaviours are: ignore, warn, error.\n\n"));
+
+leave_check:
+ clear_commit_seen(&commit_seen);
+ return res;
}
struct strbuf;
struct repository;
+struct todo_list;
-void append_todo_help(unsigned edit_todo, unsigned keep_empty,
+void append_todo_help(unsigned keep_empty, int command_count,
+ const char *shortrevisions, const char *shortonto,
struct strbuf *buf);
-int edit_todo_list(struct repository *r, unsigned flags);
+int edit_todo_list(struct repository *r, struct todo_list *todo_list,
+ struct todo_list *new_todo, const char *shortrevisions,
+ const char *shortonto, unsigned flags);
+int todo_list_check(struct todo_list *old_todo, struct todo_list *new_todo);
#endif
}
}
+/*
+ * Manually add refs/bisect, refs/rewritten and refs/worktree, which, being
+ * per-worktree, might not appear in the directory listing for
+ * refs/ in the main repo.
+ */
+static void add_per_worktree_entries_to_dir(struct ref_dir *dir, const char *dirname)
+{
+ const char *prefixes[] = { "refs/bisect/", "refs/worktree/", "refs/rewritten/" };
+ int ip;
+
+ if (strcmp(dirname, "refs/"))
+ return;
+
+ for (ip = 0; ip < ARRAY_SIZE(prefixes); ip++) {
+ const char *prefix = prefixes[ip];
+ int prefix_len = strlen(prefix);
+ struct ref_entry *child_entry;
+ int pos;
+
+ pos = search_ref_dir(dir, prefix, prefix_len);
+ if (pos >= 0)
+ continue;
+ child_entry = create_dir_entry(dir->cache, prefix, prefix_len, 1);
+ add_entry_to_dir(dir, child_entry);
+ }
+}
+
/*
* Read the loose references from the namespace dirname into dir
* (without recursing). dirname must end with '/'. dir must be the
strbuf_release(&path);
closedir(d);
- /*
- * Manually add refs/bisect and refs/worktree, which, being
- * per-worktree, might not appear in the directory listing for
- * refs/ in the main repo.
- */
- if (!strcmp(dirname, "refs/")) {
- int pos = search_ref_dir(dir, "refs/bisect/", 12);
-
- if (pos < 0) {
- struct ref_entry *child_entry = create_dir_entry(
- dir->cache, "refs/bisect/", 12, 1);
- add_entry_to_dir(dir, child_entry);
- }
-
- pos = search_ref_dir(dir, "refs/worktree/", 11);
-
- if (pos < 0) {
- struct ref_entry *child_entry = create_dir_entry(
- dir->cache, "refs/worktree/", 11, 1);
- add_entry_to_dir(dir, child_entry);
- }
- }
+ add_per_worktree_entries_to_dir(dir, dirname);
}
static struct ref_cache *get_loose_ref_cache(struct files_ref_store *refs)
if (is_packed_transaction_needed(refs->packed_ref_store,
packed_transaction)) {
ret = ref_transaction_prepare(packed_transaction, err);
+ /*
+ * A failure during the prepare step will abort
+ * itself, but not free. Do that now, and disconnect
+ * from the files_transaction so it does not try to
+ * abort us when we hit the cleanup code below.
+ */
+ if (ret) {
+ ref_transaction_free(packed_transaction);
+ backend_data->packed_transaction = NULL;
+ }
} else {
/*
* We can skip rewriting the `packed-refs`
* file. But we do need to leave it locked, so
* that somebody else doesn't pack a reference
* that we are trying to delete.
+ *
+ * We need to disconnect our transaction from
+ * backend_data, since the abort (whether successful or
+ * not) will free it.
*/
+ backend_data->packed_transaction = NULL;
if (ref_transaction_abort(packed_transaction, err)) {
ret = TRANSACTION_GENERIC_ERROR;
goto cleanup;
}
- backend_data->packed_transaction = NULL;
}
}
#include "send-pack.h"
#include "protocol.h"
#include "quote.h"
+#include "transport.h"
static struct remote *remote;
/* always ends with a trailing slash */
else {
struct strbuf unquoted = STRBUF_INIT;
if (unquote_c_style(&unquoted, value, NULL) < 0)
- die("invalid quoting in push-option value");
+ die(_("invalid quoting in push-option value: '%s'"), value);
string_list_append_nodup(&options.push_options,
strbuf_detach(&unquoted, NULL));
}
mid = &data[i];
if (data[i] == '\n') {
if (mid - start != 40)
- die("%sinfo/refs not valid: is this a git repository?",
- url.buf);
+ die(_("%sinfo/refs not valid: is this a git repository?"),
+ transport_anonymize_url(url.buf));
data[i] = 0;
ref_name = mid + 1;
ref = alloc_ref(ref_name);
PACKET_READ_CHOMP_NEWLINE |
PACKET_READ_DIE_ON_ERR_PACKET);
if (packet_reader_read(&reader) != PACKET_READ_NORMAL)
- die("invalid server response; expected service, got flush packet");
+ die(_("invalid server response; expected service, got flush packet"));
if (skip_prefix(reader.line, "# service=", &p) && !strcmp(p, service)) {
/*
d->proto_git = 1;
} else {
- die("invalid server response; got '%s'", reader.line);
+ die(_("invalid server response; got '%s'"), reader.line);
}
}
break;
case HTTP_MISSING_TARGET:
show_http_message(&type, &charset, &buffer);
- die("repository '%s' not found", url.buf);
+ die(_("repository '%s' not found"),
+ transport_anonymize_url(url.buf));
case HTTP_NOAUTH:
show_http_message(&type, &charset, &buffer);
- die("Authentication failed for '%s'", url.buf);
+ die(_("Authentication failed for '%s'"),
+ transport_anonymize_url(url.buf));
default:
show_http_message(&type, &charset, &buffer);
- die("unable to access '%s': %s", url.buf, curl_errorstr);
+ die(_("unable to access '%s': %s"),
+ transport_anonymize_url(url.buf), curl_errorstr);
}
- if (options.verbosity && !starts_with(refs_url.buf, url.buf))
- warning(_("redirecting to %s"), url.buf);
+ if (options.verbosity && !starts_with(refs_url.buf, url.buf)) {
+ char *u = transport_anonymize_url(url.buf);
+ warning(_("redirecting to %s"), u);
+ free(u);
+ }
last= xcalloc(1, sizeof(*last_discovery));
last->service = xstrdup(service);
switch (*status) {
case PACKET_READ_EOF:
if (!(options & PACKET_READ_GENTLE_ON_EOF))
- die("shouldn't have EOF when not gentle on EOF");
+ die(_("shouldn't have EOF when not gentle on EOF"));
break;
case PACKET_READ_NORMAL:
set_packet_header(buf - 4, *appended);
rpc->pos = 0;
return CURLIOE_OK;
}
- error("unable to rewind rpc post data - try increasing http.postBuffer");
+ error(_("unable to rewind rpc post data - try increasing http.postBuffer"));
return CURLIOE_FAILRESTART;
default:
strbuf_addstr(&msg, curl_errorstr);
}
}
- error("RPC failed; %s", msg.buf);
+ error(_("RPC failed; %s"), msg.buf);
strbuf_release(&msg);
}
{
uintmax_t size = len;
if (size > maximum_signed_value_of_type(curl_off_t))
- die("cannot handle pushes this big");
+ die(_("cannot handle pushes this big"));
return (curl_off_t)size;
}
ret = git_deflate(&stream, Z_FINISH);
if (ret != Z_STREAM_END)
- die("cannot deflate request; zlib deflate error %d", ret);
+ die(_("cannot deflate request; zlib deflate error %d"), ret);
ret = git_deflate_end_gently(&stream);
if (ret != Z_OK)
- die("cannot deflate request; zlib end error %d", ret);
+ die(_("cannot deflate request; zlib end error %d"), ret);
gzip_size = stream.total_out;
ALLOC_ARRAY(targets, nr_heads);
if (options.depth || options.deepen_since)
- die("dumb http transport does not support shallow capabilities");
+ die(_("dumb http transport does not support shallow capabilities"));
for (i = 0; i < nr_heads; i++)
targets[i] = xstrdup(oid_to_hex(&to_fetch[i]->old_oid));
free(targets[i]);
free(targets);
- return ret ? error("fetch failed.") : 0;
+ return ret ? error(_("fetch failed.")) : 0;
}
static int fetch_git(struct discovery *heads,
for (i = 0; i < nr_heads; i++) {
struct ref *ref = to_fetch[i];
if (!*ref->name)
- die("cannot fetch by sha1 over smart http");
+ die(_("cannot fetch by sha1 over smart http"));
packet_buf_write(&preamble, "%s %s\n",
oid_to_hex(&ref->old_oid), ref->name);
}
struct object_id old_oid;
if (get_oid_hex(p, &old_oid))
- die("protocol error: expected sha/ref, got %s'", p);
+ die(_("protocol error: expected sha/ref, got %s'"), p);
if (p[GIT_SHA1_HEXSZ] == ' ')
name = p + GIT_SHA1_HEXSZ + 1;
else if (!p[GIT_SHA1_HEXSZ])
name = "";
else
- die("protocol error: expected sha/ref, got %s'", p);
+ die(_("protocol error: expected sha/ref, got %s'"), p);
ref = alloc_ref(name);
oidcpy(&ref->old_oid, &old_oid);
to_fetch[nr_heads++] = ref;
}
else
- die("http transport does not support %s", buf->buf);
+ die(_("http transport does not support %s"), buf->buf);
strbuf_reset(buf);
if (strbuf_getline_lf(buf, stdin) == EOF)
argv_array_push(&child.args, specs[i]);
if (run_command(&child))
- die("git-http-push failed");
+ die(_("git-http-push failed"));
return 0;
}
specs[nr_spec++] = xstrdup(buf->buf + 5);
}
else
- die("http transport does not support %s", buf->buf);
+ die(_("http transport does not support %s"), buf->buf);
strbuf_reset(buf);
if (strbuf_getline_lf(buf, stdin) == EOF)
setup_git_directory_gently(&nongit);
if (argc < 2) {
- error("remote-curl: usage: git remote-curl <remote> [<url>]");
+ error(_("remote-curl: usage: git remote-curl <remote> [<url>]"));
return 1;
}
if (strbuf_getline_lf(&buf, stdin) == EOF) {
if (ferror(stdin))
- error("remote-curl: error reading command stream from git");
+ error(_("remote-curl: error reading command stream from git"));
return 1;
}
if (buf.len == 0)
break;
if (starts_with(buf.buf, "fetch ")) {
if (nongit)
- die("remote-curl: fetch attempted without a local repo");
+ die(_("remote-curl: fetch attempted without a local repo"));
parse_fetch(&buf);
} else if (!strcmp(buf.buf, "list") || starts_with(buf.buf, "list ")) {
if (!stateless_connect(arg))
break;
} else {
- error("remote-curl: unknown command '%s' from git", buf.buf);
+ error(_("remote-curl: unknown command '%s' from git"), buf.buf);
return 1;
}
strbuf_reset(&buf);
const char *gitdir,
const char *worktree)
{
- struct repository_format format;
+ struct repository_format format = REPOSITORY_FORMAT_INIT;
memset(repo, 0, sizeof(*repo));
repo->objects = raw_object_store_new();
if (worktree)
repo_set_worktree(repo, worktree);
+ clear_repository_format(&format);
return 0;
error:
if (revs->first_parent_only && revs->bisect)
die(_("--first-parent is incompatible with --bisect"));
+ if (revs->line_level_traverse &&
+ (revs->diffopt.output_format & ~(DIFF_FORMAT_PATCH | DIFF_FORMAT_NO_OUTPUT)))
+ die(_("-L does not yet support diff formats besides -p and -s"));
+
if (revs->expand_tabs_in_log < 0)
revs->expand_tabs_in_log = revs->expand_tabs_in_log_default;
* file and written to the tail of 'done'.
*/
GIT_PATH_FUNC(rebase_path_todo, "rebase-merge/git-rebase-todo")
-static GIT_PATH_FUNC(rebase_path_todo_backup,
- "rebase-merge/git-rebase-todo.backup")
+GIT_PATH_FUNC(rebase_path_todo_backup, "rebase-merge/git-rebase-todo.backup")
/*
* The rebase command lines that have already been processed. A line
}
}
-int write_message(const void *buf, size_t len, const char *filename,
- int append_eol)
+static int write_message(const void *buf, size_t len, const char *filename,
+ int append_eol)
{
struct lock_file msg_file = LOCK_INIT;
return 1;
}
-/*
- * Note that ordering matters in this enum. Not only must it match the mapping
- * below, it is also divided into several sections that matter. When adding
- * new commands, make sure you add it in the right section.
- */
-enum todo_command {
- /* commands that handle commits */
- TODO_PICK = 0,
- TODO_REVERT,
- TODO_EDIT,
- TODO_REWORD,
- TODO_FIXUP,
- TODO_SQUASH,
- /* commands that do something else than handling a single commit */
- TODO_EXEC,
- TODO_BREAK,
- TODO_LABEL,
- TODO_RESET,
- TODO_MERGE,
- /* commands that do nothing but are counted for reporting progress */
- TODO_NOOP,
- TODO_DROP,
- /* comments (not counted for reporting progress) */
- TODO_COMMENT
-};
-
static struct {
char c;
const char *str;
TODO_EDIT_MERGE_MSG = 1
};
-struct todo_item {
- enum todo_command command;
- struct commit *commit;
- unsigned int flags;
- const char *arg;
- int arg_len;
- size_t offset_in_buf;
-};
-
-struct todo_list {
- struct strbuf buf;
- struct todo_item *items;
- int nr, alloc, current;
- int done_nr, total_nr;
- struct stat_data stat;
-};
-
-#define TODO_LIST_INIT { STRBUF_INIT }
-
-static void todo_list_release(struct todo_list *todo_list)
+void todo_list_release(struct todo_list *todo_list)
{
strbuf_release(&todo_list->buf);
FREE_AND_NULL(todo_list->items);
return todo_list->items + todo_list->nr++;
}
+const char *todo_item_get_arg(struct todo_list *todo_list,
+ struct todo_item *item)
+{
+ return todo_list->buf.buf + item->arg_offset;
+}
+
static int parse_insn_line(struct repository *r, struct todo_item *item,
- const char *bol, char *eol)
+ const char *buf, const char *bol, char *eol)
{
struct object_id commit_oid;
char *end_of_object_name;
if (bol == eol || *bol == '\r' || *bol == comment_line_char) {
item->command = TODO_COMMENT;
item->commit = NULL;
- item->arg = bol;
+ item->arg_offset = bol - buf;
item->arg_len = eol - bol;
return 0;
}
return error(_("%s does not accept arguments: '%s'"),
command_to_string(item->command), bol);
item->commit = NULL;
- item->arg = bol;
+ item->arg_offset = bol - buf;
item->arg_len = eol - bol;
return 0;
}
if (item->command == TODO_EXEC || item->command == TODO_LABEL ||
item->command == TODO_RESET) {
item->commit = NULL;
- item->arg = bol;
+ item->arg_offset = bol - buf;
item->arg_len = (int)(eol - bol);
return 0;
}
} else {
item->flags |= TODO_EDIT_MERGE_MSG;
item->commit = NULL;
- item->arg = bol;
+ item->arg_offset = bol - buf;
item->arg_len = (int)(eol - bol);
return 0;
}
status = get_oid(bol, &commit_oid);
*end_of_object_name = saved;
- item->arg = end_of_object_name + strspn(end_of_object_name, " \t");
- item->arg_len = (int)(eol - item->arg);
+ bol = end_of_object_name + strspn(end_of_object_name, " \t");
+ item->arg_offset = bol - buf;
+ item->arg_len = (int)(eol - bol);
if (status < 0)
- return -1;
+ return error(_("could not parse '%.*s'"),
+ (int)(end_of_object_name - bol), bol);
item->commit = lookup_commit_reference(r, &commit_oid);
return !item->commit;
}
-static int parse_insn_buffer(struct repository *r, char *buf,
- struct todo_list *todo_list)
+int todo_list_parse_insn_buffer(struct repository *r, char *buf,
+ struct todo_list *todo_list)
{
struct todo_item *item;
char *p = buf, *next_p;
int i, res = 0, fixup_okay = file_exists(rebase_path_done());
+ todo_list->current = todo_list->nr = 0;
+
for (i = 1; *p; i++, p = next_p) {
char *eol = strchrnul(p, '\n');
item = append_new_todo(todo_list);
item->offset_in_buf = p - todo_list->buf.buf;
- if (parse_insn_line(r, item, p, eol)) {
+ if (parse_insn_line(r, item, buf, p, eol)) {
res = error(_("invalid line %d: %.*s"),
i, (int)(eol - p), p);
- item->command = TODO_NOOP;
+ item->command = TODO_COMMENT + 1;
+ item->arg_offset = p - buf;
+ item->arg_len = (int)(eol - p);
+ item->commit = NULL;
}
if (fixup_okay)
return error(_("could not stat '%s'"), todo_file);
fill_stat_data(&todo_list->stat, &st);
- res = parse_insn_buffer(r, todo_list->buf.buf, todo_list);
+ res = todo_list_parse_insn_buffer(r, todo_list->buf.buf, todo_list);
if (res) {
if (is_rebase_i(opts))
return error(_("please fix this using "
FILE *f = fopen_or_warn(rebase_path_msgtotal(), "w");
if (strbuf_read_file(&done.buf, rebase_path_done(), 0) > 0 &&
- !parse_insn_buffer(r, done.buf.buf, &done))
+ !todo_list_parse_insn_buffer(r, done.buf.buf, &done))
todo_list->done_nr = count_commands(&done);
else
todo_list->done_nr = 0;
item->command = command;
item->commit = commit;
- item->arg = NULL;
+ item->arg_offset = 0;
item->arg_len = 0;
item->offset_in_buf = todo_list->buf.len;
subject_len = find_commit_subject(commit_buffer, &subject);
while (todo_list->current < todo_list->nr) {
struct todo_item *item = todo_list->items + todo_list->current;
+ const char *arg = todo_item_get_arg(todo_list, item);
+
if (save_todo(todo_list, opts))
return -1;
if (is_rebase_i(opts)) {
fprintf(stderr,
_("Stopped at %s... %.*s\n"),
short_commit_name(commit),
- item->arg_len, item->arg);
+ item->arg_len, arg);
return error_with_patch(r, commit,
- item->arg, item->arg_len, opts, res,
- !res);
+ arg, item->arg_len, opts, res, !res);
}
if (is_rebase_i(opts) && !res)
record_in_rewritten(&item->commit->object.oid,
if (res == 1)
intend_to_amend();
return error_failed_squash(r, item->commit, opts,
- item->arg_len, item->arg);
+ item->arg_len, arg);
} else if (res && is_rebase_i(opts) && item->commit) {
int to_amend = 0;
struct object_id oid;
to_amend = 1;
return res | error_with_patch(r, item->commit,
- item->arg, item->arg_len, opts,
+ arg, item->arg_len, opts,
res, to_amend);
}
} else if (item->command == TODO_EXEC) {
- char *end_of_arg = (char *)(item->arg + item->arg_len);
+ char *end_of_arg = (char *)(arg + item->arg_len);
int saved = *end_of_arg;
struct stat st;
*end_of_arg = '\0';
- res = do_exec(r, item->arg);
+ res = do_exec(r, arg);
*end_of_arg = saved;
- /* Reread the todo file if it has changed. */
if (res) {
if (opts->reschedule_failed_exec)
reschedule = 1;
res = error_errno(_("could not stat '%s'"),
get_todo_path(opts));
else if (match_stat_data(&todo_list->stat, &st)) {
+ /* Reread the todo file if it has changed. */
todo_list_release(todo_list);
if (read_populate_todo(r, todo_list, opts))
res = -1; /* message was printed */
todo_list->current = -1;
}
} else if (item->command == TODO_LABEL) {
- if ((res = do_label(r, item->arg, item->arg_len)))
+ if ((res = do_label(r, arg, item->arg_len)))
reschedule = 1;
} else if (item->command == TODO_RESET) {
- if ((res = do_reset(r, item->arg, item->arg_len, opts)))
+ if ((res = do_reset(r, arg, item->arg_len, opts)))
reschedule = 1;
} else if (item->command == TODO_MERGE) {
if ((res = do_merge(r, item->commit,
- item->arg, item->arg_len,
+ arg, item->arg_len,
item->flags, opts)) < 0)
reschedule = 1;
else if (item->commit)
if (res > 0)
/* failed with merge conflicts */
return error_with_patch(r, item->commit,
- item->arg,
- item->arg_len, opts,
- res, 0);
+ arg, item->arg_len,
+ opts, res, 0);
} else if (!is_noop(item->command))
return error(_("unknown command %d"), item->command);
if (item->commit)
return error_with_patch(r,
item->commit,
- item->arg,
- item->arg_len, opts,
- res, 0);
+ arg, item->arg_len,
+ opts, res, 0);
}
todo_list->current++;
}
static int make_script_with_merges(struct pretty_print_context *pp,
- struct rev_info *revs, FILE *out,
+ struct rev_info *revs, struct strbuf *out,
unsigned flags)
{
int keep_empty = flags & TODO_LIST_KEEP_EMPTY;
* gathering commits not yet shown, reversing the list on the fly,
* then outputting that list (labeling revisions as needed).
*/
- fprintf(out, "%s onto\n", cmd_label);
+ strbuf_addf(out, "%s onto\n", cmd_label);
for (iter = tips; iter; iter = iter->next) {
struct commit_list *list = NULL, *iter2;
entry = oidmap_get(&state.commit2label, &commit->object.oid);
if (entry)
- fprintf(out, "\n%c Branch %s\n", comment_line_char, entry->string);
+ strbuf_addf(out, "\n%c Branch %s\n", comment_line_char, entry->string);
else
- fprintf(out, "\n");
+ strbuf_addch(out, '\n');
while (oidset_contains(&interesting, &commit->object.oid) &&
!oidset_contains(&shown, &commit->object.oid)) {
}
if (!commit)
- fprintf(out, "%s %s\n", cmd_reset,
- rebase_cousins ? "onto" : "[new root]");
+ strbuf_addf(out, "%s %s\n", cmd_reset,
+ rebase_cousins ? "onto" : "[new root]");
else {
const char *to = NULL;
&state);
if (!to || !strcmp(to, "onto"))
- fprintf(out, "%s onto\n", cmd_reset);
+ strbuf_addf(out, "%s onto\n", cmd_reset);
else {
strbuf_reset(&oneline);
pretty_print_commit(pp, commit, &oneline);
- fprintf(out, "%s %s # %s\n",
- cmd_reset, to, oneline.buf);
+ strbuf_addf(out, "%s %s # %s\n",
+ cmd_reset, to, oneline.buf);
}
}
entry = oidmap_get(&commit2todo, oid);
/* only show if not already upstream */
if (entry)
- fprintf(out, "%s\n", entry->string);
+ strbuf_addf(out, "%s\n", entry->string);
entry = oidmap_get(&state.commit2label, oid);
if (entry)
- fprintf(out, "%s %s\n",
- cmd_label, entry->string);
+ strbuf_addf(out, "%s %s\n",
+ cmd_label, entry->string);
oidset_insert(&shown, oid);
}
return 0;
}
-int sequencer_make_script(struct repository *r, FILE *out,
- int argc, const char **argv,
- unsigned flags)
+int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
+ const char **argv, unsigned flags)
{
char *format = NULL;
struct pretty_print_context pp = {0};
- struct strbuf buf = STRBUF_INIT;
struct rev_info revs;
struct commit *commit;
int keep_empty = flags & TODO_LIST_KEEP_EMPTY;
if (!is_empty && (commit->object.flags & PATCHSAME))
continue;
- strbuf_reset(&buf);
if (!keep_empty && is_empty)
- strbuf_addf(&buf, "%c ", comment_line_char);
- strbuf_addf(&buf, "%s %s ", insn,
+ strbuf_addf(out, "%c ", comment_line_char);
+ strbuf_addf(out, "%s %s ", insn,
oid_to_hex(&commit->object.oid));
- pretty_print_commit(&pp, commit, &buf);
- strbuf_addch(&buf, '\n');
- fputs(buf.buf, out);
+ pretty_print_commit(&pp, commit, out);
+ strbuf_addch(out, '\n');
}
- strbuf_release(&buf);
return 0;
}
* Add commands after pick and (series of) squash/fixup commands
* in the todo list.
*/
-int sequencer_add_exec_commands(struct repository *r,
- const char *commands)
+void todo_list_add_exec_commands(struct todo_list *todo_list,
+ struct string_list *commands)
{
- const char *todo_file = rebase_path_todo();
- struct todo_list todo_list = TODO_LIST_INIT;
- struct strbuf *buf = &todo_list.buf;
- size_t offset = 0, commands_len = strlen(commands);
- int i, insert;
+ struct strbuf *buf = &todo_list->buf;
+ size_t base_offset = buf->len;
+ int i, insert, nr = 0, alloc = 0;
+ struct todo_item *items = NULL, *base_items = NULL;
+
+ base_items = xcalloc(commands->nr, sizeof(struct todo_item));
+ for (i = 0; i < commands->nr; i++) {
+ size_t command_len = strlen(commands->items[i].string);
- if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
- return error(_("could not read '%s'."), todo_file);
+ strbuf_addstr(buf, commands->items[i].string);
+ strbuf_addch(buf, '\n');
- if (parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {
- todo_list_release(&todo_list);
- return error(_("unusable todo list: '%s'"), todo_file);
+ base_items[i].command = TODO_EXEC;
+ base_items[i].offset_in_buf = base_offset;
+ base_items[i].arg_offset = base_offset + strlen("exec ");
+ base_items[i].arg_len = command_len - strlen("exec ");
+
+ base_offset += command_len + 1;
}
/*
* Insert <commands> after every pick. Here, fixup/squash chains
* are considered part of the pick, so we insert the commands *after*
* those chains if there are any.
+ *
+ * As we insert the exec commands immediatly after rearranging
+ * any fixups and before the user edits the list, a fixup chain
+ * can never contain comments (any comments are empty picks that
+ * have been commented out because the user did not specify
+ * --keep-empty). So, it is safe to insert an exec command
+ * without looking at the command following a comment.
*/
- insert = -1;
- for (i = 0; i < todo_list.nr; i++) {
- enum todo_command command = todo_list.items[i].command;
-
- if (insert >= 0) {
- /* skip fixup/squash chains */
- if (command == TODO_COMMENT)
- continue;
- else if (is_fixup(command)) {
- insert = i + 1;
- continue;
- }
- strbuf_insert(buf,
- todo_list.items[insert].offset_in_buf +
- offset, commands, commands_len);
- offset += commands_len;
- insert = -1;
+ insert = 0;
+ for (i = 0; i < todo_list->nr; i++) {
+ enum todo_command command = todo_list->items[i].command;
+ if (insert && !is_fixup(command)) {
+ ALLOC_GROW(items, nr + commands->nr, alloc);
+ COPY_ARRAY(items + nr, base_items, commands->nr);
+ nr += commands->nr;
+
+ insert = 0;
}
+ ALLOC_GROW(items, nr + 1, alloc);
+ items[nr++] = todo_list->items[i];
+
if (command == TODO_PICK || command == TODO_MERGE)
- insert = i + 1;
+ insert = 1;
}
/* insert or append final <commands> */
- if (insert >= 0 && insert < todo_list.nr)
- strbuf_insert(buf, todo_list.items[insert].offset_in_buf +
- offset, commands, commands_len);
- else if (insert >= 0 || !offset)
- strbuf_add(buf, commands, commands_len);
+ if (insert || nr == todo_list->nr) {
+ ALLOC_GROW(items, nr + commands->nr, alloc);
+ COPY_ARRAY(items + nr, base_items, commands->nr);
+ nr += commands->nr;
+ }
- i = write_message(buf->buf, buf->len, todo_file, 0);
- todo_list_release(&todo_list);
- return i;
+ free(base_items);
+ FREE_AND_NULL(todo_list->items);
+ todo_list->items = items;
+ todo_list->nr = nr;
+ todo_list->alloc = alloc;
}
-int transform_todos(struct repository *r, unsigned flags)
+static void todo_list_to_strbuf(struct repository *r, struct todo_list *todo_list,
+ struct strbuf *buf, int num, unsigned flags)
{
- const char *todo_file = rebase_path_todo();
- struct todo_list todo_list = TODO_LIST_INIT;
- struct strbuf buf = STRBUF_INIT;
struct todo_item *item;
- int i;
-
- if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
- return error(_("could not read '%s'."), todo_file);
+ int i, max = todo_list->nr;
- if (parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {
- todo_list_release(&todo_list);
- return error(_("unusable todo list: '%s'"), todo_file);
- }
+ if (num > 0 && num < max)
+ max = num;
- for (item = todo_list.items, i = 0; i < todo_list.nr; i++, item++) {
+ for (item = todo_list->items, i = 0; i < max; i++, item++) {
/* if the item is not a command write it and continue */
if (item->command >= TODO_COMMENT) {
- strbuf_addf(&buf, "%.*s\n", item->arg_len, item->arg);
+ strbuf_addf(buf, "%.*s\n", item->arg_len,
+ todo_item_get_arg(todo_list, item));
continue;
}
/* add command to the buffer */
if (flags & TODO_LIST_ABBREVIATE_CMDS)
- strbuf_addch(&buf, command_to_char(item->command));
+ strbuf_addch(buf, command_to_char(item->command));
else
- strbuf_addstr(&buf, command_to_string(item->command));
+ strbuf_addstr(buf, command_to_string(item->command));
/* add commit id */
if (item->commit) {
if (item->command == TODO_MERGE) {
if (item->flags & TODO_EDIT_MERGE_MSG)
- strbuf_addstr(&buf, " -c");
+ strbuf_addstr(buf, " -c");
else
- strbuf_addstr(&buf, " -C");
+ strbuf_addstr(buf, " -C");
}
- strbuf_addf(&buf, " %s", oid);
+ strbuf_addf(buf, " %s", oid);
}
/* add all the rest */
if (!item->arg_len)
- strbuf_addch(&buf, '\n');
+ strbuf_addch(buf, '\n');
else
- strbuf_addf(&buf, " %.*s\n", item->arg_len, item->arg);
+ strbuf_addf(buf, " %.*s\n", item->arg_len,
+ todo_item_get_arg(todo_list, item));
}
-
- i = write_message(buf.buf, buf.len, todo_file, 0);
- todo_list_release(&todo_list);
- return i;
}
-enum missing_commit_check_level get_missing_commit_check_level(void)
+int todo_list_write_to_file(struct repository *r, struct todo_list *todo_list,
+ const char *file, const char *shortrevisions,
+ const char *shortonto, int num, unsigned flags)
{
- const char *value;
+ int res;
+ struct strbuf buf = STRBUF_INIT;
- if (git_config_get_value("rebase.missingcommitscheck", &value) ||
- !strcasecmp("ignore", value))
- return MISSING_COMMIT_CHECK_IGNORE;
- if (!strcasecmp("warn", value))
- return MISSING_COMMIT_CHECK_WARN;
- if (!strcasecmp("error", value))
- return MISSING_COMMIT_CHECK_ERROR;
- warning(_("unrecognized setting %s for option "
- "rebase.missingCommitsCheck. Ignoring."), value);
- return MISSING_COMMIT_CHECK_IGNORE;
-}
+ todo_list_to_strbuf(r, todo_list, &buf, num, flags);
+ if (flags & TODO_LIST_APPEND_TODO_HELP)
+ append_todo_help(flags & TODO_LIST_KEEP_EMPTY, count_commands(todo_list),
+ shortrevisions, shortonto, &buf);
-define_commit_slab(commit_seen, unsigned char);
-/*
- * Check if the user dropped some commits by mistake
- * Behaviour determined by rebase.missingCommitsCheck.
- * Check if there is an unrecognized command or a
- * bad SHA-1 in a command.
- */
-int check_todo_list(struct repository *r)
-{
- enum missing_commit_check_level check_level = get_missing_commit_check_level();
- struct strbuf todo_file = STRBUF_INIT;
- struct todo_list todo_list = TODO_LIST_INIT;
- struct strbuf missing = STRBUF_INIT;
- int advise_to_edit_todo = 0, res = 0, i;
- struct commit_seen commit_seen;
+ res = write_message(buf.buf, buf.len, file, 0);
+ strbuf_release(&buf);
- init_commit_seen(&commit_seen);
+ return res;
+}
- strbuf_addstr(&todo_file, rebase_path_todo());
- if (strbuf_read_file_or_whine(&todo_list.buf, todo_file.buf) < 0) {
- res = -1;
- goto leave_check;
- }
- advise_to_edit_todo = res =
- parse_insn_buffer(r, todo_list.buf.buf, &todo_list);
+static const char edit_todo_list_advice[] =
+N_("You can fix this with 'git rebase --edit-todo' "
+"and then run 'git rebase --continue'.\n"
+"Or you can abort the rebase with 'git rebase"
+" --abort'.\n");
- if (res || check_level == MISSING_COMMIT_CHECK_IGNORE)
- goto leave_check;
+int check_todo_list_from_file(struct repository *r)
+{
+ struct todo_list old_todo = TODO_LIST_INIT, new_todo = TODO_LIST_INIT;
+ int res = 0;
- /* Mark the commits in git-rebase-todo as seen */
- for (i = 0; i < todo_list.nr; i++) {
- struct commit *commit = todo_list.items[i].commit;
- if (commit)
- *commit_seen_at(&commit_seen, commit) = 1;
+ if (strbuf_read_file_or_whine(&new_todo.buf, rebase_path_todo()) < 0) {
+ res = -1;
+ goto out;
}
- todo_list_release(&todo_list);
- strbuf_addstr(&todo_file, ".backup");
- if (strbuf_read_file_or_whine(&todo_list.buf, todo_file.buf) < 0) {
+ if (strbuf_read_file_or_whine(&old_todo.buf, rebase_path_todo_backup()) < 0) {
res = -1;
- goto leave_check;
- }
- strbuf_release(&todo_file);
- res = !!parse_insn_buffer(r, todo_list.buf.buf, &todo_list);
-
- /* Find commits in git-rebase-todo.backup yet unseen */
- for (i = todo_list.nr - 1; i >= 0; i--) {
- struct todo_item *item = todo_list.items + i;
- struct commit *commit = item->commit;
- if (commit && !*commit_seen_at(&commit_seen, commit)) {
- strbuf_addf(&missing, " - %s %.*s\n",
- short_commit_name(commit),
- item->arg_len, item->arg);
- *commit_seen_at(&commit_seen, commit) = 1;
- }
+ goto out;
}
- /* Warn about missing commits */
- if (!missing.len)
- goto leave_check;
-
- if (check_level == MISSING_COMMIT_CHECK_ERROR)
- advise_to_edit_todo = res = 1;
-
- fprintf(stderr,
- _("Warning: some commits may have been dropped accidentally.\n"
- "Dropped commits (newer to older):\n"));
-
- /* Make the list user-friendly and display */
- fputs(missing.buf, stderr);
- strbuf_release(&missing);
-
- fprintf(stderr, _("To avoid this message, use \"drop\" to "
- "explicitly remove a commit.\n\n"
- "Use 'git config rebase.missingCommitsCheck' to change "
- "the level of warnings.\n"
- "The possible behaviours are: ignore, warn, error.\n\n"));
-
-leave_check:
- clear_commit_seen(&commit_seen);
- strbuf_release(&todo_file);
- todo_list_release(&todo_list);
-
- if (advise_to_edit_todo)
- fprintf(stderr,
- _("You can fix this with 'git rebase --edit-todo' "
- "and then run 'git rebase --continue'.\n"
- "Or you can abort the rebase with 'git rebase"
- " --abort'.\n"));
+ res = todo_list_parse_insn_buffer(r, old_todo.buf.buf, &old_todo);
+ if (!res)
+ res = todo_list_parse_insn_buffer(r, new_todo.buf.buf, &new_todo);
+ if (!res)
+ res = todo_list_check(&old_todo, &new_todo);
+ if (res)
+ fprintf(stderr, _(edit_todo_list_advice));
+out:
+ todo_list_release(&old_todo);
+ todo_list_release(&new_todo);
return res;
}
-static int rewrite_file(const char *path, const char *buf, size_t len)
-{
- int rc = 0;
- int fd = open(path, O_WRONLY | O_TRUNC);
- if (fd < 0)
- return error_errno(_("could not open '%s' for writing"), path);
- if (write_in_full(fd, buf, len) < 0)
- rc = error_errno(_("could not write to '%s'"), path);
- if (close(fd) && !rc)
- rc = error_errno(_("could not close '%s'"), path);
- return rc;
-}
-
/* skip picking commits whose parents are unchanged */
-static int skip_unnecessary_picks(struct repository *r, struct object_id *output_oid)
+static int skip_unnecessary_picks(struct repository *r,
+ struct todo_list *todo_list,
+ struct object_id *base_oid)
{
- const char *todo_file = rebase_path_todo();
- struct strbuf buf = STRBUF_INIT;
- struct todo_list todo_list = TODO_LIST_INIT;
struct object_id *parent_oid;
- int fd, i;
-
- if (!read_oneliner(&buf, rebase_path_onto(), 0))
- return error(_("could not read 'onto'"));
- if (get_oid(buf.buf, output_oid)) {
- strbuf_release(&buf);
- return error(_("need a HEAD to fixup"));
- }
- strbuf_release(&buf);
-
- if (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)
- return -1;
- if (parse_insn_buffer(r, todo_list.buf.buf, &todo_list) < 0) {
- todo_list_release(&todo_list);
- return -1;
- }
+ int i;
- for (i = 0; i < todo_list.nr; i++) {
- struct todo_item *item = todo_list.items + i;
+ for (i = 0; i < todo_list->nr; i++) {
+ struct todo_item *item = todo_list->items + i;
if (item->command >= TODO_NOOP)
continue;
if (item->command != TODO_PICK)
break;
if (parse_commit(item->commit)) {
- todo_list_release(&todo_list);
return error(_("could not parse commit '%s'"),
oid_to_hex(&item->commit->object.oid));
}
if (item->commit->parents->next)
break; /* merge commit */
parent_oid = &item->commit->parents->item->object.oid;
- if (!oideq(parent_oid, output_oid))
+ if (!oideq(parent_oid, base_oid))
break;
- oidcpy(output_oid, &item->commit->object.oid);
+ oidcpy(base_oid, &item->commit->object.oid);
}
if (i > 0) {
- int offset = get_item_line_offset(&todo_list, i);
const char *done_path = rebase_path_done();
- fd = open(done_path, O_CREAT | O_WRONLY | O_APPEND, 0666);
- if (fd < 0) {
- error_errno(_("could not open '%s' for writing"),
- done_path);
- todo_list_release(&todo_list);
- return -1;
- }
- if (write_in_full(fd, todo_list.buf.buf, offset) < 0) {
+ if (todo_list_write_to_file(r, todo_list, done_path, NULL, NULL, i, 0)) {
error_errno(_("could not write to '%s'"), done_path);
- todo_list_release(&todo_list);
- close(fd);
return -1;
}
- close(fd);
- if (rewrite_file(rebase_path_todo(), todo_list.buf.buf + offset,
- todo_list.buf.len - offset) < 0) {
- todo_list_release(&todo_list);
- return -1;
- }
+ MOVE_ARRAY(todo_list->items, todo_list->items + i, todo_list->nr - i);
+ todo_list->nr -= i;
+ todo_list->current = 0;
- todo_list.current = i;
- if (is_fixup(peek_command(&todo_list, 0)))
- record_in_rewritten(output_oid, peek_command(&todo_list, 0));
+ if (is_fixup(peek_command(todo_list, 0)))
+ record_in_rewritten(base_oid, peek_command(todo_list, 0));
}
- todo_list_release(&todo_list);
-
return 0;
}
int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,
const char *shortrevisions, const char *onto_name,
- const char *onto, const char *orig_head, const char *cmd,
- unsigned autosquash)
+ const char *onto, const char *orig_head, struct string_list *commands,
+ unsigned autosquash, struct todo_list *todo_list)
{
const char *shortonto, *todo_file = rebase_path_todo();
- struct todo_list todo_list = TODO_LIST_INIT;
- struct strbuf *buf = &(todo_list.buf);
+ struct todo_list new_todo = TODO_LIST_INIT;
+ struct strbuf *buf = &todo_list->buf;
struct object_id oid;
- struct stat st;
+ int res;
get_oid(onto, &oid);
shortonto = find_unique_abbrev(&oid, DEFAULT_ABBREV);
- if (!lstat(todo_file, &st) && st.st_size == 0 &&
- write_message("noop\n", 5, todo_file, 0))
- return -1;
+ if (buf->len == 0) {
+ struct todo_item *item = append_new_todo(todo_list);
+ item->command = TODO_NOOP;
+ item->commit = NULL;
+ item->arg_len = item->arg_offset = item->flags = item->offset_in_buf = 0;
+ }
- if (autosquash && rearrange_squash(r))
+ if (autosquash && todo_list_rearrange_squash(todo_list))
return -1;
- if (cmd && *cmd)
- sequencer_add_exec_commands(r, cmd);
-
- if (strbuf_read_file(buf, todo_file, 0) < 0)
- return error_errno(_("could not read '%s'."), todo_file);
-
- if (parse_insn_buffer(r, buf->buf, &todo_list)) {
- todo_list_release(&todo_list);
- return error(_("unusable todo list: '%s'"), todo_file);
- }
+ if (commands->nr)
+ todo_list_add_exec_commands(todo_list, commands);
- if (count_commands(&todo_list) == 0) {
+ if (count_commands(todo_list) == 0) {
apply_autostash(opts);
sequencer_remove_state(opts);
- todo_list_release(&todo_list);
return error(_("nothing to do"));
}
- strbuf_addch(buf, '\n');
- strbuf_commented_addf(buf, Q_("Rebase %s onto %s (%d command)",
- "Rebase %s onto %s (%d commands)",
- count_commands(&todo_list)),
- shortrevisions, shortonto, count_commands(&todo_list));
- append_todo_help(0, flags & TODO_LIST_KEEP_EMPTY, buf);
-
- if (write_message(buf->buf, buf->len, todo_file, 0)) {
- todo_list_release(&todo_list);
+ res = edit_todo_list(r, todo_list, &new_todo, shortrevisions,
+ shortonto, flags);
+ if (res == -1)
return -1;
- }
-
- if (copy_file(rebase_path_todo_backup(), todo_file, 0666))
- return error(_("could not copy '%s' to '%s'."), todo_file,
- rebase_path_todo_backup());
-
- if (transform_todos(r, flags | TODO_LIST_SHORTEN_IDS))
- return error(_("could not transform the todo list"));
-
- strbuf_reset(buf);
-
- if (launch_sequence_editor(todo_file, buf, NULL)) {
+ else if (res == -2) {
apply_autostash(opts);
sequencer_remove_state(opts);
- todo_list_release(&todo_list);
return -1;
- }
-
- strbuf_stripspace(buf, 1);
- if (buf->len == 0) {
+ } else if (res == -3) {
apply_autostash(opts);
sequencer_remove_state(opts);
- todo_list_release(&todo_list);
+ todo_list_release(&new_todo);
return error(_("nothing to do"));
}
- todo_list_release(&todo_list);
-
- if (check_todo_list(r)) {
+ if (todo_list_parse_insn_buffer(r, new_todo.buf.buf, &new_todo) ||
+ todo_list_check(todo_list, &new_todo)) {
+ fprintf(stderr, _(edit_todo_list_advice));
checkout_onto(opts, onto_name, onto, orig_head);
+ todo_list_release(&new_todo);
+
return -1;
}
- if (transform_todos(r, flags & ~(TODO_LIST_SHORTEN_IDS)))
- return error(_("could not transform the todo list"));
-
- if (opts->allow_ff && skip_unnecessary_picks(r, &oid))
+ if (opts->allow_ff && skip_unnecessary_picks(r, &new_todo, &oid)) {
+ todo_list_release(&new_todo);
return error(_("could not skip unnecessary pick commands"));
+ }
+
+ if (todo_list_write_to_file(r, &new_todo, todo_file, NULL, NULL, -1,
+ flags & ~(TODO_LIST_SHORTEN_IDS))) {
+ todo_list_release(&new_todo);
+ return error_errno(_("could not write '%s'"), todo_file);
+ }
+
+ todo_list_release(&new_todo);
if (checkout_onto(opts, onto_name, oid_to_hex(&oid), orig_head))
return -1;
* message will have to be retrieved from the commit (as the oneline in the
* script cannot be trusted) in order to normalize the autosquash arrangement.
*/
-int rearrange_squash(struct repository *r)
+int todo_list_rearrange_squash(struct todo_list *todo_list)
{
- const char *todo_file = rebase_path_todo();
- struct todo_list todo_list = TODO_LIST_INIT;
struct hashmap subject2item;
- int res = 0, rearranged = 0, *next, *tail, i;
+ int rearranged = 0, *next, *tail, i, nr = 0, alloc = 0;
char **subjects;
struct commit_todo_item commit_todo;
-
- if (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)
- return -1;
- if (parse_insn_buffer(r, todo_list.buf.buf, &todo_list) < 0) {
- todo_list_release(&todo_list);
- return -1;
- }
+ struct todo_item *items = NULL;
init_commit_todo_item(&commit_todo);
/*
* be moved to appear after the i'th.
*/
hashmap_init(&subject2item, (hashmap_cmp_fn) subject2item_cmp,
- NULL, todo_list.nr);
- ALLOC_ARRAY(next, todo_list.nr);
- ALLOC_ARRAY(tail, todo_list.nr);
- ALLOC_ARRAY(subjects, todo_list.nr);
- for (i = 0; i < todo_list.nr; i++) {
+ NULL, todo_list->nr);
+ ALLOC_ARRAY(next, todo_list->nr);
+ ALLOC_ARRAY(tail, todo_list->nr);
+ ALLOC_ARRAY(subjects, todo_list->nr);
+ for (i = 0; i < todo_list->nr; i++) {
struct strbuf buf = STRBUF_INIT;
- struct todo_item *item = todo_list.items + i;
+ struct todo_item *item = todo_list->items + i;
const char *commit_buffer, *subject, *p;
size_t subject_len;
int i2 = -1;
}
if (is_fixup(item->command)) {
- todo_list_release(&todo_list);
clear_commit_todo_item(&commit_todo);
return error(_("the script was already rearranged."));
}
*commit_todo_item_at(&commit_todo, commit2))
/* found by commit name */
i2 = *commit_todo_item_at(&commit_todo, commit2)
- - todo_list.items;
+ - todo_list->items;
else {
/* copy can be a prefix of the commit subject */
for (i2 = 0; i2 < i; i2++)
}
if (i2 >= 0) {
rearranged = 1;
- todo_list.items[i].command =
+ todo_list->items[i].command =
starts_with(subject, "fixup!") ?
TODO_FIXUP : TODO_SQUASH;
if (next[i2] < 0)
}
if (rearranged) {
- struct strbuf buf = STRBUF_INIT;
-
- for (i = 0; i < todo_list.nr; i++) {
- enum todo_command command = todo_list.items[i].command;
+ for (i = 0; i < todo_list->nr; i++) {
+ enum todo_command command = todo_list->items[i].command;
int cur = i;
/*
continue;
while (cur >= 0) {
- const char *bol =
- get_item_line(&todo_list, cur);
- const char *eol =
- get_item_line(&todo_list, cur + 1);
-
- /* replace 'pick', by 'fixup' or 'squash' */
- command = todo_list.items[cur].command;
- if (is_fixup(command)) {
- strbuf_addstr(&buf,
- todo_command_info[command].str);
- bol += strcspn(bol, " \t");
- }
-
- strbuf_add(&buf, bol, eol - bol);
-
+ ALLOC_GROW(items, nr + 1, alloc);
+ items[nr++] = todo_list->items[cur];
cur = next[cur];
}
}
- res = rewrite_file(todo_file, buf.buf, buf.len);
- strbuf_release(&buf);
+ FREE_AND_NULL(todo_list->items);
+ todo_list->items = items;
+ todo_list->nr = nr;
+ todo_list->alloc = alloc;
}
free(next);
free(tail);
- for (i = 0; i < todo_list.nr; i++)
+ for (i = 0; i < todo_list->nr; i++)
free(subjects[i]);
free(subjects);
hashmap_free(&subject2item, 1);
- todo_list_release(&todo_list);
clear_commit_todo_item(&commit_todo);
- return res;
+
+ return 0;
}
const char *git_path_commit_editmsg(void);
const char *git_path_seq_dir(void);
const char *rebase_path_todo(void);
+const char *rebase_path_todo_backup(void);
#define APPEND_SIGNOFF_DEDUP (1u << 0)
};
#define REPLAY_OPTS_INIT { .action = -1, .current_fixups = STRBUF_INIT }
-enum missing_commit_check_level {
- MISSING_COMMIT_CHECK_IGNORE = 0,
- MISSING_COMMIT_CHECK_WARN,
- MISSING_COMMIT_CHECK_ERROR
+/*
+ * Note that ordering matters in this enum. Not only must it match the mapping
+ * of todo_command_info (in sequencer.c), it is also divided into several
+ * sections that matter. When adding new commands, make sure you add it in the
+ * right section.
+ */
+enum todo_command {
+ /* commands that handle commits */
+ TODO_PICK = 0,
+ TODO_REVERT,
+ TODO_EDIT,
+ TODO_REWORD,
+ TODO_FIXUP,
+ TODO_SQUASH,
+ /* commands that do something else than handling a single commit */
+ TODO_EXEC,
+ TODO_BREAK,
+ TODO_LABEL,
+ TODO_RESET,
+ TODO_MERGE,
+ /* commands that do nothing but are counted for reporting progress */
+ TODO_NOOP,
+ TODO_DROP,
+ /* comments (not counted for reporting progress) */
+ TODO_COMMENT
};
-int write_message(const void *buf, size_t len, const char *filename,
- int append_eol);
+struct todo_item {
+ enum todo_command command;
+ struct commit *commit;
+ unsigned int flags;
+ int arg_len;
+ /* The offset of the command and its argument in the strbuf */
+ size_t offset_in_buf, arg_offset;
+};
+
+struct todo_list {
+ struct strbuf buf;
+ struct todo_item *items;
+ int nr, alloc, current;
+ int done_nr, total_nr;
+ struct stat_data stat;
+};
+
+#define TODO_LIST_INIT { STRBUF_INIT }
+
+int todo_list_parse_insn_buffer(struct repository *r, char *buf,
+ struct todo_list *todo_list);
+int todo_list_write_to_file(struct repository *r, struct todo_list *todo_list,
+ const char *file, const char *shortrevisions,
+ const char *shortonto, int num, unsigned flags);
+void todo_list_release(struct todo_list *todo_list);
+const char *todo_item_get_arg(struct todo_list *todo_list,
+ struct todo_item *item);
/* Call this to setup defaults before parsing command line options */
void sequencer_init_config(struct replay_opts *opts);
* commits should be rebased onto the new base, this flag needs to be passed.
*/
#define TODO_LIST_REBASE_COUSINS (1U << 4)
-int sequencer_make_script(struct repository *repo, FILE *out,
- int argc, const char **argv,
- unsigned flags);
-
-int sequencer_add_exec_commands(struct repository *r, const char *command);
-int transform_todos(struct repository *r, unsigned flags);
-enum missing_commit_check_level get_missing_commit_check_level(void);
-int check_todo_list(struct repository *r);
+#define TODO_LIST_APPEND_TODO_HELP (1U << 5)
+
+int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
+ const char **argv, unsigned flags);
+
+void todo_list_add_exec_commands(struct todo_list *todo_list,
+ struct string_list *commands);
+int check_todo_list_from_file(struct repository *r);
int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,
const char *shortrevisions, const char *onto_name,
- const char *onto, const char *orig_head, const char *cmd,
- unsigned autosquash);
-int rearrange_squash(struct repository *r);
+ const char *onto, const char *orig_head, struct string_list *commands,
+ unsigned autosquash, struct todo_list *todo_list);
+int todo_list_rearrange_squash(struct todo_list *todo_list);
/*
* Append a signoff to the commit message in "msgbuf". The ignore_footer
} else if (strcmp(var, "core.worktree") == 0) {
if (!value)
return config_error_nonbool(var);
+ free(data->work_tree);
data->work_tree = xstrdup(value);
}
return 0;
}
repository_format_precious_objects = candidate->precious_objects;
- repository_format_partial_clone = candidate->partial_clone;
+ repository_format_partial_clone = xstrdup_or_null(candidate->partial_clone);
repository_format_worktree_config = candidate->worktree_config;
string_list_clear(&candidate->unknown_extensions, 0);
}
if (candidate->work_tree) {
free(git_work_tree_cfg);
- git_work_tree_cfg = candidate->work_tree;
+ git_work_tree_cfg = xstrdup(candidate->work_tree);
inside_work_tree = -1;
}
- } else {
- free(candidate->work_tree);
}
return 0;
}
+static void init_repository_format(struct repository_format *format)
+{
+ const struct repository_format fresh = REPOSITORY_FORMAT_INIT;
+
+ memcpy(format, &fresh, sizeof(fresh));
+}
+
int read_repository_format(struct repository_format *format, const char *path)
{
- memset(format, 0, sizeof(*format));
- format->version = -1;
- format->is_bare = -1;
- format->hash_algo = GIT_HASH_SHA1;
- string_list_init(&format->unknown_extensions, 1);
+ clear_repository_format(format);
git_config_from_file(check_repo_format, path, format);
+ if (format->version == -1)
+ clear_repository_format(format);
return format->version;
}
+void clear_repository_format(struct repository_format *format)
+{
+ string_list_clear(&format->unknown_extensions, 0);
+ free(format->work_tree);
+ free(format->partial_clone);
+ init_repository_format(format);
+}
+
int verify_repository_format(const struct repository_format *format,
struct strbuf *err)
{
struct strbuf dir = STRBUF_INIT, err = STRBUF_INIT;
size_t gitdir_offset = gitdir->len, cwd_len;
size_t commondir_offset = commondir->len;
- struct repository_format candidate;
+ struct repository_format candidate = REPOSITORY_FORMAT_INIT;
if (strbuf_getcwd(&dir))
return -1;
strbuf_release(&err);
strbuf_setlen(commondir, commondir_offset);
strbuf_setlen(gitdir, gitdir_offset);
+ clear_repository_format(&candidate);
return -1;
}
+ clear_repository_format(&candidate);
return 0;
}
static struct strbuf cwd = STRBUF_INIT;
struct strbuf dir = STRBUF_INIT, gitdir = STRBUF_INIT;
const char *prefix = NULL;
- struct repository_format repo_fmt;
+ struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT;
/*
* We may have read an incomplete configuration before
strbuf_release(&dir);
strbuf_release(&gitdir);
+ clear_repository_format(&repo_fmt);
return prefix;
}
void check_repository_format(void)
{
- struct repository_format repo_fmt;
+ struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT;
check_repository_format_gently(get_git_dir(), &repo_fmt, NULL);
startup_info->have_repository = 1;
+ clear_repository_format(&repo_fmt);
}
/*
find_short_packed_object(&ds);
status = finish_object_disambiguation(&ds, oid);
+ /*
+ * If we didn't find it, do the usual reprepare() slow-path,
+ * since the object may have recently been added to the repository
+ * or migrated from loose to packed.
+ */
+ if (status == MISSING_OBJECT) {
+ reprepare_packed_git(the_repository);
+ find_short_object_filename(&ds);
+ find_short_packed_object(&ds);
+ status = finish_object_disambiguation(&ds, oid);
+ }
+
if (!quietly && (status == SHORT_NAME_AMBIGUOUS)) {
struct oid_array collect = OID_ARRAY_INIT;
return get_oid_with_context(the_repository, name, 0, oid, &unused);
}
+/*
+ * This returns a non-zero value if the string (built using printf
+ * format and the given arguments) is not a valid object.
+ */
+int get_oidf(struct object_id *oid, const char *fmt, ...)
+{
+ va_list ap;
+ int ret;
+ struct strbuf sb = STRBUF_INIT;
+
+ va_start(ap, fmt);
+ strbuf_vaddf(&sb, fmt, ap);
+ va_end(ap);
+
+ ret = get_oid(sb.buf, oid);
+ strbuf_release(&sb);
+
+ return ret;
+}
/*
* Many callers know that the user meant to name a commit-ish by
-Subproject commit 232357eb2ea0397388254a4b188333a227bf5b10
+Subproject commit 16033998da4b273aebd92c84b1e1b12e4aaf7009
#endif
/*ENDIANNESS SELECTION*/
+#ifndef SHA1DC_FORCE_ALIGNED_ACCESS
#if defined(SHA1DC_FORCE_UNALIGNED_ACCESS) || defined(SHA1DC_ON_INTEL_LIKE_PROCESSOR)
#define SHA1DC_ALLOW_UNALIGNED_ACCESS
-#endif /*UNALIGNMENT DETECTION*/
-
+#endif /*UNALIGNED ACCESS DETECTION*/
+#endif /*FORCE ALIGNED ACCESS*/
#define rotate_right(x,n) (((x)>>(n))|((x)<<(32-(n))))
#define rotate_left(x,n) (((x)<<(n))|((x)>>(32-(n))))
strbuf_splice(sb, pos, 0, data, len);
}
+void strbuf_vinsertf(struct strbuf *sb, size_t pos, const char *fmt, va_list ap)
+{
+ int len, len2;
+ char save;
+ va_list cp;
+
+ if (pos > sb->len)
+ die("`pos' is too far after the end of the buffer");
+ va_copy(cp, ap);
+ len = vsnprintf(sb->buf + sb->len, 0, fmt, cp);
+ va_end(cp);
+ if (len < 0)
+ BUG("your vsnprintf is broken (returned %d)", len);
+ if (!len)
+ return; /* nothing to do */
+ if (unsigned_add_overflows(sb->len, len))
+ die("you want to use way too much memory");
+ strbuf_grow(sb, len);
+ memmove(sb->buf + pos + len, sb->buf + pos, sb->len - pos);
+ /* vsnprintf() will append a NUL, overwriting one of our characters */
+ save = sb->buf[pos + len];
+ len2 = vsnprintf(sb->buf + pos, len + 1, fmt, ap);
+ sb->buf[pos + len] = save;
+ if (len2 != len)
+ BUG("your vsnprintf is broken (returns inconsistent lengths)");
+ strbuf_setlen(sb, sb->len + len);
+}
+
+void strbuf_insertf(struct strbuf *sb, size_t pos, const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ strbuf_vinsertf(sb, pos, fmt, ap);
+ va_end(ap);
+}
+
void strbuf_remove(struct strbuf *sb, size_t pos, size_t len)
{
strbuf_splice(sb, pos, len, "", 0);
strbuf_setlen(sb, sb->len + sb2->len);
}
+const char *strbuf_join_argv(struct strbuf *buf,
+ int argc, const char **argv, char delim)
+{
+ if (!argc)
+ return buf->buf;
+
+ strbuf_addstr(buf, *argv);
+ while (--argc) {
+ strbuf_addch(buf, delim);
+ strbuf_addstr(buf, *(++argv));
+ }
+
+ return buf->buf;
+}
+
void strbuf_addchars(struct strbuf *sb, int c, size_t n)
{
strbuf_grow(sb, n);
*/
void strbuf_insert(struct strbuf *sb, size_t pos, const void *, size_t);
+/**
+ * Insert data to the given position of the buffer giving a printf format
+ * string. The contents will be shifted, not overwritten.
+ */
+void strbuf_vinsertf(struct strbuf *sb, size_t pos, const char *fmt,
+ va_list ap);
+
+void strbuf_insertf(struct strbuf *sb, size_t pos, const char *fmt, ...);
+
/**
* Remove given amount of data from a given position of the buffer.
*/
*/
void strbuf_addbuf(struct strbuf *sb, const struct strbuf *sb2);
+/**
+ * Join the arguments into a buffer. `delim` is put between every
+ * two arguments.
+ */
+const char *strbuf_join_argv(struct strbuf *buf, int argc,
+ const char **argv, char delim);
+
/**
* This function can be used to expand a format string containing
* placeholders. To that end, it parses the string and calls the specified
struct oid_array *commits;
if (retvalue)
+ /*
+ * NEEDSWORK: This indicates that the overall fetch
+ * failed, even though there may be a subsequent fetch
+ * by commit hash that might work. It may be a good
+ * idea to not indicate failure in this case, and only
+ * indicate failure if the subsequent fetch fails.
+ */
spf->result = 1;
if (!task || !task->sub)
variable to "1" or "0", respectively.
--stress::
---stress=<N>::
Run the test script repeatedly in multiple parallel jobs until
one of them fails. Useful for reproducing rare failures in
flaky tests. The number of parallel jobs is, in order of
- precedence: <N>, or the value of the GIT_TEST_STRESS_LOAD
+ precedence: the value of the GIT_TEST_STRESS_LOAD
environment variable, or twice the number of available
processors (as shown by the 'getconf' utility), or 8.
Implies `--verbose -x --immediate` to get the most information
'.stress-<nr>' suffix, and the trash directory of the failed
test job is renamed to end with a '.stress-failed' suffix.
+--stress-jobs=<N>::
+ Override the number of parallel jobs. Implies `--stress`.
+
--stress-limit=<N>::
When combined with --stress run the test script repeatedly
this many times in each of the parallel jobs or until one of
- them fails, whichever comes first.
+ them fails, whichever comes first. Implies `--stress`.
You can also set the GIT_TEST_INSTALLED environment variable to
the bindir of an existing git installation to test that installation.
GIT_TEST_SPLIT_INDEX=<boolean> forces split-index mode on the whole
test suite. Accept any boolean values that are accepted by git-config.
+GIT_TEST_PROTOCOL_VERSION=<n>, when set, overrides the
+'protocol.version' setting to n if it is less than n.
+
GIT_TEST_FULL_IN_PACK_ARRAY=<boolean> exercises the uncommon
pack-objects code path where there are more than 1024 packs even if
the actual number of packs in repository is below this limit. Accept
GIT_TEST_PRELOAD_INDEX=<boolean> exercises the preload-index code path
by overriding the minimum number of cache entries required per thread.
-GIT_TEST_REBASE_USE_BUILTIN=<boolean>, when false, disables the
-builtin version of git-rebase. See 'rebase.useBuiltin' in
+GIT_TEST_STASH_USE_BUILTIN=<boolean>, when false, disables the
+built-in version of git-stash. See 'stash.useBuiltin' in
git-config(1).
GIT_TEST_INDEX_THREADS=<n> enables exercising the multi-threaded loading
fetch-pack to not request sideband-all (even if the server advertises
sideband-all).
+GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS=<boolean>, when true (which is
+the default when running tests), errors out when an abbreviated option
+is used.
+
Naming Tests
------------
# You should have received a copy of the GNU General Public License
# along with this program. If not, see http://www.gnu.org/licenses/ .
-# do the --tee work early; it otherwise confuses our careful
-# GIT_BUILD_DIR mangling
-case "$GIT_TEST_TEE_STARTED, $* " in
-done,*)
- # do not redirect again
- ;;
-*' --tee '*|*' --va'*)
- mkdir -p test-results
- BASE=test-results/$(basename "$0" .sh)
- (GIT_TEST_TEE_STARTED=done ${SHELL-sh} "$0" "$@" 2>&1;
- echo $? > $BASE.exit) | tee $BASE.out
- test "$(cat $BASE.exit)" = 0
- exit
- ;;
-esac
-
+# These variables must be set before the inclusion of test-lib.sh below,
+# because it will change our working directory.
TEST_DIRECTORY=$(pwd)/..
TEST_OUTPUT_DIRECTORY=$(pwd)
-if test -z "$GIT_TEST_INSTALLED"; then
- perf_results_prefix=
-else
- perf_results_prefix=$(printf "%s" "${GIT_TEST_INSTALLED%/bin-wrappers}" | tr -c "[a-zA-Z0-9]" "[_*]")"."
- # make the tested dir absolute
- GIT_TEST_INSTALLED=$(cd "$GIT_TEST_INSTALLED" && pwd)
-fi
+ABSOLUTE_GIT_TEST_INSTALLED=$(
+ test -n "$GIT_TEST_INSTALLED" && cd "$GIT_TEST_INSTALLED" && pwd)
TEST_NO_CREATE_REPO=t
TEST_NO_MALLOC_CHECK=t
. ../test-lib.sh
+if test -z "$GIT_TEST_INSTALLED"; then
+ perf_results_prefix=
+else
+ perf_results_prefix=$(printf "%s" "${GIT_TEST_INSTALLED%/bin-wrappers}" | tr -c "[a-zA-Z0-9]" "[_*]")"."
+ GIT_TEST_INSTALLED=$ABSOLUTE_GIT_TEST_INSTALLED
+fi
+
# Variables from test-lib that are normally internal to the tests; we
# need to export them for test_perf subshells
export TEST_DIRECTORY TRASH_DIRECTORY GIT_BUILD_DIR GIT_TEST_CMP
)
'
+test_expect_success MINGW 'core.hidedotfiles = false' '
+ git config --global core.hidedotfiles false &&
+ rm -rf newdir &&
+ mkdir newdir &&
+ (
+ sane_unset GIT_DIR GIT_WORK_TREE GIT_CONFIG &&
+ git -C newdir init
+ ) &&
+ ! is_hidden newdir/.git
+'
+
test_expect_success MINGW 'redirect std handles' '
GIT_REDIRECT_STDOUT=output.txt git rev-parse --git-dir &&
test .git = "$(cat output.txt)" &&
EOF
test_expect_success 'unambiguously abbreviated option' '
+ GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS=false \
test-tool parse-options --int 2 --boolean --no-bo >output 2>output.err &&
test_must_be_empty output.err &&
test_cmp expect output
'
test_expect_success 'unambiguously abbreviated option with "="' '
+ GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS=false \
test-tool parse-options --expect="integer: 2" --int=2
'
test_expect_success 'ambiguously abbreviated option' '
- test_expect_code 129 test-tool parse-options --strin 123
+ test_expect_code 129 env GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS=false \
+ test-tool parse-options --strin 123
'
test_expect_success 'non ambiguous option (after two options it abbreviates)' '
+ GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS=false \
test-tool parse-options --expect="string: 123" --st 123
'
EOF
test_expect_success 'negation of OPT_NONEG flags is not ambiguous' '
+ GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS=false \
test-tool parse-options --no-ambig >output 2>output.err &&
test_must_be_empty output.err &&
test_cmp expect output
test-tool parse-options --expect="verbose: 0" -v -v -v --no-verbose
'
+test_expect_success 'GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS works' '
+ GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS=false \
+ test-tool parse-options --ye &&
+ test_must_fail env GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS=true \
+ test-tool parse-options --ye
+'
+
test_done
)
'
+test_expect_success 'internal tree objects are not "missing"' '
+ git init missing-empty &&
+ (
+ cd missing-empty &&
+ empty_tree=$(git hash-object -t tree /dev/null) &&
+ commit=$(echo foo | git commit-tree $empty_tree) &&
+ git rev-list --objects $commit
+ )
+'
+
test_done
)
'
+test_expect_success 'conditional include with /**/' '
+ REPO=foo/bar/repo &&
+ git init $REPO &&
+ cat >>$REPO/.git/config <<-\EOF &&
+ [includeIf "gitdir:**/foo/**/bar/**"]
+ path=bar7
+ EOF
+ echo "[test]seven=7" >$REPO/.git/bar7 &&
+ echo 7 >expect &&
+ git -C $REPO config test.seven >actual &&
+ test_cmp expect actual
+'
+
test_expect_success SYMLINKS 'conditional include, set up symlinked $HOME' '
mkdir real-home &&
ln -s real-home home &&
test_cmp unchanged actual
'
+test_expect_success 'delete fails cleanly if packed-refs.new write fails' '
+ # Setup and expectations are similar to the test above.
+ prefix=refs/failed-packed-refs &&
+ git update-ref $prefix/foo $C &&
+ git pack-refs --all &&
+ git update-ref $prefix/foo $D &&
+ git for-each-ref $prefix >unchanged &&
+ # This should not happen in practice, but it is an easy way to get a
+ # reliable error (we open with create_tempfile(), which uses O_EXCL).
+ : >.git/packed-refs.new &&
+ test_when_finished "rm -f .git/packed-refs.new" &&
+ test_must_fail git update-ref -d $prefix/foo &&
+ git for-each-ref $prefix >actual &&
+ test_cmp unchanged actual
+'
+
test_done
test_cmp expected actual.wt2
'
+test_expect_success 'for-each-ref from main repo' '
+ mkdir fer1 &&
+ git -C fer1 init repo &&
+ test_commit -C fer1/repo initial &&
+ git -C fer1/repo worktree add ../second &&
+ git -C fer1/repo update-ref refs/bisect/main HEAD &&
+ git -C fer1/repo update-ref refs/rewritten/main HEAD &&
+ git -C fer1/repo update-ref refs/worktree/main HEAD &&
+ git -C fer1/repo for-each-ref --format="%(refname)" | grep main >actual &&
+ cat >expected <<-\EOF &&
+ refs/bisect/main
+ refs/rewritten/main
+ refs/worktree/main
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'for-each-ref from linked repo' '
+ mkdir fer2 &&
+ git -C fer2 init repo &&
+ test_commit -C fer2/repo initial &&
+ git -C fer2/repo worktree add ../second &&
+ git -C fer2/second update-ref refs/bisect/second HEAD &&
+ git -C fer2/second update-ref refs/rewritten/second HEAD &&
+ git -C fer2/second update-ref refs/worktree/second HEAD &&
+ git -C fer2/second for-each-ref --format="%(refname)" | grep second >actual &&
+ cat >expected <<-\EOF &&
+ refs/bisect/second
+ refs/heads/second
+ refs/rewritten/second
+ refs/worktree/second
+ EOF
+ test_cmp expected actual
+'
+
test_done
# for each of type, we have one version which is referenced by another object
# (and so while unreachable, not dangling), and another variant which really is
# dangling.
-test_expect_success 'fsck notices dangling objects' '
+test_expect_success 'create dangling-object repository' '
git init dangling &&
(
cd dangling &&
commit=$(git commit-tree $tree) &&
dcommit=$(git commit-tree -p $commit $tree) &&
- cat >expect <<-EOF &&
+ cat >expect <<-EOF
dangling blob $dblob
dangling commit $dcommit
dangling tree $dtree
EOF
+ )
+'
+test_expect_success 'fsck notices dangling objects' '
+ (
+ cd dangling &&
git fsck >actual &&
# the output order is non-deterministic, as it comes from a hash
sort <actual >actual.sorted &&
)
'
+test_expect_success 'fsck --connectivity-only notices dangling objects' '
+ (
+ cd dangling &&
+ git fsck --connectivity-only >actual &&
+ # the output order is non-deterministic, as it comes from a hash
+ sort <actual >actual.sorted &&
+ test_i18ncmp expect actual.sorted
+ )
+'
+
test_expect_success 'fsck $name notices bogus $name' '
test_must_fail git fsck bogus &&
test_must_fail git fsck $ZERO_OID
test_cmp both.txt.conflicted.cleaned both.txt.cleaned
'
+test_expect_success 'force checkout a conflict file creates stage zero entry' '
+ git init co-force &&
+ (
+ cd co-force &&
+ echo a >a &&
+ git add a &&
+ git commit -ama &&
+ A_OBJ=$(git rev-parse :a) &&
+ git branch topic &&
+ echo b >a &&
+ git commit -amb &&
+ B_OBJ=$(git rev-parse :a) &&
+ git checkout topic &&
+ echo c >a &&
+ C_OBJ=$(git hash-object a) &&
+ git checkout -m master &&
+ test_cmp_rev :1:a $A_OBJ &&
+ test_cmp_rev :2:a $B_OBJ &&
+ test_cmp_rev :3:a $C_OBJ &&
+ git checkout -f topic &&
+ test_cmp_rev :0:a $A_OBJ
+ )
+'
+
test_done
git rebase master
'
+test_expect_success 'rebase sets ORIG_HEAD to pre-rebase state' '
+ git checkout -b orig-head topic &&
+ pre="$(git rev-parse --verify HEAD)" &&
+ git rebase master &&
+ test_cmp_rev "$pre" ORIG_HEAD &&
+ ! test_cmp_rev "$pre" HEAD
+'
+
test_expect_success 'rebase, with <onto> and <upstream> specified as :/quuxery' '
test_when_finished "git branch -D torebase" &&
git checkout -b torebase my-topic-branch^ &&
)
'
+test_expect_success 'rebase -c rebase.useBuiltin=false warning' '
+ expected="rebase.useBuiltin support has been removed" &&
+
+ # Only warn when the legacy rebase is requested...
+ test_must_fail git -c rebase.useBuiltin=false rebase 2>err &&
+ test_i18ngrep "$expected" err &&
+ test_must_fail env GIT_TEST_REBASE_USE_BUILTIN=false git rebase 2>err &&
+ test_i18ngrep "$expected" err &&
+
+ # ...not when we would have used the built-in anyway
+ test_must_fail git -c rebase.useBuiltin=true rebase 2>err &&
+ test_must_be_empty err &&
+ test_must_fail env GIT_TEST_REBASE_USE_BUILTIN=true git rebase 2>err &&
+ test_must_be_empty err
+'
+
test_done
test_expect_success 'rebase -x with empty command fails' '
test_when_finished "git rebase --abort ||:" &&
- test_must_fail env GIT_TEST_REBASE_USE_BUILTIN=true \
- git rebase -x "" @ 2>actual &&
+ test_must_fail env git rebase -x "" @ 2>actual &&
test_write_lines "error: empty exec command" >expected &&
test_i18ncmp expected actual &&
- test_must_fail env GIT_TEST_REBASE_USE_BUILTIN=true \
- git rebase -x " " @ 2>actual &&
+ test_must_fail env git rebase -x " " @ 2>actual &&
test_i18ncmp expected actual
'
'
test_expect_success 'rebase -x with newline in command fails' '
test_when_finished "git rebase --abort ||:" &&
- test_must_fail env GIT_TEST_REBASE_USE_BUILTIN=true \
- git rebase -x "a${LF}b" @ 2>actual &&
+ test_must_fail env git rebase -x "a${LF}b" @ 2>actual &&
test_write_lines "error: exec commands cannot contain newlines" \
>expected &&
test_i18ncmp expected actual
(
set_cat_todo_editor &&
test_must_fail git -c rebase.instructionFormat= \
- rebase --autosquash --force -i HEAD^ >actual &&
+ rebase --autosquash --force-rebase -i HEAD^ >actual &&
git log -1 --format="pick %h %s" >expect &&
test_cmp expect actual
)
test -e F
'
+test_expect_success SHA1 'loose object cache vs re-reading todo list' '
+ GIT_REBASE_TODO=.git/rebase-merge/git-rebase-todo &&
+ export GIT_REBASE_TODO &&
+ write_script append-todo.sh <<-\EOS &&
+ # For values 5 and 6, this yields SHA-1s with the same first two digits
+ echo "pick $(git rev-parse --short \
+ $(printf "%s\\n" \
+ "tree $EMPTY_TREE" \
+ "author A U Thor <author@example.org> $1 +0000" \
+ "committer A U Thor <author@example.org> $1 +0000" \
+ "" \
+ "$1" |
+ git hash-object -t commit -w --stdin))" >>$GIT_REBASE_TODO
+
+ shift
+ test -z "$*" ||
+ echo "exec $0 $*" >>$GIT_REBASE_TODO
+ EOS
+
+ git rebase HEAD -x "./append-todo.sh 5 6"
+'
+
test_done
EOF
test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
test_tick &&
- git rebase -i --force --root -r &&
+ git rebase -i --force-rebase --root -r &&
test "Parsnip" = "$(git show -s --format=%an HEAD^)" &&
test $(git rev-parse second-root^0) != $(git rev-parse HEAD^) &&
test $(git rev-parse second-root:second-root.t) = \
test_cmp_rev HEAD $before &&
test_tick &&
- git rebase -i --force -r HEAD^^ &&
+ git rebase -i --force-rebase -r HEAD^^ &&
test "Hank" = "$(git show -s --format=%an HEAD)" &&
test "$before" != $(git rev-parse HEAD) &&
test_cmp_graph HEAD^^.. <<-\EOF
. ./test-lib.sh
# Setup some files to be removed, some with funny characters
-test_expect_success \
- 'Initialize test directory' \
- "touch -- foo bar baz 'space embedded' -q &&
- git add -- foo bar baz 'space embedded' -q &&
- git commit -m 'add normal files'"
+test_expect_success 'Initialize test directory' '
+ touch -- foo bar baz "space embedded" -q &&
+ git add -- foo bar baz "space embedded" -q &&
+ git commit -m "add normal files"
+'
-if test_have_prereq !FUNNYNAMES; then
+if test_have_prereq !FUNNYNAMES
+then
say 'Your filesystem does not allow tabs in filenames.'
fi
-test_expect_success FUNNYNAMES 'add files with funny names' "
- touch -- 'tab embedded' 'newline
-embedded' &&
- git add -- 'tab embedded' 'newline
-embedded' &&
- git commit -m 'add files with tabs and newlines'
-"
-
-test_expect_success \
- 'Pre-check that foo exists and is in index before git rm foo' \
- '[ -f foo ] && git ls-files --error-unmatch foo'
-
-test_expect_success \
- 'Test that git rm foo succeeds' \
- 'git rm --cached foo'
-
-test_expect_success \
- 'Test that git rm --cached foo succeeds if the index matches the file' \
- 'echo content >foo &&
- git add foo &&
- git rm --cached foo'
-
-test_expect_success \
- 'Test that git rm --cached foo succeeds if the index matches the file' \
- 'echo content >foo &&
- git add foo &&
- git commit -m foo &&
- echo "other content" >foo &&
- git rm --cached foo'
-
-test_expect_success \
- 'Test that git rm --cached foo fails if the index matches neither the file nor HEAD' '
- echo content >foo &&
- git add foo &&
- git commit -m foo --allow-empty &&
- echo "other content" >foo &&
- git add foo &&
- echo "yet another content" >foo &&
- test_must_fail git rm --cached foo
-'
-
-test_expect_success \
- 'Test that git rm --cached -f foo works in case where --cached only did not' \
- 'echo content >foo &&
- git add foo &&
- git commit -m foo --allow-empty &&
- echo "other content" >foo &&
- git add foo &&
- echo "yet another content" >foo &&
- git rm --cached -f foo'
-
-test_expect_success \
- 'Post-check that foo exists but is not in index after git rm foo' \
- '[ -f foo ] && test_must_fail git ls-files --error-unmatch foo'
-
-test_expect_success \
- 'Pre-check that bar exists and is in index before "git rm bar"' \
- '[ -f bar ] && git ls-files --error-unmatch bar'
-
-test_expect_success \
- 'Test that "git rm bar" succeeds' \
- 'git rm bar'
-
-test_expect_success \
- 'Post-check that bar does not exist and is not in index after "git rm -f bar"' \
- '! [ -f bar ] && test_must_fail git ls-files --error-unmatch bar'
-
-test_expect_success \
- 'Test that "git rm -- -q" succeeds (remove a file that looks like an option)' \
- 'git rm -- -q'
-
-test_expect_success FUNNYNAMES \
- "Test that \"git rm -f\" succeeds with embedded space, tab, or newline characters." \
- "git rm -f 'space embedded' 'tab embedded' 'newline
-embedded'"
+test_expect_success FUNNYNAMES 'add files with funny names' '
+ touch -- "tab embedded" "newline${LF}embedded" &&
+ git add -- "tab embedded" "newline${LF}embedded" &&
+ git commit -m "add files with tabs and newlines"
+'
+
+test_expect_success 'Pre-check that foo exists and is in index before git rm foo' '
+ test_path_is_file foo &&
+ git ls-files --error-unmatch foo
+'
+
+test_expect_success 'Test that git rm foo succeeds' '
+ git rm --cached foo
+'
+
+test_expect_success 'Test that git rm --cached foo succeeds if the index matches the file' '
+ echo content >foo &&
+ git add foo &&
+ git rm --cached foo
+'
+
+test_expect_success 'Test that git rm --cached foo succeeds if the index matches the file' '
+ echo content >foo &&
+ git add foo &&
+ git commit -m foo &&
+ echo "other content" >foo &&
+ git rm --cached foo
+'
+
+test_expect_success 'Test that git rm --cached foo fails if the index matches neither the file nor HEAD' '
+ echo content >foo &&
+ git add foo &&
+ git commit -m foo --allow-empty &&
+ echo "other content" >foo &&
+ git add foo &&
+ echo "yet another content" >foo &&
+ test_must_fail git rm --cached foo
+'
+
+test_expect_success 'Test that git rm --cached -f foo works in case where --cached only did not' '
+ echo content >foo &&
+ git add foo &&
+ git commit -m foo --allow-empty &&
+ echo "other content" >foo &&
+ git add foo &&
+ echo "yet another content" >foo &&
+ git rm --cached -f foo
+'
+
+test_expect_success 'Post-check that foo exists but is not in index after git rm foo' '
+ test_path_is_file foo &&
+ test_must_fail git ls-files --error-unmatch foo
+'
+
+test_expect_success 'Pre-check that bar exists and is in index before "git rm bar"' '
+ test_path_is_file bar &&
+ git ls-files --error-unmatch bar
+'
+
+test_expect_success 'Test that "git rm bar" succeeds' '
+ git rm bar
+'
+
+test_expect_success 'Post-check that bar does not exist and is not in index after "git rm -f bar"' '
+ test_path_is_missing bar &&
+ test_must_fail git ls-files --error-unmatch bar
+'
+
+test_expect_success 'Test that "git rm -- -q" succeeds (remove a file that looks like an option)' '
+ git rm -- -q
+'
+
+test_expect_success FUNNYNAMES 'Test that "git rm -f" succeeds with embedded space, tab, or newline characters.' '
+ git rm -f "space embedded" "tab embedded" "newline${LF}embedded"
+'
test_expect_success SANITY 'Test that "git rm -f" fails if its rm fails' '
test_when_finished "chmod 775 ." &&
test_must_fail git rm -f baz
'
-test_expect_success \
- 'When the rm in "git rm -f" fails, it should not remove the file from the index' \
- 'git ls-files --error-unmatch baz'
+test_expect_success 'When the rm in "git rm -f" fails, it should not remove the file from the index' '
+ git ls-files --error-unmatch baz
+'
test_expect_success 'Remove nonexistent file with --ignore-unmatch' '
git rm --ignore-unmatch nonexistent
test_expect_success 'Modify foo -- rm should refuse' '
echo >>foo &&
test_must_fail git rm foo baz &&
- test -f foo &&
- test -f baz &&
+ test_path_is_file foo &&
+ test_path_is_file baz &&
git ls-files --error-unmatch foo baz
'
test_expect_success 'Modified foo -- rm -f should work' '
git rm -f foo baz &&
- test ! -f foo &&
- test ! -f baz &&
+ test_path_is_missing foo &&
+ test_path_is_missing baz &&
test_must_fail git ls-files --error-unmatch foo &&
test_must_fail git ls-files --error-unmatch bar
'
test_expect_success 'foo is different in index from HEAD -- rm should refuse' '
test_must_fail git rm foo baz &&
- test -f foo &&
- test -f baz &&
+ test_path_is_file foo &&
+ test_path_is_file baz &&
git ls-files --error-unmatch foo baz
'
test_expect_success 'but with -f it should work.' '
git rm -f foo baz &&
- test ! -f foo &&
- test ! -f baz &&
+ test_path_is_missing foo &&
+ test_path_is_missing baz &&
test_must_fail git ls-files --error-unmatch foo &&
test_must_fail git ls-files --error-unmatch baz
'
test_expect_success 'Recursive without -r fails' '
test_must_fail git rm frotz &&
- test -d frotz &&
- test -f frotz/nitfol
+ test_path_is_dir frotz &&
+ test_path_is_file frotz/nitfol
'
test_expect_success 'Recursive with -r but dirty' '
echo qfwfq >>frotz/nitfol &&
test_must_fail git rm -r frotz &&
- test -d frotz &&
- test -f frotz/nitfol
+ test_path_is_dir frotz &&
+ test_path_is_file frotz/nitfol
'
test_expect_success 'Recursive with -r -f' '
git rm -f -r frotz &&
- ! test -f frotz/nitfol &&
- ! test -d frotz
+ test_path_is_missing frotz/nitfol &&
+ test_path_is_missing frotz
'
test_expect_success 'Remove nonexistent file returns nonzero exit status' '
test_expect_success 'Call "rm" from outside the work tree' '
mkdir repo &&
- (cd repo &&
- git init &&
- echo something >somefile &&
- git add somefile &&
- git commit -m "add a file" &&
- (cd .. &&
- git --git-dir=repo/.git --work-tree=repo rm somefile) &&
- test_must_fail git ls-files --error-unmatch somefile)
+ (
+ cd repo &&
+ git init &&
+ echo something >somefile &&
+ git add somefile &&
+ git commit -m "add a file" &&
+ (
+ cd .. &&
+ git --git-dir=repo/.git --work-tree=repo rm somefile
+ ) &&
+ test_must_fail git ls-files --error-unmatch somefile
+ )
'
test_expect_success 'refresh index before checking if it is up-to-date' '
-
git reset --hard &&
test-tool chmtime -86400 frotz/nitfol &&
git rm frotz/nitfol &&
- test ! -f frotz/nitfol
-
+ test_path_is_missing frotz/nitfol
'
test_expect_success 'choking "git rm" should not let it die with cruft' '
i=0 &&
while test $i -lt 12000
do
- echo "100644 1234567890123456789012345678901234567890 0 some-file-$i"
- i=$(( $i + 1 ))
+ echo "100644 1234567890123456789012345678901234567890 0 some-file-$i"
+ i=$(( $i + 1 ))
done | git update-index --index-info &&
git rm -n "some-file-*" | : &&
test_path_is_missing .git/index.lock
echo content >dir/subdir/subsubdir/file &&
git add dir/subdir/subsubdir/file &&
git rm -f dir/subdir/subsubdir/file &&
- ! test -d dir
+ test_path_is_missing dir
'
cat >expect <<EOF
git add .gitmodules &&
git commit -m "add submodule" &&
git rm submod &&
- test ! -e submod &&
+ test_path_is_missing submod &&
git status -s -uno --ignore-submodules=none >actual &&
test_cmp expect actual &&
test_must_fail git config -f .gitmodules submodule.sub.url &&
git reset --hard &&
git submodule update &&
git rm submod &&
- test ! -d submod &&
+ test_path_is_missing submod &&
git status -s -uno --ignore-submodules=none >actual &&
test_cmp expect actual &&
test_must_fail git config -f .gitmodules submodule.sub.url &&
git reset --hard &&
git submodule update &&
git rm submod/ &&
- test ! -d submod &&
+ test_path_is_missing submod &&
git status -s -uno --ignore-submodules=none >actual &&
test_cmp expect actual
'
git submodule update &&
git -C submod checkout HEAD^ &&
test_must_fail git rm submod &&
- test -d submod &&
- test -f submod/.git &&
+ test_path_is_dir submod &&
+ test_path_is_file submod/.git &&
git status -s -uno --ignore-submodules=none >actual &&
test_cmp expect.modified actual &&
git rm -f submod &&
- test ! -d submod &&
+ test_path_is_missing submod &&
git status -s -uno --ignore-submodules=none >actual &&
test_cmp expect actual &&
test_must_fail git config -f .gitmodules submodule.sub.url &&
git reset --hard &&
git submodule update &&
git rm --cached submod &&
- test -d submod &&
- test -f submod/.git &&
+ test_path_is_dir submod &&
+ test_path_is_file submod/.git &&
git status -s -uno >actual &&
test_cmp expect.cached actual &&
git config -f .gitmodules submodule.sub.url &&
git reset --hard &&
git submodule update &&
git rm -n submod &&
- test -f submod/.git &&
+ test_path_is_file submod/.git &&
git diff-index --exit-code HEAD
'
git rm .gitmodules &&
git rm submod >actual 2>actual.err &&
test_must_be_empty actual.err &&
- ! test -d submod &&
- ! test -f submod/.git &&
+ test_path_is_missing submod &&
+ test_path_is_missing submod/.git &&
git status -s -uno >actual &&
test_cmp expect.both_deleted actual
'
git submodule update &&
git config -f .gitmodules foo.bar true &&
test_must_fail git rm submod >actual 2>actual.err &&
- test -s actual.err &&
- test -d submod &&
- test -f submod/.git &&
+ test_file_not_empty actual.err &&
+ test_path_is_dir submod &&
+ test_path_is_file submod/.git &&
git diff-files --quiet -- submod &&
git add .gitmodules &&
git rm submod >actual 2>actual.err &&
test_must_be_empty actual.err &&
- ! test -d submod &&
- ! test -f submod/.git &&
+ test_path_is_missing submod &&
+ test_path_is_missing submod/.git &&
git status -s -uno >actual &&
test_cmp expect actual
'
echo "warning: Could not find section in .gitmodules where path=submod" >expect.err &&
git rm submod >actual 2>actual.err &&
test_i18ncmp expect.err actual.err &&
- ! test -d submod &&
- ! test -f submod/.git &&
+ test_path_is_missing submod &&
+ test_path_is_missing submod/.git &&
git status -s -uno >actual &&
test_cmp expect actual
'
git submodule update &&
echo X >submod/empty &&
test_must_fail git rm submod &&
- test -d submod &&
- test -f submod/.git &&
+ test_path_is_dir submod &&
+ test_path_is_file submod/.git &&
git status -s -uno --ignore-submodules=none >actual &&
test_cmp expect.modified_inside actual &&
git rm -f submod &&
- test ! -d submod &&
+ test_path_is_missing submod &&
git status -s -uno --ignore-submodules=none >actual &&
test_cmp expect actual
'
git submodule update &&
echo X >submod/untracked &&
test_must_fail git rm submod &&
- test -d submod &&
- test -f submod/.git &&
+ test_path_is_dir submod &&
+ test_path_is_file submod/.git &&
git status -s -uno --ignore-submodules=none >actual &&
test_cmp expect.modified_untracked actual &&
git rm -f submod &&
- test ! -d submod &&
+ test_path_is_missing submod &&
git status -s -uno --ignore-submodules=none >actual &&
test_cmp expect actual
'
git submodule update &&
test_must_fail git merge conflict2 &&
git rm submod &&
- test ! -d submod &&
+ test_path_is_missing submod &&
git status -s -uno --ignore-submodules=none >actual &&
test_cmp expect actual
'
git -C submod checkout HEAD^ &&
test_must_fail git merge conflict2 &&
test_must_fail git rm submod &&
- test -d submod &&
- test -f submod/.git &&
+ test_path_is_dir submod &&
+ test_path_is_file submod/.git &&
git status -s -uno --ignore-submodules=none >actual &&
test_cmp expect.conflict actual &&
git rm -f submod &&
- test ! -d submod &&
+ test_path_is_missing submod &&
git status -s -uno --ignore-submodules=none >actual &&
test_cmp expect actual &&
test_must_fail git config -f .gitmodules submodule.sub.url &&
echo X >submod/empty &&
test_must_fail git merge conflict2 &&
test_must_fail git rm submod &&
- test -d submod &&
- test -f submod/.git &&
+ test_path_is_dir submod &&
+ test_path_is_file submod/.git &&
git status -s -uno --ignore-submodules=none >actual &&
test_cmp expect.conflict actual &&
git rm -f submod &&
- test ! -d submod &&
+ test_path_is_missing submod &&
git status -s -uno --ignore-submodules=none >actual &&
test_cmp expect actual &&
test_must_fail git config -f .gitmodules submodule.sub.url &&
echo X >submod/untracked &&
test_must_fail git merge conflict2 &&
test_must_fail git rm submod &&
- test -d submod &&
- test -f submod/.git &&
+ test_path_is_dir submod &&
+ test_path_is_file submod/.git &&
git status -s -uno --ignore-submodules=none >actual &&
test_cmp expect.conflict actual &&
git rm -f submod &&
- test ! -d submod &&
+ test_path_is_missing submod &&
git status -s -uno --ignore-submodules=none >actual &&
test_cmp expect actual
'
git checkout conflict1 &&
git reset --hard &&
git submodule update &&
- (cd submod &&
+ (
+ cd submod &&
rm .git &&
cp -R ../.git/modules/sub .git &&
GIT_WORK_TREE=. git config --unset core.worktree
) &&
test_must_fail git merge conflict2 &&
test_must_fail git rm submod &&
- test -d submod &&
- test -d submod/.git &&
+ test_path_is_dir submod &&
+ test_path_is_dir submod/.git &&
git status -s -uno --ignore-submodules=none >actual &&
test_cmp expect.conflict actual &&
test_must_fail git rm -f submod &&
- test -d submod &&
- test -d submod/.git &&
+ test_path_is_dir submod &&
+ test_path_is_dir submod/.git &&
git status -s -uno --ignore-submodules=none >actual &&
test_cmp expect.conflict actual &&
git merge --abort &&
git reset --hard &&
test_must_fail git merge conflict2 &&
git rm submod &&
- test ! -d submod &&
+ test_path_is_missing submod &&
git status -s -uno --ignore-submodules=none >actual &&
test_cmp expect actual
'
git checkout -f master &&
git reset --hard &&
git submodule update &&
- (cd submod &&
+ (
+ cd submod &&
rm .git &&
cp -R ../.git/modules/sub .git &&
GIT_WORK_TREE=. git config --unset core.worktree &&
rm -r ../.git/modules/sub
) &&
git rm submod 2>output.err &&
- ! test -d submod &&
- ! test -d submod/.git &&
+ test_path_is_missing submod &&
+ test_path_is_missing submod/.git &&
git status -s -uno --ignore-submodules=none >actual &&
- test -s actual &&
+ test_file_not_empty actual &&
test_i18ngrep Migrating output.err
'
test_expect_success 'setup subsubmodule' '
git reset --hard &&
git submodule update &&
- (cd submod &&
+ (
+ cd submod &&
git update-index --add --cacheinfo 160000 $(git rev-parse HEAD) subsubmod &&
git config -f .gitmodules submodule.sub.url ../. &&
git config -f .gitmodules submodule.sub.path subsubmod &&
test_expect_success 'rm recursively removes work tree of unmodified submodules' '
git rm submod &&
- test ! -d submod &&
+ test_path_is_missing submod &&
git status -s -uno --ignore-submodules=none >actual &&
test_cmp expect actual
'
git submodule update --recursive &&
git -C submod/subsubmod checkout HEAD^ &&
test_must_fail git rm submod &&
- test -d submod &&
- test -f submod/.git &&
+ test_path_is_dir submod &&
+ test_path_is_file submod/.git &&
git status -s -uno --ignore-submodules=none >actual &&
test_cmp expect.modified_inside actual &&
git rm -f submod &&
- test ! -d submod &&
+ test_path_is_missing submod &&
git status -s -uno --ignore-submodules=none >actual &&
test_cmp expect actual
'
git submodule update --recursive &&
echo X >submod/subsubmod/empty &&
test_must_fail git rm submod &&
- test -d submod &&
- test -f submod/.git &&
+ test_path_is_dir submod &&
+ test_path_is_file submod/.git &&
git status -s -uno --ignore-submodules=none >actual &&
test_cmp expect.modified_inside actual &&
git rm -f submod &&
- test ! -d submod &&
+ test_path_is_missing submod &&
git status -s -uno --ignore-submodules=none >actual &&
test_cmp expect actual
'
git submodule update --recursive &&
echo X >submod/subsubmod/untracked &&
test_must_fail git rm submod &&
- test -d submod &&
- test -f submod/.git &&
+ test_path_is_dir submod &&
+ test_path_is_file submod/.git &&
git status -s -uno --ignore-submodules=none >actual &&
test_cmp expect.modified_untracked actual &&
git rm -f submod &&
- test ! -d submod &&
+ test_path_is_missing submod &&
git status -s -uno --ignore-submodules=none >actual &&
test_cmp expect actual
'
test_expect_success "rm absorbs submodule's nested .git directory" '
git reset --hard &&
git submodule update --recursive &&
- (cd submod/subsubmod &&
+ (
+ cd submod/subsubmod &&
rm .git &&
mv ../../.git/modules/sub/modules/sub .git &&
GIT_WORK_TREE=. git config --unset core.worktree
) &&
git rm submod 2>output.err &&
- ! test -d submod &&
- ! test -d submod/subsubmod/.git &&
+ test_path_is_missing submod &&
+ test_path_is_missing submod/subsubmod/.git &&
git status -s -uno --ignore-submodules=none >actual &&
- test -s actual &&
+ test_file_not_empty actual &&
test_i18ngrep Migrating output.err
'
. ./test-lib.sh
test_expect_success 'stash some dirty working directory' '
- echo 1 > file &&
+ echo 1 >file &&
git add file &&
echo unrelated >other-file &&
git add other-file &&
test_tick &&
git commit -m initial &&
- echo 2 > file &&
+ echo 2 >file &&
git add file &&
- echo 3 > file &&
+ echo 3 >file &&
test_tick &&
git stash &&
git diff-files --quiet &&
git diff-index --cached --quiet HEAD
'
-cat > expect << EOF
+cat >expect <<EOF
diff --git a/file b/file
index 0cfbf08..00750ed 100644
--- a/file
test_expect_success 'parents of stash' '
test $(git rev-parse stash^) = $(git rev-parse HEAD) &&
- git diff stash^2..stash > output &&
+ git diff stash^2..stash >output &&
test_cmp expect output
'
test_expect_success 'apply stashed changes (including index)' '
git reset --hard HEAD^ &&
- echo 6 > other-file &&
+ echo 6 >other-file &&
git add other-file &&
test_tick &&
git commit -m other-file &&
test_expect_success 'drop top stash' '
git reset --hard &&
- git stash list > stashlist1 &&
- echo 7 > file &&
+ git stash list >expected &&
+ echo 7 >file &&
git stash &&
git stash drop &&
- git stash list > stashlist2 &&
- test_cmp stashlist1 stashlist2 &&
+ git stash list >actual &&
+ test_cmp expected actual &&
git stash apply &&
test 3 = $(cat file) &&
test 1 = $(git show :file) &&
test_expect_success 'drop middle stash' '
git reset --hard &&
- echo 8 > file &&
+ echo 8 >file &&
git stash &&
- echo 9 > file &&
+ echo 9 >file &&
git stash &&
git stash drop stash@{1} &&
test 2 = $(git stash list | wc -l) &&
test 0 = $(git stash list | wc -l)
'
-cat > expect << EOF
+cat >expect <<EOF
diff --git a/file2 b/file2
new file mode 100644
index 0000000..1fe912c
+bar2
EOF
-cat > expect1 << EOF
+cat >expect1 <<EOF
diff --git a/file b/file
index 257cc56..5716ca5 100644
--- a/file
+bar
EOF
-cat > expect2 << EOF
+cat >expect2 <<EOF
diff --git a/file b/file
index 7601807..5716ca5 100644
--- a/file
EOF
test_expect_success 'stash branch' '
- echo foo > file &&
+ echo foo >file &&
git commit file -m first &&
- echo bar > file &&
- echo bar2 > file2 &&
+ echo bar >file &&
+ echo bar2 >file2 &&
git add file2 &&
git stash &&
- echo baz > file &&
+ echo baz >file &&
git commit file -m second &&
git stash branch stashbranch &&
test refs/heads/stashbranch = $(git symbolic-ref HEAD) &&
test $(git rev-parse HEAD) = $(git rev-parse master^) &&
- git diff --cached > output &&
+ git diff --cached >output &&
test_cmp expect output &&
- git diff > output &&
+ git diff >output &&
test_cmp expect1 output &&
git add file &&
git commit -m alternate\ second &&
- git diff master..stashbranch > output &&
+ git diff master..stashbranch >output &&
test_cmp output expect2 &&
test 0 = $(git stash list | wc -l)
'
test_expect_success 'apply -q is quiet' '
- echo foo > file &&
+ echo foo >file &&
git stash &&
- git stash apply -q > output.out 2>&1 &&
+ git stash apply -q >output.out 2>&1 &&
test_must_be_empty output.out
'
test_expect_success 'save -q is quiet' '
- git stash save --quiet > output.out 2>&1 &&
+ git stash save --quiet >output.out 2>&1 &&
test_must_be_empty output.out
'
test_expect_success 'pop -q is quiet' '
- git stash pop -q > output.out 2>&1 &&
+ git stash pop -q >output.out 2>&1 &&
test_must_be_empty output.out
'
test_expect_success 'pop -q --index works and is quiet' '
- echo foo > file &&
+ echo foo >file &&
git add file &&
git stash save --quiet &&
- git stash pop -q --index > output.out 2>&1 &&
+ git stash pop -q --index >output.out 2>&1 &&
test foo = "$(git show :file)" &&
test_must_be_empty output.out
'
test_expect_success 'drop -q is quiet' '
git stash &&
- git stash drop -q > output.out 2>&1 &&
+ git stash drop -q >output.out 2>&1 &&
test_must_be_empty output.out
'
test_expect_success 'stash -k' '
- echo bar3 > file &&
- echo bar4 > file2 &&
+ echo bar3 >file &&
+ echo bar4 >file2 &&
git add file2 &&
git stash -k &&
test bar,bar4 = $(cat file),$(cat file2)
'
test_expect_success 'stash --no-keep-index' '
- echo bar33 > file &&
- echo bar44 > file2 &&
+ echo bar33 >file &&
+ echo bar44 >file2 &&
git add file2 &&
git stash --no-keep-index &&
test bar,bar2 = $(cat file),$(cat file2)
'
test_expect_success 'stash --invalid-option' '
- echo bar5 > file &&
- echo bar6 > file2 &&
+ echo bar5 >file &&
+ echo bar6 >file2 &&
git add file2 &&
test_must_fail git stash --invalid-option &&
test_must_fail git stash save --invalid-option &&
test new = "$(cat file3)"
'
+test_expect_success 'stash --intent-to-add file' '
+ git reset --hard &&
+ echo new >file4 &&
+ git add --intent-to-add file4 &&
+ test_when_finished "git rm -f file4" &&
+ test_must_fail git stash
+'
+
test_expect_success 'stash rm then recreate' '
git reset --hard &&
git rm file &&
test foo = "$(cat file/file)"
'
+test_expect_success 'giving too many ref arguments does not modify files' '
+ git stash clear &&
+ test_when_finished "git reset --hard HEAD" &&
+ echo foo >file2 &&
+ git stash &&
+ echo bar >file2 &&
+ git stash &&
+ test-tool chmtime =123456789 file2 &&
+ for type in apply pop "branch stash-branch"
+ do
+ test_must_fail git stash $type stash@{0} stash@{1} 2>err &&
+ test_i18ngrep "Too many revisions" err &&
+ test 123456789 = $(test-tool chmtime -g file2) || return 1
+ done
+'
+
+test_expect_success 'drop: too many arguments errors out (does nothing)' '
+ git stash list >expect &&
+ test_must_fail git stash drop stash@{0} stash@{1} 2>err &&
+ test_i18ngrep "Too many revisions" err &&
+ git stash list >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'show: too many arguments errors out (does nothing)' '
+ test_must_fail git stash show stash@{0} stash@{1} 2>err 1>out &&
+ test_i18ngrep "Too many revisions" err &&
+ test_must_be_empty out
+'
+
test_expect_success 'stash create - no changes' '
git stash clear &&
test_when_finished "git reset --hard HEAD" &&
git stash clear &&
test_when_finished "git reset --hard HEAD" &&
git reset --hard &&
- echo foo >> file &&
+ echo foo >>file &&
STASH_ID=$(git stash create) &&
git reset --hard &&
git stash branch stash-branch ${STASH_ID} &&
- test_when_finished "git reset --hard HEAD && git checkout master && git branch -D stash-branch" &&
+ test_when_finished "git reset --hard HEAD && git checkout master &&
+ git branch -D stash-branch" &&
test $(git ls-files --modified | wc -l) -eq 1
'
git stash clear &&
test_when_finished "git reset --hard HEAD" &&
git reset --hard &&
- echo foo >> file &&
+ echo foo >>file &&
git stash &&
test_when_finished "git stash drop" &&
- echo bar >> file &&
+ echo bar >>file &&
STASH_ID=$(git stash create) &&
git reset --hard &&
git stash branch stash-branch ${STASH_ID} &&
- test_when_finished "git reset --hard HEAD && git checkout master && git branch -D stash-branch" &&
+ test_when_finished "git reset --hard HEAD && git checkout master &&
+ git branch -D stash-branch" &&
test $(git ls-files --modified | wc -l) -eq 1
'
+test_expect_success 'stash branch complains with no arguments' '
+ test_must_fail git stash branch 2>err &&
+ test_i18ngrep "No branch name specified" err
+'
+
test_expect_success 'stash show format defaults to --stat' '
git stash clear &&
test_when_finished "git reset --hard HEAD" &&
git reset --hard &&
- echo foo >> file &&
+ echo foo >>file &&
git stash &&
test_when_finished "git stash drop" &&
- echo bar >> file &&
+ echo bar >>file &&
STASH_ID=$(git stash create) &&
git reset --hard &&
cat >expected <<-EOF &&
git stash clear &&
test_when_finished "git reset --hard HEAD" &&
git reset --hard &&
- echo foo >> file &&
+ echo foo >>file &&
git stash &&
test_when_finished "git stash drop" &&
- echo bar >> file &&
+ echo bar >>file &&
STASH_ID=$(git stash create) &&
git reset --hard &&
echo "1 0 file" >expected &&
git stash clear &&
test_when_finished "git reset --hard HEAD" &&
git reset --hard &&
- echo foo >> file &&
+ echo foo >>file &&
git stash &&
test_when_finished "git stash drop" &&
- echo bar >> file &&
+ echo bar >>file &&
STASH_ID=$(git stash create) &&
git reset --hard &&
cat >expected <<-EOF &&
git stash clear &&
test_when_finished "git reset --hard HEAD" &&
git reset --hard &&
- echo foo >> file &&
+ echo foo >>file &&
STASH_ID=$(git stash create) &&
git reset --hard &&
echo "1 0 file" >expected &&
git stash clear &&
test_when_finished "git reset --hard HEAD" &&
git reset --hard &&
- echo foo >> file &&
+ echo foo >>file &&
STASH_ID=$(git stash create) &&
git reset --hard &&
cat >expected <<-EOF &&
test_cmp expected actual
'
-test_expect_success 'stash drop - fail early if specified stash is not a stash reference' '
+test_expect_success 'stash show --patience shows diff' '
+ git reset --hard &&
+ echo foo >>file &&
+ STASH_ID=$(git stash create) &&
+ git reset --hard &&
+ cat >expected <<-EOF &&
+ diff --git a/file b/file
+ index 7601807..71b52c4 100644
+ --- a/file
+ +++ b/file
+ @@ -1 +1,2 @@
+ baz
+ +foo
+ EOF
+ git stash show --patience ${STASH_ID} >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'drop: fail early if specified stash is not a stash ref' '
git stash clear &&
test_when_finished "git reset --hard HEAD && git stash clear" &&
git reset --hard &&
- echo foo > file &&
+ echo foo >file &&
git stash &&
- echo bar > file &&
+ echo bar >file &&
git stash &&
test_must_fail git stash drop $(git rev-parse stash@{0}) &&
git stash pop &&
git reset --hard HEAD
'
-test_expect_success 'stash pop - fail early if specified stash is not a stash reference' '
+test_expect_success 'pop: fail early if specified stash is not a stash ref' '
git stash clear &&
test_when_finished "git reset --hard HEAD && git stash clear" &&
git reset --hard &&
- echo foo > file &&
+ echo foo >file &&
git stash &&
- echo bar > file &&
+ echo bar >file &&
git stash &&
test_must_fail git stash pop $(git rev-parse stash@{0}) &&
git stash pop &&
test_expect_success 'ref with non-existent reflog' '
git stash clear &&
- echo bar5 > file &&
- echo bar6 > file2 &&
+ echo bar5 >file &&
+ echo bar6 >file2 &&
git add file2 &&
git stash &&
test_must_fail git rev-parse --quiet --verify does-not-exist &&
test_expect_success 'invalid ref of the form stash@{n}, n >= N' '
git stash clear &&
test_must_fail git stash drop stash@{0} &&
- echo bar5 > file &&
- echo bar6 > file2 &&
+ echo bar5 >file &&
+ echo bar6 >file2 &&
git add file2 &&
git stash &&
test_must_fail git stash drop stash@{1} &&
git stash drop
'
-test_expect_success 'stash branch should not drop the stash if the branch exists' '
+test_expect_success 'branch: do not drop the stash if the branch exists' '
git stash clear &&
echo foo >file &&
git add file &&
git rev-parse stash@{0} --
'
-test_expect_success 'stash branch should not drop the stash if the apply fails' '
+test_expect_success 'branch: should not drop the stash if the apply fails' '
git stash clear &&
git reset HEAD~1 --hard &&
echo foo >file &&
git rev-parse stash@{0} --
'
-test_expect_success 'stash apply shows status same as git status (relative to current directory)' '
+test_expect_success 'apply: show same status as git status (relative to ./)' '
git stash clear &&
echo 1 >subdir/subfile1 &&
echo 2 >subdir/subfile2 &&
test_i18ncmp expect actual
'
-cat > expect << EOF
+cat >expect <<EOF
diff --git a/HEAD b/HEAD
new file mode 100644
index 0000000..fe0cbee
test_expect_success 'stash where working directory contains "HEAD" file' '
git stash clear &&
git reset --hard &&
- echo file-not-a-ref > HEAD &&
+ echo file-not-a-ref >HEAD &&
git add HEAD &&
test_tick &&
git stash &&
git diff-files --quiet &&
git diff-index --cached --quiet HEAD &&
test "$(git rev-parse stash^)" = "$(git rev-parse HEAD)" &&
- git diff stash^..stash > output &&
+ git diff stash^..stash >output &&
test_cmp expect output
'
test_i18ncmp expect actual
'
-test_expect_success 'stash push with pathspec shows no changes when there are none' '
+test_expect_success 'push <pathspec>: show no changes when there are none' '
>foo &&
git add foo &&
git commit -m "tmp" &&
test_i18ncmp expect actual
'
-test_expect_success 'stash push with pathspec not in the repository errors out' '
+test_expect_success 'push: <pathspec> not in the repository errors out' '
>untracked &&
test_must_fail git stash push untracked &&
test_path_is_file untracked
'
+test_expect_success 'push: -q is quiet with changes' '
+ >foo &&
+ git add foo &&
+ git stash push -q >output 2>&1 &&
+ test_must_be_empty output
+'
+
+test_expect_success 'push: -q is quiet with no changes' '
+ git stash push -q >output 2>&1 &&
+ test_must_be_empty output
+'
+
+test_expect_success 'push: -q is quiet even if there is no initial commit' '
+ git init foo_dir &&
+ test_when_finished rm -rf foo_dir &&
+ (
+ cd foo_dir &&
+ >bar &&
+ test_must_fail git stash push -q >output 2>&1 &&
+ test_must_be_empty output
+ )
+'
+
test_expect_success 'untracked files are left in place when -u is not given' '
>file &&
git add file &&
test_path_is_file subdir/untracked
'
+test_expect_success 'stash with user.name and user.email set works' '
+ test_config user.name "A U Thor" &&
+ test_config user.email "a.u@thor" &&
+ git stash
+'
+
test_expect_success 'stash works when user.name and user.email are not set' '
git reset &&
>1 &&
test_i18ncmp expect actual
'
+test_expect_success 'stash -u with globs' '
+ >untracked.txt &&
+ git stash -u -- ":(glob)**/*.txt" &&
+ test_path_is_missing untracked.txt
+'
+
test_done
--- /dev/null
+#!/bin/sh
+
+test_description='Test git stash show configuration.'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ test_commit file
+'
+
+# takes three parameters:
+# 1. the stash.showStat value (or "<unset>")
+# 2. the stash.showPatch value (or "<unset>")
+# 3. the diff options of the expected output (or nothing for no output)
+test_stat_and_patch () {
+ if test "<unset>" = "$1"
+ then
+ test_unconfig stash.showStat
+ else
+ test_config stash.showStat "$1"
+ fi &&
+
+ if test "<unset>" = "$2"
+ then
+ test_unconfig stash.showPatch
+ else
+ test_config stash.showPatch "$2"
+ fi &&
+
+ shift 2 &&
+ echo 2 >file.t &&
+ if test $# != 0
+ then
+ git diff "$@" >expect
+ fi &&
+ git stash &&
+ git stash show >actual &&
+
+ if test $# = 0
+ then
+ test_must_be_empty actual
+ else
+ test_cmp expect actual
+ fi
+}
+
+test_expect_success 'showStat unset showPatch unset' '
+ test_stat_and_patch "<unset>" "<unset>" --stat
+'
+
+test_expect_success 'showStat unset showPatch false' '
+ test_stat_and_patch "<unset>" false --stat
+'
+
+test_expect_success 'showStat unset showPatch true' '
+ test_stat_and_patch "<unset>" true --stat -p
+'
+
+test_expect_success 'showStat false showPatch unset' '
+ test_stat_and_patch false "<unset>"
+'
+
+test_expect_success 'showStat false showPatch false' '
+ test_stat_and_patch false false
+'
+
+test_expect_success 'showStat false showPatch true' '
+ test_stat_and_patch false true -p
+'
+
+test_expect_success 'showStat true showPatch unset' '
+ test_stat_and_patch true "<unset>" --stat
+'
+
+test_expect_success 'showStat true showPatch false' '
+ test_stat_and_patch true false --stat
+'
+
+test_expect_success 'showStat true showPatch true' '
+ test_stat_and_patch true true --stat -p
+'
+
+test_done
ls patches/0004-This-is-an-excessively-long-subject-line-for-a-messa.patch
'
+test_expect_success 'failure to write cover-letter aborts gracefully' '
+ test_when_finished "rmdir 0000-cover-letter.patch" &&
+ mkdir 0000-cover-letter.patch &&
+ test_must_fail git format-patch --no-renames --cover-letter -1
+'
+
test_expect_success 'cover-letter inherits diff options' '
git mv file foo &&
git commit -m foo &&
git branch side1d &&
git branch side2d &&
git checkout side1d &&
- test_seq 1 10 >$(printf "file\twith\ttabs") &&
+ test_seq 1 10 >"$(printf "file\twith\ttabs")" &&
git add file* &&
git commit -m with &&
git checkout side2d &&
- test_seq 1 9 >$(printf "i\tam\ttabbed") &&
- echo ten >>$(printf "i\tam\ttabbed") &&
+ test_seq 1 9 >"$(printf "i\tam\ttabbed")" &&
+ echo ten >>"$(printf "i\tam\ttabbed")" &&
git add *tabbed &&
git commit -m iam &&
git checkout -b funny-names-mergery side1d &&
git merge --no-commit side2d &&
git rm *tabs &&
- echo eleven >>$(printf "i\tam\ttabbed") &&
+ echo eleven >>"$(printf "i\tam\ttabbed")" &&
git mv "$(printf "i\tam\ttabbed")" "$(printf "fickle\tnaming")" &&
git add fickle* &&
git commit
printf "Subject: " >subject-prefix &&
- cat - subject-prefix msg-without-scissors-line >msg-with-scissors-line <<-\EOF &&
+ cat - subject-prefix msg-without-scissors-line >msg-with-scissors-line <<-\EOF
This line should not be included in the commit message with --scissors enabled.
- - >8 - - remove everything above this line - - >8 - -
EOF
-
- signoff="Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>"
'
test_expect_success setup '
git log $(for x in $(test_seq 200); do echo -L $((2*x)),+1:c.c; done)
'
+test_expect_success '-s shows only line-log commits' '
+ git log --format="commit %s" -L1,24:b.c >expect.raw &&
+ grep ^commit expect.raw >expect &&
+ git log --format="commit %s" -L1,24:b.c -s >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '-p shows the default patch output' '
+ git log -L1,24:b.c >expect &&
+ git log -L1,24:b.c -p >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--raw is forbidden' '
+ test_must_fail git log -L1,24:b.c --raw
+'
+
test_done
awk -f print_2.awk ls_files_result |
sort >expected &&
- git -C r1 pack-objects --rev --stdout >all.pack <<-EOF &&
+ git -C r1 pack-objects --revs --stdout >all.pack <<-EOF &&
HEAD
EOF
git -C r1 index-pack ../all.pack &&
'
test_expect_success 'verify blob:none packfile has no blobs' '
- git -C r1 pack-objects --rev --stdout --filter=blob:none >filter.pack <<-EOF &&
+ git -C r1 pack-objects --revs --stdout --filter=blob:none >filter.pack <<-EOF &&
HEAD
EOF
git -C r1 index-pack ../filter.pack &&
git -C r5 commit -m "foo" &&
del=$(git -C r5 rev-parse HEAD^{tree} | sed "s|..|&/|") &&
rm r5/.git/objects/$del &&
- test_must_fail git -C r5 pack-objects --rev --stdout 2>bad_tree <<-EOF &&
+ test_must_fail git -C r5 pack-objects --revs --stdout 2>bad_tree <<-EOF &&
HEAD
EOF
grep "bad tree object" bad_tree
'
test_expect_success 'verify tree:0 packfile has no blobs or trees' '
- git -C r1 pack-objects --rev --stdout --filter=tree:0 >commitsonly.pack <<-EOF &&
+ git -C r1 pack-objects --revs --stdout --filter=tree:0 >commitsonly.pack <<-EOF &&
HEAD
EOF
git -C r1 index-pack ../commitsonly.pack &&
test_expect_success 'grab tree directly when using tree:0' '
# We should get the tree specified directly but not its blobs or subtrees.
- git -C r1 pack-objects --rev --stdout --filter=tree:0 >commitsonly.pack <<-EOF &&
+ git -C r1 pack-objects --revs --stdout --filter=tree:0 >commitsonly.pack <<-EOF &&
HEAD:
EOF
git -C r1 index-pack ../commitsonly.pack &&
awk -f print_2.awk ls_files_result |
sort >expected &&
- git -C r2 pack-objects --rev --stdout >all.pack <<-EOF &&
+ git -C r2 pack-objects --revs --stdout >all.pack <<-EOF &&
HEAD
EOF
git -C r2 index-pack ../all.pack &&
'
test_expect_success 'verify blob:limit=500 omits all blobs' '
- git -C r2 pack-objects --rev --stdout --filter=blob:limit=500 >filter.pack <<-EOF &&
+ git -C r2 pack-objects --revs --stdout --filter=blob:limit=500 >filter.pack <<-EOF &&
HEAD
EOF
git -C r2 index-pack ../filter.pack &&
'
test_expect_success 'verify blob:limit=1000' '
- git -C r2 pack-objects --rev --stdout --filter=blob:limit=1000 >filter.pack <<-EOF &&
+ git -C r2 pack-objects --revs --stdout --filter=blob:limit=1000 >filter.pack <<-EOF &&
HEAD
EOF
git -C r2 index-pack ../filter.pack &&
awk -f print_2.awk ls_files_result |
sort >expected &&
- git -C r2 pack-objects --rev --stdout --filter=blob:limit=1001 >filter.pack <<-EOF &&
+ git -C r2 pack-objects --revs --stdout --filter=blob:limit=1001 >filter.pack <<-EOF &&
HEAD
EOF
git -C r2 index-pack ../filter.pack &&
awk -f print_2.awk ls_files_result |
sort >expected &&
- git -C r2 pack-objects --rev --stdout --filter=blob:limit=10001 >filter.pack <<-EOF &&
+ git -C r2 pack-objects --revs --stdout --filter=blob:limit=10001 >filter.pack <<-EOF &&
HEAD
EOF
git -C r2 index-pack ../filter.pack &&
awk -f print_2.awk ls_files_result |
sort >expected &&
- git -C r2 pack-objects --rev --stdout --filter=blob:limit=1k >filter.pack <<-EOF &&
+ git -C r2 pack-objects --revs --stdout --filter=blob:limit=1k >filter.pack <<-EOF &&
HEAD
EOF
git -C r2 index-pack ../filter.pack &&
awk -f print_2.awk ls_files_result |
sort >expected &&
- git -C r2 pack-objects --rev --stdout --filter=blob:limit=1k >filter.pack <<-EOF &&
+ git -C r2 pack-objects --revs --stdout --filter=blob:limit=1k >filter.pack <<-EOF &&
HEAD
$(git -C r2 rev-parse HEAD:large.10000)
EOF
awk -f print_2.awk ls_files_result |
sort >expected &&
- git -C r2 pack-objects --rev --stdout --filter=blob:limit=1m >filter.pack <<-EOF &&
+ git -C r2 pack-objects --revs --stdout --filter=blob:limit=1m >filter.pack <<-EOF &&
HEAD
EOF
git -C r2 index-pack ../filter.pack &&
awk -f print_2.awk ls_files_result |
sort >expected &&
- git -C r3 pack-objects --rev --stdout >all.pack <<-EOF &&
+ git -C r3 pack-objects --revs --stdout >all.pack <<-EOF &&
HEAD
EOF
git -C r3 index-pack ../all.pack &&
awk -f print_2.awk ls_files_result |
sort >expected &&
- git -C r3 pack-objects --rev --stdout --filter=sparse:path=../pattern1 >filter.pack <<-EOF &&
+ git -C r3 pack-objects --revs --stdout --filter=sparse:path=../pattern1 >filter.pack <<-EOF &&
HEAD
EOF
git -C r3 index-pack ../filter.pack &&
awk -f print_2.awk ls_files_result |
sort >expected &&
- git -C r3 pack-objects --rev --stdout --filter=sparse:path=../pattern2 >filter.pack <<-EOF &&
+ git -C r3 pack-objects --revs --stdout --filter=sparse:path=../pattern2 >filter.pack <<-EOF &&
HEAD
EOF
git -C r3 index-pack ../filter.pack &&
awk -f print_2.awk ls_files_result |
sort >expected &&
- git -C r4 pack-objects --rev --stdout >all.pack <<-EOF &&
+ git -C r4 pack-objects --revs --stdout >all.pack <<-EOF &&
HEAD
EOF
git -C r4 index-pack ../all.pack &&
sort >expected &&
oid=$(git -C r4 ls-files -s pattern | awk -f print_2.awk) &&
- git -C r4 pack-objects --rev --stdout --filter=sparse:oid=$oid >filter.pack <<-EOF &&
+ git -C r4 pack-objects --revs --stdout --filter=sparse:oid=$oid >filter.pack <<-EOF &&
HEAD
EOF
git -C r4 index-pack ../filter.pack &&
awk -f print_2.awk ls_files_result |
sort >expected &&
- git -C r4 pack-objects --rev --stdout --filter=sparse:oid=master:pattern >filter.pack <<-EOF &&
+ git -C r4 pack-objects --revs --stdout --filter=sparse:oid=master:pattern >filter.pack <<-EOF &&
HEAD
EOF
git -C r4 index-pack ../filter.pack &&
'
test_expect_success 'verify pack-objects fails w/ missing objects' '
- test_must_fail git -C r1 pack-objects --rev --stdout >miss.pack <<-EOF
+ test_must_fail git -C r1 pack-objects --revs --stdout >miss.pack <<-EOF
HEAD
EOF
'
test_expect_success 'verify pack-objects fails w/ --missing=error' '
- test_must_fail git -C r1 pack-objects --rev --stdout --missing=error >miss.pack <<-EOF
+ test_must_fail git -C r1 pack-objects --revs --stdout --missing=error >miss.pack <<-EOF
HEAD
EOF
'
test_expect_success 'verify pack-objects w/ --missing=allow-any' '
- git -C r1 pack-objects --rev --stdout --missing=allow-any >miss.pack <<-EOF
+ git -C r1 pack-objects --revs --stdout --missing=allow-any >miss.pack <<-EOF
HEAD
EOF
'
test_expect_success 'write graph' '
cd "$TRASH_DIRECTORY/full" &&
- graph1=$(git commit-graph write) &&
+ git commit-graph write &&
test_path_is_file $objdir/info/commit-graph &&
graph_read_expect "3"
'
$shared .have
EOF
- GIT_TRACE_PACKET=$(pwd)/trace \
+ GIT_TRACE_PACKET=$(pwd)/trace GIT_TEST_PROTOCOL_VERSION= \
git push \
--receive-pack="unset GIT_TRACE_PACKET; git-receive-pack" \
fork HEAD:foo &&
test_expect_success 'git rebase with implicit use of interactive backend' '
git reset --hard D &&
clear_hook_input &&
- test_must_fail git rebase --keep --onto A B &&
+ test_must_fail git rebase --keep-empty --onto A B &&
echo C > foo &&
git add foo &&
git rebase --continue &&
test_expect_success 'git rebase --skip with implicit use of interactive backend' '
git reset --hard D &&
clear_hook_input &&
- test_must_fail git rebase --keep --onto A B &&
+ test_must_fail git rebase --keep-empty --onto A B &&
test_must_fail git rebase --skip &&
echo D > foo &&
git add foo &&
test_commit -C server 6 &&
git init client &&
- test_must_fail git -C client fetch-pack ../server \
+ # Some protocol versions (e.g. 2) support fetching
+ # unadvertised objects, so restrict this test to v0.
+ test_must_fail env GIT_TEST_PROTOCOL_VERSION= git -C client fetch-pack ../server \
$(git -C server rev-parse refs/heads/master^) 2>err &&
test_i18ngrep "Server does not allow request for unadvertised object" err
'
test -s "$1" &&
perl -alne '
next unless $F[1] eq "upload-pack<";
- last if $F[2] eq "0000";
+ next unless $F[2] eq "want";
print $F[2], " ", $F[3];
' "$1"
}
$(git rev-parse refs/tags/mark1.10) refs/tags/mark1.10
$(git rev-parse refs/tags/mark1.2) refs/tags/mark1.2
EOF
- git ls-remote --symref >actual &&
+ # Protocol v2 supports sending symrefs for refs other than HEAD, so use
+ # protocol v0 here.
+ GIT_TEST_PROTOCOL_VERSION= git ls-remote --symref >actual &&
test_cmp expect actual
'
ref: refs/heads/master HEAD
1bd44cb9d13204b0fe1958db0082f5028a16eb3a HEAD
EOF
- git ls-remote --symref . HEAD >actual &&
+ # Protocol v2 supports sending symrefs for refs other than HEAD, so use
+ # protocol v0 here.
+ GIT_TEST_PROTOCOL_VERSION= git ls-remote --symref . HEAD >actual &&
test_cmp expect actual
'
1bd44cb9d13204b0fe1958db0082f5028a16eb3a refs/heads/foo
1bd44cb9d13204b0fe1958db0082f5028a16eb3a refs/heads/master
EOF
- git ls-remote --symref --heads . >actual &&
+ # Protocol v2 supports sending symrefs for refs other than HEAD, so use
+ # protocol v0 here.
+ GIT_TEST_PROTOCOL_VERSION= git ls-remote --symref --heads . >actual &&
test_cmp expect actual
'
1bd44cb9d13204b0fe1958db0082f5028a16eb3a refs/heads/foo
1bd44cb9d13204b0fe1958db0082f5028a16eb3a refs/heads/master
EOF
- git ls-remote --symref --heads . >actual &&
+ # Protocol v2 supports sending symrefs for refs other than HEAD, so use
+ # protocol v0 here.
+ GIT_TEST_PROTOCOL_VERSION= git ls-remote --symref --heads . >actual &&
test_cmp expect actual &&
- git ls-remote --symref . "refs/heads/*" >actual &&
+ GIT_TEST_PROTOCOL_VERSION= git ls-remote --symref . "refs/heads/*" >actual &&
test_cmp expect actual
'
test_description='Merge logic in fetch'
+# NEEDSWORK: If the overspecification of the expected result is reduced, we
+# might be able to run this test in all protocol versions.
+GIT_TEST_PROTOCOL_VERSION=
+
. ./test-lib.sh
LF='
git prune &&
test_must_fail git cat-file -t $the_commit &&
+ # Some protocol versions (e.g. 2) support fetching
+ # unadvertised objects, so restrict this test to v0.
+
# fetching the hidden object should fail by default
- test_must_fail git fetch -v ../testrepo $the_commit:refs/heads/copy 2>err &&
+ test_must_fail env GIT_TEST_PROTOCOL_VERSION= \
+ git fetch -v ../testrepo $the_commit:refs/heads/copy 2>err &&
test_i18ngrep "Server does not allow request for unadvertised object" err &&
test_must_fail git rev-parse --verify refs/heads/copy &&
mk_empty shallow &&
(
cd shallow &&
- test_must_fail git fetch --depth=1 ../testrepo/.git $SHA1 &&
+ # Some protocol versions (e.g. 2) support fetching
+ # unadvertised objects, so restrict this test to v0.
+ test_must_fail env GIT_TEST_PROTOCOL_VERSION= \
+ git fetch --depth=1 ../testrepo/.git $SHA1 &&
git --git-dir=../testrepo/.git config uploadpack.allowreachablesha1inwant true &&
git fetch --depth=1 ../testrepo/.git $SHA1 &&
git cat-file commit $SHA1
mk_empty shallow &&
(
cd shallow &&
- test_must_fail ok=sigpipe git fetch ../testrepo/.git $SHA1_3 &&
- test_must_fail ok=sigpipe git fetch ../testrepo/.git $SHA1_1 &&
+ # Some protocol versions (e.g. 2) support fetching
+ # unadvertised objects, so restrict this test to v0.
+ test_must_fail ok=sigpipe env GIT_TEST_PROTOCOL_VERSION= \
+ git fetch ../testrepo/.git $SHA1_3 &&
+ test_must_fail ok=sigpipe env GIT_TEST_PROTOCOL_VERSION= \
+ git fetch ../testrepo/.git $SHA1_1 &&
git --git-dir=../testrepo/.git config uploadpack.allowreachablesha1inwant true &&
git fetch ../testrepo/.git $SHA1_1 &&
git cat-file commit $SHA1_1 &&
test_must_fail git cat-file commit $SHA1_2 &&
git fetch ../testrepo/.git $SHA1_2 &&
git cat-file commit $SHA1_2 &&
- test_must_fail ok=sigpipe git fetch ../testrepo/.git $SHA1_3
+ test_must_fail ok=sigpipe env GIT_TEST_PROTOCOL_VERSION= \
+ git fetch ../testrepo/.git $SHA1_3
)
'
done
test_cmp expect actual
'
-test_expect_success 'push --follow-tag only pushes relevant tags' '
+test_expect_success 'push --follow-tags only pushes relevant tags' '
mk_test testrepo heads/master &&
rm -fr src dst &&
git init src &&
git tag -m "future" future &&
git checkout master &&
git for-each-ref refs/heads/master refs/tags/tag >../expect &&
- git push --follow-tag ../dst master
+ git push --follow-tags ../dst master
) &&
(
cd dst &&
) &&
git add b &&
git commit -m "added submodule" &&
- git push --recurse-submodule=check origin master
+ git push --recurse-submodules=check origin master
)
'
cd clone &&
git checkout --orphan newnew &&
test_commit new-too &&
- GIT_TRACE_PACKET="$TRASH_DIRECTORY/trace" git fetch --depth=2 &&
+ # NEEDSWORK: If the overspecification of the expected result is reduced, we
+ # might be able to run this test in all protocol versions.
+ GIT_TRACE_PACKET="$TRASH_DIRECTORY/trace" GIT_TEST_PROTOCOL_VERSION= \
+ git fetch --depth=2 &&
grep "fetch-pack< ACK .* ready" ../trace &&
! grep "fetch-pack> done" ../trace
)
cd "$ROOT_PATH" &&
git clone $HTTPD_URL/smart/test_repo.git/ test_repo_clone &&
- check_access_log exp
+ # NEEDSWORK: If the overspecification of the expected result is reduced, we
+ # might be able to run this test in all protocol versions.
+ if test -z "$GIT_TEST_PROTOCOL_VERSION"
+ then
+ check_access_log exp
+ fi
'
test_expect_success 'clone remote repository' '
POST /smart/test_repo.git/git-receive-pack HTTP/1.1 200
EOF
test_expect_success 'used receive-pack service' '
- check_access_log exp
+ # NEEDSWORK: If the overspecification of the expected result is reduced, we
+ # might be able to run this test in all protocol versions.
+ if test -z "$GIT_TEST_PROTOCOL_VERSION"
+ then
+ check_access_log exp
+ fi
'
test_http_push_nonff "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git \
test_i18ngrep "unable to access.*/redir-to/502" stderr
'
+test_expect_success 'fetching via http alternates works' '
+ parent=$HTTPD_DOCUMENT_ROOT_PATH/alt-parent.git &&
+ git init --bare "$parent" &&
+ git -C "$parent" --work-tree=. commit --allow-empty -m foo &&
+ git -C "$parent" update-server-info &&
+ commit=$(git -C "$parent" rev-parse HEAD) &&
+
+ child=$HTTPD_DOCUMENT_ROOT_PATH/alt-child.git &&
+ git init --bare "$child" &&
+ echo "../../alt-parent.git/objects" >"$child/objects/info/alternates" &&
+ git -C "$child" update-ref HEAD $commit &&
+ git -C "$child" update-server-info &&
+
+ git -c http.followredirects=true clone "$HTTPD_URL/dumb/alt-child.git"
+'
+
test_done
< Cache-Control: no-cache, max-age=0, must-revalidate
< Content-Type: application/x-git-upload-pack-result
EOF
- GIT_TRACE_CURL=true git clone --quiet $HTTPD_URL/smart/repo.git clone 2>err &&
+ GIT_TRACE_CURL=true GIT_TEST_PROTOCOL_VERSION= \
+ git clone --quiet $HTTPD_URL/smart/repo.git clone 2>err &&
test_cmp file clone/file &&
tr '\''\015'\'' Q <err |
sed -e "
/^< Content-Length: /d
/^< Transfer-Encoding: /d
" >actual &&
- sed -e "s/^> Accept-Encoding: .*/> Accept-Encoding: ENCODINGS/" \
- actual >actual.smudged &&
- test_cmp exp actual.smudged &&
- grep "Accept-Encoding:.*gzip" actual >actual.gzip &&
- test_line_count = 2 actual.gzip
+ # NEEDSWORK: If the overspecification of the expected result is reduced, we
+ # might be able to run this test in all protocol versions.
+ if test -z "$GIT_TEST_PROTOCOL_VERSION"
+ then
+ sed -e "s/^> Accept-Encoding: .*/> Accept-Encoding: ENCODINGS/" \
+ actual >actual.smudged &&
+ test_cmp exp actual.smudged &&
+
+ grep "Accept-Encoding:.*gzip" actual >actual.gzip &&
+ test_line_count = 2 actual.gzip
+ fi
'
test_expect_success 'fetch changes via http' '
GET /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
POST /smart/repo.git/git-upload-pack HTTP/1.1 200
EOF
- check_access_log exp
+
+ # NEEDSWORK: If the overspecification of the expected result is reduced, we
+ # might be able to run this test in all protocol versions.
+ if test -z "$GIT_TEST_PROTOCOL_VERSION"
+ then
+ check_access_log exp
+ fi
'
test_expect_success 'follow redirects (301)' '
test_expect_success 'no-op half-auth fetch does not require a password' '
set_askpass wrong &&
- git --git-dir=half-auth fetch &&
+
+ # NEEDSWORK: When using HTTP(S), protocol v0 supports a "half-auth"
+ # configuration with authentication required only when downloading
+ # objects and not refs, by having the HTTP server only require
+ # authentication for the "git-upload-pack" path and not "info/refs".
+ # This is not possible with protocol v2, since both objects and refs
+ # are obtained from the "git-upload-pack" path. A solution to this is
+ # to teach the server and client to be able to inline ls-refs requests
+ # as an Extra Parameter (see pack-protocol.txt), so that "info/refs"
+ # can serve refs, just like it does in protocol v0.
+ GIT_TEST_PROTOCOL_VERSION=0 git --git-dir=half-auth fetch &&
expect_askpass none
'
git config http.cookiefile cookies.txt &&
git config http.savecookies true &&
git ls-remote $HTTPD_URL/smart_cookies/repo.git master &&
- tail -3 cookies.txt | sort >cookies_tail.txt &&
- test_cmp expect_cookies.txt cookies_tail.txt
+
+ # NEEDSWORK: If the overspecification of the expected result is reduced, we
+ # might be able to run this test in all protocol versions.
+ if test -z "$GIT_TEST_PROTOCOL_VERSION"
+ then
+ tail -3 cookies.txt | sort >cookies_tail.txt &&
+ test_cmp expect_cookies.txt cookies_tail.txt
+ fi
'
test_expect_success 'transfer.hiderefs works over smart-http' '
git init --bare test_reachable.git &&
git -C test_reachable.git remote add origin "$HTTPD_URL/smart/repo.git" &&
- test_must_fail git -C test_reachable.git fetch origin "$(git rev-parse HEAD)"
+ # Some protocol versions (e.g. 2) support fetching
+ # unadvertised objects, so restrict this test to v0.
+ test_must_fail env GIT_TEST_PROTOCOL_VERSION= \
+ git -C test_reachable.git fetch origin "$(git rev-parse HEAD)"
'
test_expect_success 'test allowanysha1inwant with unreachable' '
git init --bare test_reachable.git &&
git -C test_reachable.git remote add origin "$HTTPD_URL/smart/repo.git" &&
- test_must_fail git -C test_reachable.git fetch origin "$(git rev-parse HEAD)" &&
+ # Some protocol versions (e.g. 2) support fetching
+ # unadvertised objects, so restrict this test to v0.
+ test_must_fail env GIT_TEST_PROTOCOL_VERSION= \
+ git -C test_reachable.git fetch origin "$(git rev-parse HEAD)" &&
git -C "$server" config uploadpack.allowanysha1inwant 1 &&
git -C test_reachable.git fetch origin "$(git rev-parse HEAD)"
# not need to send any ancestors of "c3", but we still need to send "c3"
# itself.
test_config -C client fetch.negotiationalgorithm skipping &&
- trace_fetch client origin to_fetch &&
+
+ # The ref advertisement itself is filtered when protocol v2 is used, so
+ # use v0.
+ GIT_TEST_PROTOCOL_VERSION= trace_fetch client origin to_fetch &&
have_sent c5 c4^ c2side &&
have_not_sent c4 c4^^ c4^^^
'
}
test_expect_success 'clone myhost:src uses ssh' '
- git clone myhost:src ssh-clone &&
+ GIT_TEST_PROTOCOL_VERSION=0 git clone myhost:src ssh-clone &&
expect_ssh myhost src
'
'
test_expect_success 'bracketed hostnames are still ssh' '
- git clone "[myhost:123]:src" ssh-bracket-clone &&
+ GIT_TEST_PROTOCOL_VERSION=0 git clone "[myhost:123]:src" ssh-bracket-clone &&
expect_ssh "-p 123" myhost src
'
test_expect_success 'OpenSSH variant passes -4' '
- git clone -4 "[myhost:123]:src" ssh-ipv4-clone &&
+ GIT_TEST_PROTOCOL_VERSION=0 git clone -4 "[myhost:123]:src" ssh-ipv4-clone &&
expect_ssh "-4 -p 123" myhost src
'
test_when_finished "rm -f \"\$TRASH_DIRECTORY/uplink\"" &&
GIT_SSH="$TRASH_DIRECTORY/uplink" &&
test_when_finished "GIT_SSH=\"\$TRASH_DIRECTORY/ssh\$X\"" &&
- git clone "[myhost:123]:src" ssh-bracket-clone-sshlike-uplink &&
+ GIT_TEST_PROTOCOL_VERSION=0 git clone "[myhost:123]:src" ssh-bracket-clone-sshlike-uplink &&
expect_ssh "-p 123" myhost src
'
test_expect_success 'GIT_SSH_VARIANT overrides plink detection' '
copy_ssh_wrapper_as "$TRASH_DIRECTORY/plink" &&
- GIT_SSH_VARIANT=ssh \
- git clone "[myhost:123]:src" ssh-bracket-clone-variant-1 &&
+ GIT_TEST_PROTOCOL_VERSION=0 GIT_SSH_VARIANT=ssh \
+ git clone "[myhost:123]:src" ssh-bracket-clone-variant-1 &&
expect_ssh "-p 123" myhost src
'
test_expect_success 'ssh.variant overrides plink detection' '
copy_ssh_wrapper_as "$TRASH_DIRECTORY/plink" &&
- git -c ssh.variant=ssh \
+ GIT_TEST_PROTOCOL_VERSION=0 git -c ssh.variant=ssh \
clone "[myhost:123]:src" ssh-bracket-clone-variant-2 &&
expect_ssh "-p 123" myhost src
'
# $3 path
test_clone_url () {
counter=$(($counter + 1))
- test_might_fail git clone "$1" tmp$counter &&
+ test_might_fail env GIT_TEST_PROTOCOL_VERSION=0 git clone "$1" tmp$counter &&
shift &&
expect_ssh "$@"
}
TEST_NO_CREATE_REPO=1
+# This is a protocol-specific test.
+GIT_TEST_PROTOCOL_VERSION=
+
. ./test-lib.sh
# Test protocol v1 with 'git://' transport
check_same BROKEN_HASH6 BISECT_HEAD &&
git bisect bad BISECT_HEAD &&
check_same BROKEN_HASH5 BISECT_HEAD &&
- git bisect good BISECT_HEAD &&
+ test_must_fail git bisect good BISECT_HEAD &&
check_same BROKEN_HASH6 bisect/bad &&
git bisect reset
'
check_same BROKEN_HASH6 BISECT_HEAD &&
git bisect good BISECT_HEAD &&
check_same BROKEN_HASH8 BISECT_HEAD &&
- git bisect good BISECT_HEAD &&
+ test_must_fail git bisect good BISECT_HEAD &&
check_same BROKEN_HASH9 bisect/bad &&
git bisect reset
'
git bisect reset &&
git checkout broken &&
git bisect start broken master --no-checkout &&
- git bisect run \"\$SHELL_PATH\" -c '
+ test_must_fail git bisect run \"\$SHELL_PATH\" -c '
GOOD=\$(git for-each-ref \"--format=%(objectname)\" refs/bisect/good-*) &&
git rev-list --objects BISECT_HEAD --not \$GOOD >tmp.\$\$ &&
git pack-objects --stdout >/dev/null < tmp.\$\$
--- /dev/null
+#!/bin/sh
+
+test_description='post index change hook'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ mkdir -p dir1 &&
+ touch dir1/file1.txt &&
+ echo testing >dir1/file2.txt &&
+ git add . &&
+ git commit -m "initial"
+'
+
+test_expect_success 'test status, add, commit, others trigger hook without flags set' '
+ mkdir -p .git/hooks &&
+ write_script .git/hooks/post-index-change <<-\EOF &&
+ if test "$1" -eq 1; then
+ echo "Invalid combination of flags passed to hook; updated_workdir is set." >testfailure
+ exit 1
+ fi
+ if test "$2" -eq 1; then
+ echo "Invalid combination of flags passed to hook; updated_skipworktree is set." >testfailure
+ exit 1
+ fi
+ if test -f ".git/index.lock"; then
+ echo ".git/index.lock exists" >testfailure
+ exit 3
+ fi
+ if ! test -f ".git/index"; then
+ echo ".git/index does not exist" >testfailure
+ exit 3
+ fi
+ echo "success" >testsuccess
+ EOF
+ mkdir -p dir2 &&
+ touch dir2/file1.txt &&
+ touch dir2/file2.txt &&
+ : force index to be dirty &&
+ test-tool chmtime +60 dir1/file1.txt &&
+ git status &&
+ test_path_is_file testsuccess && rm -f testsuccess &&
+ test_path_is_missing testfailure &&
+ git add . &&
+ test_path_is_file testsuccess && rm -f testsuccess &&
+ test_path_is_missing testfailure &&
+ git commit -m "second" &&
+ test_path_is_file testsuccess && rm -f testsuccess &&
+ test_path_is_missing testfailure &&
+ git checkout -- dir1/file1.txt &&
+ test_path_is_file testsuccess && rm -f testsuccess &&
+ test_path_is_missing testfailure &&
+ git update-index &&
+ test_path_is_missing testsuccess &&
+ test_path_is_missing testfailure &&
+ git reset --soft &&
+ test_path_is_missing testsuccess &&
+ test_path_is_missing testfailure
+'
+
+test_expect_success 'test checkout and reset trigger the hook' '
+ write_script .git/hooks/post-index-change <<-\EOF &&
+ if test "$1" -eq 1 && test "$2" -eq 1; then
+ echo "Invalid combination of flags passed to hook; updated_workdir and updated_skipworktree are both set." >testfailure
+ exit 1
+ fi
+ if test "$1" -eq 0 && test "$2" -eq 0; then
+ echo "Invalid combination of flags passed to hook; neither updated_workdir or updated_skipworktree are set." >testfailure
+ exit 2
+ fi
+ if test "$1" -eq 1; then
+ if test -f ".git/index.lock"; then
+ echo "updated_workdir set but .git/index.lock exists" >testfailure
+ exit 3
+ fi
+ if ! test -f ".git/index"; then
+ echo "updated_workdir set but .git/index does not exist" >testfailure
+ exit 3
+ fi
+ else
+ echo "update_workdir should be set for checkout" >testfailure
+ exit 4
+ fi
+ echo "success" >testsuccess
+ EOF
+ : force index to be dirty &&
+ test-tool chmtime +60 dir1/file1.txt &&
+ git checkout master &&
+ test_path_is_file testsuccess && rm -f testsuccess &&
+ test_path_is_missing testfailure &&
+ test-tool chmtime +60 dir1/file1.txt &&
+ git checkout HEAD &&
+ test_path_is_file testsuccess && rm -f testsuccess &&
+ test_path_is_missing testfailure &&
+ test-tool chmtime +60 dir1/file1.txt &&
+ git reset --hard &&
+ test_path_is_file testsuccess && rm -f testsuccess &&
+ test_path_is_missing testfailure &&
+ git checkout -B test &&
+ test_path_is_file testsuccess && rm -f testsuccess &&
+ test_path_is_missing testfailure
+'
+
+test_expect_success 'test reset --mixed and update-index triggers the hook' '
+ write_script .git/hooks/post-index-change <<-\EOF &&
+ if test "$1" -eq 1 && test "$2" -eq 1; then
+ echo "Invalid combination of flags passed to hook; updated_workdir and updated_skipworktree are both set." >testfailure
+ exit 1
+ fi
+ if test "$1" -eq 0 && test "$2" -eq 0; then
+ echo "Invalid combination of flags passed to hook; neither updated_workdir or updated_skipworktree are set." >testfailure
+ exit 2
+ fi
+ if test "$2" -eq 1; then
+ if test -f ".git/index.lock"; then
+ echo "updated_skipworktree set but .git/index.lock exists" >testfailure
+ exit 3
+ fi
+ if ! test -f ".git/index"; then
+ echo "updated_skipworktree set but .git/index does not exist" >testfailure
+ exit 3
+ fi
+ else
+ echo "updated_skipworktree should be set for reset --mixed and update-index" >testfailure
+ exit 4
+ fi
+ echo "success" >testsuccess
+ EOF
+ : force index to be dirty &&
+ test-tool chmtime +60 dir1/file1.txt &&
+ git reset --mixed --quiet HEAD~1 &&
+ test_path_is_file testsuccess && rm -f testsuccess &&
+ test_path_is_missing testfailure &&
+ git hash-object -w --stdin <dir1/file2.txt >expect &&
+ git update-index --cacheinfo 100644 "$(cat expect)" dir1/file1.txt &&
+ test_path_is_file testsuccess && rm -f testsuccess &&
+ test_path_is_missing testfailure &&
+ git update-index --skip-worktree dir1/file2.txt &&
+ git update-index --remove dir1/file2.txt &&
+ test_path_is_file testsuccess && rm -f testsuccess &&
+ test_path_is_missing testfailure
+'
+
+test_done
cd super3 &&
sed -e "s#url = ../#url = file://$pwd/#" <.gitmodules >.gitmodules.tmp &&
mv -f .gitmodules.tmp .gitmodules &&
- test_must_fail git submodule update --init --depth=1 2>actual &&
+ # Some protocol versions (e.g. 2) support fetching
+ # unadvertised objects, so restrict this test to v0.
+ test_must_fail env GIT_TEST_PROTOCOL_VERSION= \
+ git submodule update --init --depth=1 2>actual &&
test_i18ngrep "Direct fetching of that commit failed." actual &&
git -C ../submodule config uploadpack.allowReachableSHA1InWant true &&
git submodule update --init --depth=1 >actual &&
test_i18ngrep "deleted:" actual &&
test_i18ngrep "new file:" actual &&
- git status --find-rename=100% >actual &&
+ git status --find-renames=100% >actual &&
test_i18ngrep "deleted:" actual &&
test_i18ngrep "new file:" actual
'
git status -M=01% >actual &&
test_i18ngrep "renamed:" actual &&
- git status --find-rename=01% >actual &&
+ git status --find-renames=01% >actual &&
test_i18ngrep "renamed:" actual
'
-test_expect_success 'copies not overridden by find-rename' '
+test_expect_success 'copies not overridden by find-renames' '
cp renamed copy &&
git add copy &&
test_i18ngrep "copied:" actual &&
test_i18ngrep "renamed:" actual &&
- git -c status.renames=copies status --find-rename=01% >actual &&
+ git -c status.renames=copies status --find-renames=01% >actual &&
test_i18ngrep "copied:" actual &&
test_i18ngrep "renamed:" actual
'
test_cmp expected actual
'
- test_expect_success "grep -w $L (with --column, --invert)" '
+ test_expect_success "grep -w $L (with --column, --invert-match)" '
{
echo ${HC}file:1:foo mmap bar
echo ${HC}file:1:foo_mmap bar
echo ${HC}file:1:foo_mmap bar mmap
echo ${HC}file:1:foo mmap bar_mmap
} >expected &&
- git grep --column --invert -w -e baz $H -- file >actual &&
+ git grep --column --invert-match -w -e baz $H -- file >actual &&
test_cmp expected actual
'
- test_expect_success "grep $L (with --column, --invert, extended OR)" '
+ test_expect_success "grep $L (with --column, --invert-match, extended OR)" '
{
echo ${HC}hello_world:6:HeLLo_world
} >expected &&
- git grep --column --invert -e ll --or --not -e _ $H -- hello_world \
+ git grep --column --invert-match -e ll --or --not -e _ $H -- hello_world \
>actual &&
test_cmp expected actual
'
- test_expect_success "grep $L (with --column, --invert, extended AND)" '
+ test_expect_success "grep $L (with --column, --invert-match, extended AND)" '
{
echo ${HC}hello_world:3:Hello world
echo ${HC}hello_world:3:Hello_world
echo ${HC}hello_world:6:HeLLo_world
} >expected &&
- git grep --column --invert --not -e _ --and --not -e ll $H -- hello_world \
+ git grep --column --invert-match --not -e _ --and --not -e ll $H -- hello_world \
>actual &&
test_cmp expected actual
'
echo ".gitignore:.*o*" &&
cat ../expect.full
} >../expect.with.ignored &&
- git grep --no-index --no-exclude o >../actual.full &&
+ git grep --no-index --no-exclude-standard o >../actual.full &&
test_cmp ../expect.with.ignored ../actual.full
)
'
echo ".gitignore:.*o*" &&
cat ../expect.full
} >../expect.with.ignored &&
- git -c grep.fallbackToNoIndex grep --no-exclude o >../actual.full &&
+ git -c grep.fallbackToNoIndex grep --no-exclude-standard o >../actual.full &&
test_cmp ../expect.with.ignored ../actual.full
)
'
test_completion "git --help core" "core-tutorial "
'
+test_expect_success 'completion.commands removes multiple commands' '
+ test_config completion.commands "-cherry -mergetool" &&
+ git --list-cmds=list-mainporcelain,list-complete,config >out &&
+ ! grep -E "^(cherry|mergetool)$" out
+'
+
test_expect_success 'setup for integration tests' '
echo content >file1 &&
echo more >file2 &&
fi
}
+# Check if the file exists and has a size greater than zero
+test_file_not_empty () {
+ if ! test -s "$1"
+ then
+ echo "'$1' is not a non-empty file."
+ false
+ fi
+}
+
test_path_is_missing () {
if test -e "$1"
then
. "$GIT_BUILD_DIR"/GIT-BUILD-OPTIONS
export PERL_PATH SHELL_PATH
+# Disallow the use of abbreviated options in the test suite by default
+if test -z "${GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS}"
+then
+ GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS=true
+ export GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS
+fi
+
################################################################
# It appears that people try to run tests without building...
"${GIT_TEST_INSTALLED:-$GIT_BUILD_DIR}/git$X" >/dev/null
--stress)
stress=t ;;
--stress=*)
+ echo "error: --stress does not accept an argument: '$opt'" >&2
+ echo "did you mean --stress-jobs=${opt#*=} or --stress-limit=${opt#*=}?" >&2
+ exit 1
+ ;;
+ --stress-jobs=*)
+ stress=t;
stress=${opt#--*=}
case "$stress" in
*[!0-9]*|0*|"")
- echo "error: --stress=<N> requires the number of jobs to run" >&2
+ echo "error: --stress-jobs=<N> requires the number of jobs to run" >&2
exit 1
;;
*) # Good.
esac
;;
--stress-limit=*)
+ stress=t;
stress_limit=${opt#--*=}
case "$stress_limit" in
*[!0-9]*|0*|"")
}
void trace2_region_enter_fl(const char *file, int line, const char *category,
- const char *label, const struct repository *repo)
+ const char *label, const struct repository *repo, ...)
{
+ va_list ap;
+ va_start(ap, repo);
trace2_region_enter_printf_va_fl(file, line, category, label, repo,
- NULL, NULL);
+ NULL, ap);
+ va_end(ap);
+
}
void trace2_region_enter_printf_fl(const char *file, int line,
}
void trace2_region_leave_fl(const char *file, int line, const char *category,
- const char *label, const struct repository *repo)
+ const char *label, const struct repository *repo, ...)
{
+ va_list ap;
+ va_start(ap, repo);
trace2_region_leave_printf_va_fl(file, line, category, label, repo,
- NULL, NULL);
+ NULL, ap);
+ va_end(ap);
}
void trace2_region_leave_printf_fl(const char *file, int line,
* on this thread.
*/
void trace2_region_enter_fl(const char *file, int line, const char *category,
- const char *label, const struct repository *repo);
+ const char *label, const struct repository *repo, ...);
#define trace2_region_enter(category, label, repo) \
trace2_region_enter_fl(__FILE__, __LINE__, (category), (label), (repo))
* in this nesting level.
*/
void trace2_region_leave_fl(const char *file, int line, const char *category,
- const char *label, const struct repository *repo);
+ const char *label, const struct repository *repo, ...);
#define trace2_region_leave(category, label, repo) \
trace2_region_leave_fl(__FILE__, __LINE__, (category), (label), (repo))
static void maybe_add_string_va(struct json_writer *jw, const char *field_name,
const char *fmt, va_list ap)
{
- if (fmt && *fmt && ap) {
+ if (fmt && *fmt) {
va_list copy_ap;
struct strbuf buf = STRBUF_INIT;
static void maybe_append_string_va(struct strbuf *buf, const char *fmt,
va_list ap)
{
- if (fmt && *fmt && ap) {
+ if (fmt && *fmt) {
va_list copy_ap;
va_copy(copy_ap, ap);
static void maybe_append_string_va(struct strbuf *buf, const char *fmt,
va_list ap)
{
- if (fmt && *fmt && ap) {
+ if (fmt && *fmt) {
va_list copy_ap;
va_copy(copy_ap, ap);
{ 0x0E34, 0x0E3A },
{ 0x0E47, 0x0E4E },
{ 0x0EB1, 0x0EB1 },
-{ 0x0EB4, 0x0EB9 },
-{ 0x0EBB, 0x0EBC },
+{ 0x0EB4, 0x0EBC },
{ 0x0EC8, 0x0ECD },
{ 0x0F18, 0x0F19 },
{ 0x0F35, 0x0F35 },
{ 0xA980, 0xA982 },
{ 0xA9B3, 0xA9B3 },
{ 0xA9B6, 0xA9B9 },
-{ 0xA9BC, 0xA9BC },
+{ 0xA9BC, 0xA9BD },
{ 0xA9E5, 0xA9E5 },
{ 0xAA29, 0xAA2E },
{ 0xAA31, 0xAA32 },
{ 0x11727, 0x1172B },
{ 0x1182F, 0x11837 },
{ 0x11839, 0x1183A },
+{ 0x119D4, 0x119D7 },
+{ 0x119DA, 0x119DB },
+{ 0x119E0, 0x119E0 },
{ 0x11A01, 0x11A0A },
{ 0x11A33, 0x11A38 },
{ 0x11A3B, 0x11A3E },
{ 0x11D95, 0x11D95 },
{ 0x11D97, 0x11D97 },
{ 0x11EF3, 0x11EF4 },
+{ 0x13430, 0x13438 },
{ 0x16AF0, 0x16AF4 },
{ 0x16B30, 0x16B36 },
+{ 0x16F4F, 0x16F4F },
{ 0x16F8F, 0x16F92 },
{ 0x1BC9D, 0x1BC9E },
{ 0x1BCA0, 0x1BCA3 },
{ 0x1E01B, 0x1E021 },
{ 0x1E023, 0x1E024 },
{ 0x1E026, 0x1E02A },
+{ 0x1E130, 0x1E136 },
+{ 0x1E2EC, 0x1E2EF },
{ 0x1E8D0, 0x1E8D6 },
{ 0x1E944, 0x1E94A },
{ 0xE0001, 0xE0001 },
{ 0xFE68, 0xFE6B },
{ 0xFF01, 0xFF60 },
{ 0xFFE0, 0xFFE6 },
-{ 0x16FE0, 0x16FE1 },
-{ 0x17000, 0x187F1 },
+{ 0x16FE0, 0x16FE3 },
+{ 0x17000, 0x187F7 },
{ 0x18800, 0x18AF2 },
{ 0x1B000, 0x1B11E },
+{ 0x1B150, 0x1B152 },
+{ 0x1B164, 0x1B167 },
{ 0x1B170, 0x1B2FB },
{ 0x1F004, 0x1F004 },
{ 0x1F0CF, 0x1F0CF },
{ 0x1F680, 0x1F6C5 },
{ 0x1F6CC, 0x1F6CC },
{ 0x1F6D0, 0x1F6D2 },
+{ 0x1F6D5, 0x1F6D5 },
{ 0x1F6EB, 0x1F6EC },
-{ 0x1F6F4, 0x1F6F9 },
-{ 0x1F910, 0x1F93E },
-{ 0x1F940, 0x1F970 },
+{ 0x1F6F4, 0x1F6FA },
+{ 0x1F7E0, 0x1F7EB },
+{ 0x1F90D, 0x1F971 },
{ 0x1F973, 0x1F976 },
-{ 0x1F97A, 0x1F97A },
-{ 0x1F97C, 0x1F9A2 },
-{ 0x1F9B0, 0x1F9B9 },
-{ 0x1F9C0, 0x1F9C2 },
-{ 0x1F9D0, 0x1F9FF },
+{ 0x1F97A, 0x1F9A2 },
+{ 0x1F9A5, 0x1F9AA },
+{ 0x1F9AE, 0x1F9CA },
+{ 0x1F9CD, 0x1F9FF },
+{ 0x1FA70, 0x1FA73 },
+{ 0x1FA78, 0x1FA7A },
+{ 0x1FA80, 0x1FA82 },
+{ 0x1FA90, 0x1FA95 },
{ 0x20000, 0x2FFFD },
{ 0x30000, 0x3FFFD }
};
WRITE_TREE_SILENT |
WRITE_TREE_REPAIR);
}
+
+ o->result.updated_workdir = 1;
discard_index(o->dst_index);
*o->dst_index = o->result;
} else {
if (o->update && S_ISGITLINK(old->ce_mode) &&
should_update_submodules() && !verify_uptodate(old, o))
update |= CE_UPDATE;
- add_entry(o, old, update, 0);
+ add_entry(o, old, update, CE_STAGEMASK);
return 0;
}
return merged_entry(a, old, o);
DIR *dir;
struct dirent *d;
int ret = 0;
- struct repository_format format;
+ struct repository_format format = REPOSITORY_FORMAT_INIT;
submodule_gitdir = git_pathdup_submodule(path, "%s", "");
if (!submodule_gitdir)
read_repository_format(&format, sb.buf);
if (format.version != 0) {
strbuf_release(&sb);
+ clear_repository_format(&format);
return 1;
}
+ clear_repository_format(&format);
/* Replace config by worktrees. */
strbuf_setlen(&sb, sb.len - strlen("config"));