summaryrefslogtreecommitdiff
AgeCommit message (Collapse)AuthorFilesLines
2022-02-09Merge branch 'jt/sparse-checkout-leading-dir-fix'Libravatar Junio C Hamano2-0/+9
"git sparse-checkout init" failed to write into $GIT_DIR/info directory when the repository was created without one, which has been corrected to auto-create it. * jt/sparse-checkout-leading-dir-fix: sparse-checkout: create leading directory
2022-02-09Merge branch 'en/plug-leaks-in-merge'Libravatar Junio C Hamano2-6/+10
Leakfix. * en/plug-leaks-in-merge: merge: fix memory leaks in cmd_merge() merge-ort: fix memory leak in merge_ort_internal()
2022-02-09Merge branch 'ab/config-based-hooks-2'Libravatar Junio C Hamano27-158/+522
More "config-based hooks". * ab/config-based-hooks-2: run-command: remove old run_hook_{le,ve}() hook API receive-pack: convert push-to-checkout hook to hook.h read-cache: convert post-index-change to use hook.h commit: convert {pre-commit,prepare-commit-msg} hook to hook.h git-p4: use 'git hook' to run hooks send-email: use 'git hook run' for 'sendemail-validate' git hook run: add an --ignore-missing flag hooks: convert worktree 'post-checkout' hook to hook library hooks: convert non-worktree 'post-checkout' hook to hook library merge: convert post-merge to use hook.h am: convert applypatch-msg to use hook.h rebase: convert pre-rebase to use hook.h hook API: add a run_hooks_l() wrapper am: convert {pre,post}-applypatch to use hook.h gc: use hook library for pre-auto-gc hook hook API: add a run_hooks() wrapper hook: add 'run' subcommand
2022-02-09Merge branch 'fs/ssh-signing-crlf'Libravatar Junio C Hamano1-10/+24
The code path that verifies signatures made with ssh were made to work better on a system with CRLF line endings. * fs/ssh-signing-crlf: gpg-interface: trim CR from ssh-keygen
2022-02-09Merge branch 'jc/name-rev-stdin'Libravatar Junio C Hamano8-32/+72
"git name-rev --stdin" does not behave like usual "--stdin" at all. Start the process of renaming it to "--annotate-stdin". * jc/name-rev-stdin: name-rev.c: use strbuf_getline instead of limited size buffer name-rev: deprecate --stdin in favor of --annotate-stdin
2022-02-09Merge branch 'gc/fetch-negotiate-only-early-return'Libravatar Junio C Hamano4-3/+63
"git fetch --negotiate-only" is an internal command used by "git push" to figure out which part of our history is missing from the other side. It should never recurse into submodules even when fetch.recursesubmodules configuration variable is set, nor it should trigger "gc". The code has been tightened up to ensure it only does common ancestry discovery and nothing else. * gc/fetch-negotiate-only-early-return: fetch: help translators by reusing the same message template fetch --negotiate-only: do not update submodules fetch: skip tasks related to fetching objects fetch: use goto cleanup in cmd_fetch()
2022-02-09Merge branch 'pw/add-p-hunk-split-fix'Libravatar Junio C Hamano2-12/+56
"git add -p" rewritten in C regressed hunk splitting in some cases, which has been corrected. * pw/add-p-hunk-split-fix: builtin add -p: fix hunk splitting t3701: clean up hunk splitting tests
2022-02-09Merge branch 'tl/doc-cli-options-first'Libravatar Junio C Hamano1-5/+14
We explain that revs come first before the pathspec among command line arguments, but did not spell out that dashed options come before other args, which has been corrected. * tl/doc-cli-options-first: git-cli.txt: clarify "options first and then args"
2022-02-09Merge branch 'po/readme-mention-contributor-hints'Libravatar Junio C Hamano1-3/+11
Doc update. * po/readme-mention-contributor-hints: README.md: add CodingGuidelines and a link for Translators
2022-02-09Merge branch 'jt/conditional-config-on-remote-url'Libravatar Junio C Hamano4-41/+290
The conditional inclusion mechanism of configuration files using "[includeIf <condition>]" learns to base its decision on the URL of the remote repository the repository interacts with. * jt/conditional-config-on-remote-url: config: include file if remote URL matches a glob config: make git_config_include() static
2022-02-09Merge branch 'en/merge-ort-restart-optim-fix'Libravatar Junio C Hamano2-0/+71
The merge-ort misbehaved when merge.renameLimit configuration is set too low and failed to find all renames. * en/merge-ort-restart-optim-fix: merge-ort: avoid assuming all renames detected
2022-02-09Merge branch 'js/test-unset-trace2-parents'Libravatar Junio C Hamano1-0/+2
Avoid tests that are run under GIT_TRACE2 set from failing unnecessarily. * js/test-unset-trace2-parents: test-lib: unset trace2 parent envvars
2022-02-05The first batchLibravatar Junio C Hamano1-3/+23
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-02-05Merge branch 'ms/update-index-racy'Libravatar Junio C Hamano6-6/+135
"git update-index --refresh" has been taught to deal better with racy timestamps (just like "git status" already does). * ms/update-index-racy: update-index: refresh should rewrite index in case of racy timestamps t7508: add tests capturing racy timestamp handling t7508: fix bogus mtime verification test-lib: introduce API for verifying file mtime
2022-02-05Merge branch 'jc/reflog-parse-options'Libravatar Junio C Hamano1-79/+97
Use the parse-options API in "git reflog" command. * jc/reflog-parse-options: builtin/reflog.c: use parse-options api for expire, delete subcommands
2022-02-05Merge branch 'ab/cat-file'Libravatar Junio C Hamano9-80/+282
Assorted updates to "git cat-file", especially "-h". * ab/cat-file: cat-file: s/_/-/ in typo'd usage_msg_optf() message cat-file: don't whitespace-pad "(...)" in SYNOPSIS and usage output cat-file: use GET_OID_ONLY_TO_DIE in --(textconv|filters) object-name.c: don't have GET_OID_ONLY_TO_DIE imply *_QUIETLY cat-file: correct and improve usage information cat-file: fix remaining usage bugs cat-file: make --batch-all-objects a CMDMODE cat-file: move "usage" variable to cmd_cat_file() cat-file docs: fix SYNOPSIS and "-h" output parse-options API: add a usage_msg_optf() cat-file tests: test messaging on bad objects/paths cat-file tests: test bad usage
2022-02-05Merge branch 'jc/qsort-s-alignment-fix'Libravatar Junio C Hamano2-21/+9
Fix a hand-rolled alloca() imitation that may have violated alignment requirement of data being sorted in compatibility implementation of qsort_s() and stable qsort(). * jc/qsort-s-alignment-fix: stable-qsort: avoid using potentially unaligned access compat/qsort_s.c: avoid using potentially unaligned access
2022-02-05Merge branch 'rs/apply-symlinks-use-strset'Libravatar Junio C Hamano2-49/+19
"git apply" (ab)used the util pointer of the string-list to keep track of how each symbolic link needs to be handled, which has been simplified by using strset. * rs/apply-symlinks-use-strset: apply: use strsets to track symlinks
2022-02-05Merge branch 'rs/grep-expr-cleanup'Libravatar Junio C Hamano1-34/+36
Code clean-up. * rs/grep-expr-cleanup: grep: use grep_and_expr() in compile_pattern_and() grep: extract grep_binexp() from grep_or_expr() grep: use grep_not_expr() in compile_pattern_not() grep: use grep_or_expr() in compile_pattern_or()
2022-02-05Merge branch 'jh/p4-spawning-external-commands-cleanup'Libravatar Junio C Hamano1-97/+79
* jh/p4-spawning-external-commands-cleanup: git-p4: don't print shell commands as python lists git-p4: pass command arguments as lists instead of using shell git-p4: don't select shell mode using the type of the command argument
2022-02-05Merge branch 'jh/p4-fix-use-of-process-error-exception'Libravatar Junio C Hamano1-3/+3
* jh/p4-fix-use-of-process-error-exception: git-p4: fix instantiation of CalledProcessError
2022-02-05Merge branch 'jc/find-header'Libravatar Junio C Hamano3-25/+29
Code clean-up. * jc/find-header: receive-pack.c: consolidate find header logic
2022-02-05Merge branch 'pb/pull-rebase-autostash-fix'Libravatar Junio C Hamano2-4/+16
"git pull --rebase" ignored the rebase.autostash configuration variable when the remote history is a descendant of our history, which has been corrected. * pb/pull-rebase-autostash-fix: pull --rebase: honor rebase.autostash when fast-forwarding
2022-01-28Sync with Git 2.35.1Libravatar Junio C Hamano1-0/+6
2022-01-28Name the next one 2.36 to prepare for 2.35.1Libravatar Junio C Hamano2-1/+35
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-01-28Git 2.35.1Libravatar Junio C Hamano3-2/+8
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-01-28Merge branch 'en/keep-cwd' into maintLibravatar Junio C Hamano3-2/+30
Fix a regression in 2.35 that roke the use of "rebase" and "stash" in a secondary worktree. * en/keep-cwd: sequencer, stash: fix running from worktree subdir
2022-01-26Merge branch 'en/keep-cwd'Libravatar Junio C Hamano3-2/+30
Fix a regression in 2.35 that roke the use of "rebase" and "stash" in a secondary worktree. * en/keep-cwd: sequencer, stash: fix running from worktree subdir
2022-01-26Start post 2.35 cycleLibravatar Junio C Hamano1-1/+1
The tree is not open for new development yet, but let's mark the beginning of the new cycle before we start merging down regression fix topics. Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-01-26sequencer, stash: fix running from worktree subdirLibravatar Elijah Newren3-2/+30
In commits bc3ae46b42 ("rebase: do not attempt to remove startup_info->original_cwd", 2021-12-09) and 0fce211ccc ("stash: do not attempt to remove startup_info->original_cwd", 2021-12-09), we wanted to allow the subprocess to know which directory the parent process was running from, so that the subprocess could protect it. However... When run from a non-main worktree, setup_git_directory() will note that the discovered git directory (/PATH/TO/.git/worktree/non-main-worktree) does not match DEFAULT_GIT_DIR_ENVIRONMENT (see setup_discovered_git_dir()), and decide to set GIT_DIR in the environment. This matters because... Whenever git is run with the GIT_DIR environment variable set, and GIT_WORK_TREE not set, it presumes that '.' is the working tree. So... This combination results in the subcommand being very confused about the working tree. Fix it by also setting the GIT_WORK_TREE environment variable along with setting cmd.dir. A possibly more involved fix we could consider for later would be to make setup.c set GIT_WORK_TREE whenever (a) it discovers both the git directory and the working tree and (b) it decides to set GIT_DIR in the environment. I did not attempt that here as such would be too big of a change for a 2.35.1 release. Test-case-by: Glen Choo <chooglen@google.com> Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-01-24Git 2.35Libravatar Junio C Hamano2-1/+11
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-01-24Merge branch 'ab/checkout-branch-info-leakfix'Libravatar Junio C Hamano2-3/+13
We added an unrelated sanity checking that leads to a BUG() while plugging a leak, which triggered in a repository with symrefs in the local branch namespace that point at a ref outside. Partially revert the change to avoid triggering the BUG(). * ab/checkout-branch-info-leakfix: checkout: avoid BUG() when hitting a broken repository
2022-01-24Merge tag 'l10n-2.35.0-rnd2' of git://github.com/git-l10n/git-poLibravatar Junio C Hamano11-39598/+36951
l10n-2.35.0-rnd2 * tag 'l10n-2.35.0-rnd2' of git://github.com/git-l10n/git-po: l10n: Update Catalan translation l10n: zh_TW: v2.35.0 round 2 (0 untranslated) l10n: Update Catalan translation l10n: de.po: Update German translation l10n: de.po: Fix translation for "'%s' is aliased to '%s'" l10n: po-id for 2.35 (round 2) l10n: Update Catalan translation l10n: vi(5195t): Update for v2.35.0 round 2 l10n: batch update to fix typo in branch.c l10n: git.pot: v2.35.0 round 2 (1 new, 1 removed) l10n: bg.po: Updated Bulgarian translation (5195t) l10n: zh_CN: v2.35.0 round 1 l10n: fr: v2.35.0 round 1 l10n: zh_TW: v2.35.0 round 1 (1 fuzzy) l10n: po-id for 2.35 (round 1) l10n: sv.po: Update Swedish translation (5196t0f0u) l10n: sv.po: Fix typo l10n: tr: v2.35.0 round 1 l10n: git.pot: v2.35.0 round 1 (126 new, 142 removed)
2022-01-23l10n: Update Catalan translationLibravatar Jordi Mas1-16/+16
Signed-off-by: Jordi Mas <jmas@softcatala.org>
2022-01-22Merge branch 'l10n/zh_TW/220113' of github.com:l10n-tw/git-poLibravatar Jiang Xin1-3115/+3462
* 'l10n/zh_TW/220113' of github.com:l10n-tw/git-po: l10n: zh_TW: v2.35.0 round 2 (0 untranslated) l10n: zh_TW: v2.35.0 round 1 (1 fuzzy)
2022-01-21checkout: avoid BUG() when hitting a broken repositoryLibravatar Junio C Hamano2-3/+13
When 9081a421 (checkout: fix "branch info" memory leaks, 2021-11-16) cleaned up existing memory leaks, we added an unrelated sanity check to ensure that a local branch is truly local and not a symref to elsewhere that dies with BUG() otherwise. This was misguided in two ways. First of all, such a tightening did not belong to a leak-fix patch. And the condition it detected was *not* a bug in our program but a problem in user data, where warning() or die() would have been more appropriate. As the condition is not fatal (the result of computing the local branch name in the code that is involved in the faulty check is only used as a textual label for the commit), let's revert the code to the original state, i.e. strip "refs/heads/" to compute the local branch name if possible, and otherwise leave it NULL. The consumer of the information in merge_working_tree() is prepared to see NULL in there and act accordingly. cf. https://bugzilla.redhat.com/show_bug.cgi?id=2042920 Reported-by: Petr Šplíchal <psplicha@redhat.com> Reported-by: Todd Zullinger <tmz@pobox.com> Helped-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-01-21merge: fix memory leaks in cmd_merge()Libravatar Elijah Newren1-1/+5
There were two commit_lists created in cmd_merge() that were only conditionally free()'d. Add a quick conditional call to free_commit_list() for each of them at the end of the function. Testing this commit against t6404 under valgrind shows that this patch fixes the following two leaks: 16 bytes in 1 blocks are definitely lost in loss record 16 of 126 at 0x484086F: malloc (vg_replace_malloc.c:380) by 0x69FFEB: do_xmalloc (wrapper.c:41) by 0x6A0073: xmalloc (wrapper.c:62) by 0x52A72D: commit_list_insert (commit.c:556) by 0x47FC93: reduce_parents (merge.c:1114) by 0x4801EE: collect_parents (merge.c:1214) by 0x480B56: cmd_merge (merge.c:1465) by 0x40686E: run_builtin (git.c:464) by 0x406C51: handle_builtin (git.c:716) by 0x406E96: run_argv (git.c:783) by 0x40730A: cmd_main (git.c:914) by 0x4E7DFA: main (common-main.c:56) 8 (16 direct, 32 indirect) bytes in 1 blocks are definitely lost in \ loss record 61 of 126 at 0x484086F: malloc (vg_replace_malloc.c:380) by 0x69FFEB: do_xmalloc (wrapper.c:41) by 0x6A0073: xmalloc (wrapper.c:62) by 0x52A72D: commit_list_insert (commit.c:556) by 0x52A8F2: commit_list_insert_by_date (commit.c:620) by 0x5270AC: get_merge_bases_many_0 (commit-reach.c:413) by 0x52716C: repo_get_merge_bases (commit-reach.c:438) by 0x480E5A: cmd_merge (merge.c:1520) by 0x40686E: run_builtin (git.c:464) by 0x406C51: handle_builtin (git.c:716) by 0x406E96: run_argv (git.c:783) by 0x40730A: cmd_main (git.c:914) There are still 3 leaks in chdir_notify_register() after this, but chdir_notify_register() has been brought up on the list before and folks were not a fan of fixing those, so I'm not touching them. Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-01-21merge-ort: fix memory leak in merge_ort_internal()Libravatar Elijah Newren1-5/+5
The documentation for merge_incore_recursive(), modelled after merge_recursive(), notes that merge_bases will be consumed (emptied) so make a copy if you need it However, in merge_ort_internal() (which merge_incore_recursive() calls), it runs merged_merge_bases = pop_commit(&merge_bases); ... for (iter = merge_bases; iter; iter = iter->next) { ... } In other words, it only consumes the *first* entry of merge_bases, and the rest it iterates through. If it iterated through all of them, the caller could be responsible for free'ing the memory. If it consumed all of them, the current documentation would be correct and the callers would need to do nothing. The current middle ground makes it impossible for callers to avoid memory leaks, since any attempt to use the merge_bases it passes in would result in a use-after-free. It turns out this part of the code was copied from merge-recursive.c, which has had the same bug for 15.5 years. However, since we are trying to keep merge-recursive.c stable as we sunset it, let's just fix the leak in in merge_ort_internal() by having it actually consume all the elements of the merge_bases commit_list. Testing this commit against t6404 (the first testcase specifically about recursive merges) under valgrind shows that this patch fixes the following leak: 32 (16 direct, 16 indirect) bytes in 1 blocks are definitely lost \ in loss record 49 of 126 at 0x484086F: malloc (vg_replace_malloc.c:380) by 0x69FFEB: do_xmalloc (wrapper.c:41) by 0x6A0073: xmalloc (wrapper.c:62) by 0x52A72D: commit_list_insert (commit.c:556) by 0x47EC86: try_merge_strategy (merge.c:751) by 0x48143B: cmd_merge (merge.c:1679) by 0x40686E: run_builtin (git.c:464) by 0x406C51: handle_builtin (git.c:716) by 0x406E96: run_argv (git.c:783) by 0x40730A: cmd_main (git.c:914) by 0x4E7DFA: main (common-main.c:56) Reported-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com> Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-01-22l10n: zh_TW: v2.35.0 round 2 (0 untranslated)Libravatar Yi-Jyun Pan1-84/+85
Used 1 translation from zh_CN. Thanks to zh_CN translation team! Signed-off-by: Yi-Jyun Pan <pan93412@gmail.com>
2022-01-21sparse-checkout: create leading directoryLibravatar Jonathan Tan2-0/+9
When creating the sparse-checkout file, Git does not create the leading directory, "$GIT_DIR/info", if it does not exist. This causes problems if the repository does not have that directory. Therefore, ensure that the leading directory is created. This is the only "open" in builtin/sparse-checkout.c that does not have a leading directory check. (The other one in write_patterns_and_update() does.) Note that the test needs to explicitly specify a template when running "git init" because the default template used in the tests has the "info/" directory included. Helped-by: Jose Lopes <jabolopes@google.com> Signed-off-by: Jonathan Tan <jonathantanmy@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-01-21l10n: Update Catalan translationLibravatar Jordi Mas1-555/+444
Signed-off-by: Jordi Mas <jmas@softcatala.org>
2022-01-20Merge branch 'js/branch-track-inherit'Libravatar Junio C Hamano4-9/+9
"git branch -h" incorrectly said "--track[=direct|inherit]", implying that "--trackinherit" is a valid option, which has been corrected. source: <3de40324bea6a1dd9bca2654721471e3809e87d8.1642538935.git.steadmon@google.com> source: <c3c26192-aee9-185a-e559-b8735139e49c@web.de> * js/branch-track-inherit: branch,checkout: fix --track documentation
2022-01-20fetch: help translators by reusing the same message templateLibravatar Junio C Hamano2-2/+3
Follow the example set by 12909b6b (i18n: turn "options are incompatible" into "cannot be used together", 2022-01-05) and use the same message string to reduce the need for translation. Reported-by: Jiang Xin <worldhello.net@gmail.com> Helped-by: Glen Choo <chooglen@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-01-20branch,checkout: fix --track documentationLibravatar René Scharfe4-9/+9
Document that the accepted variants of the --track option are --track, --track=direct, and --track=inherit. The equal sign in the latter two cannot be replaced with whitespace; in general optional arguments need to be attached firmly to their option. Put "direct" consistently before "inherit", if only for the reasons that the former is the default, explained first in the documentation, and comes before the latter alphabetically. Mention both modes in the short help so that readers don't have to look them up in the full documentation. They are literal strings and thus untranslatable. PARSE_OPT_LITERAL_ARGHELP is inferred due to the pipe and parenthesis characters, so we don't have to provide that flag explicitly. Mention that -t has the same effect as --track and --track=direct. There is no way to specify inherit mode using the short option, because short options generally don't accept optional arguments. Signed-off-by: René Scharfe <l.s.r@web.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-01-20test-lib: unset trace2 parent envvarsLibravatar Josh Steadmon1-0/+2
The trace2 subsystem can inherit certain information from parent processes via environment variables; e.g., the parent command name and session ID. This allows trace2 to note when a command is the child process of another Git process, and to adjust various pieces of output accordingly. This behavior breaks certain tests that examine trace2 output when the tests run as a child of another git process, such as in `git rebase -x "make test"`. While we could fix this by unsetting the relevant variables in the affected tests (currently t0210, t0211, t0212, and t6421), this would leave other tests vulnerable to similar breakage if new test cases are added which inspect trace2 output. So fix this in general by unsetting GIT_TRACE2_PARENT_NAME and GIT_TRACE2_PARENT_SID in test-lib.sh. Reported-by: Emily Shaffer <emilyshaffer@google.com> Helped-by: Jonathan Tan <jonathantanmy@google.com> Signed-off-by: Josh Steadmon <steadmon@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-01-20l10n: de.po: Update German translationLibravatar Matthias Rüster1-3162/+3168
Signed-off-by: Matthias Rüster <matthias.ruester@gmail.com> Reviewed-by: Ralf Thielow <ralf.thielow@gmail.com>
2022-01-20l10n: de.po: Fix translation for "'%s' is aliased to '%s'"Libravatar Jürgen Krämer1-1/+1
The German translation for "'%s' is aliased to '%s'" is incorrect. It switches the order of alias name and alias definition. A better translation would be "'%s' ist ein Alias für '%s'". (Full stop removed intentionally, because the original does not use one either.) Signed-off-by: Matthias Rüster <matthias.ruester@gmail.com>
2022-01-20Merge branch 'po-id' of github.com:bagasme/git-poLibravatar Jiang Xin1-196/+310
* 'po-id' of github.com:bagasme/git-po: l10n: po-id for 2.35 (round 2)
2022-01-19Git 2.35-rc2Libravatar Junio C Hamano1-1/+1
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-01-19getcwd(mingw): handle the case when there is no cwdLibravatar Johannes Schindelin1-0/+4
A recent upstream topic introduced checks for certain Git commands that prevent them from deleting the current working directory, introducing also a regression test that ensures that commands such as `git version` _can_ run without a current working directory. While technically not possible on Windows via the regular Win32 API, we do run the regression tests in an MSYS2 Bash which uses a POSIX emulation layer (the MSYS2/Cygwin runtime) where a really evil hack _does_ allow to delete a directory even if it is the current working directory. Therefore, Git needs to be prepared for a missing working directory, even on Windows. This issue was not noticed in upstream Git because there was no caller that tried to discover a Git directory with a deleted current working directory in the test suite. But in the microsoft/git fork, we do want to run `pre-command`/`post-command` hooks for every command, even for `git version`, which means that we make precisely such a call. The bug is not in that `pre-command`/`post-command` feature, though, but in `mingw_getcwd()` and needs to be addressed there. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>