{"thread":{"id":"46315","subject":"What's cooking in git.git (Jul 2017, #01; Wed, 5)","startedAt":"2017-07-05T22:35:59Z","lastAt":"2017-09-29T18:31:00Z","messageCount":14,"participants":["Junio C Hamano","Stefan Beller","Ævar Arnfjörð Bjarmason","Brandon Williams"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"323873","messageId":"xmqqeftuh5q7.fsf@gitster.mtv.corp.google.com","threadId":"46315","inReplyTo":null,"subject":"What's cooking in git.git (Jul 2017, #01; Wed, 5)","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-07-05T22:35:44Z","receivedAt":"2017-07-05T22:35:59Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Here are the topics that have been cooking.  Commits prefixed with\n'-' are only in 'pu' (proposed updates) while commits prefixed with\n'+' are in 'next'.  The ones marked with '.' do not appear in any of\nthe integration branches, but I am still holding onto them.\n\nYou can find the changes described here in the integration branches\nof the repositories listed at\n\n    http://git-blame.blogspot.com/p/git-public-repositories.html\n\n--------------------------------------------------\n[Graduated to \"master\"]\n\n* bw/repo-object (2017-06-23) 21 commits\n  (merged to 'next' on 2017-06-26 at ed9c0b77c3)\n + ls-files: use repository object\n + repository: enable initialization of submodules\n + submodule: convert is_submodule_initialized to work on a repository\n + submodule: add repo_read_gitmodules\n + submodule-config: store the_submodule_cache in the_repository\n + repository: add index_state to struct repo\n + config: read config from a repository object\n + path: add repo_worktree_path and strbuf_repo_worktree_path\n + path: add repo_git_path and strbuf_repo_git_path\n + path: worktree_git_path() should not use file relocation\n + path: convert do_git_path to take a 'struct repository'\n + path: convert strbuf_git_common_path to take a 'struct repository'\n + path: always pass in commondir to update_common_dir\n + path: create path.h\n + environment: store worktree in the_repository\n + environment: place key repository state in the_repository\n + repository: introduce the repository object\n + environment: remove namespace_len variable\n + setup: add comment indicating a hack\n + setup: don't perform lazy initialization of repository state\n + Merge branches 'bw/ls-files-sans-the-index' and 'bw/config-h' into bw/repo-object\n\n Introduce a \"repository\" object to eventually make it easier to\n work in multiple repositories (the primary focus is to work with\n the superproject and its submodules) in a single process.\n\n\n* cc/shared-index-permfix (2017-06-25) 3 commits\n  (merged to 'next' on 2017-06-26 at bb41584bf0)\n + t1700: make sure split-index respects core.sharedrepository\n + t1301: move modebits() to test-lib-functions.sh\n + read-cache: use shared perms when writing shared index\n\n The split index code did not honor core.sharedrepository setting\n correctly.\n\n\n* jt/unify-object-info (2017-06-26) 8 commits\n  (merged to 'next' on 2017-06-26 at 540ea81983)\n + sha1_file: refactor has_sha1_file_with_flags\n + sha1_file: do not access pack if unneeded\n + sha1_file: teach sha1_object_info_extended more flags\n + sha1_file: refactor read_object\n + sha1_file: move delta base cache code up\n + sha1_file: rename LOOKUP_REPLACE_OBJECT\n + sha1_file: rename LOOKUP_UNKNOWN_OBJECT\n + sha1_file: teach packed_object_info about typename\n\n Code clean-ups.\n\n\n* rs/sha1-name-readdir-optim (2017-06-24) 4 commits\n  (merged to 'next' on 2017-06-26 at a70587f2b9)\n + sha1_file: guard against invalid loose subdirectory numbers\n + sha1_file: let for_each_file_in_obj_subdir() handle subdir names\n + p4205: add perf test script for pretty log formats\n + sha1_name: cache readdir(3) results in find_short_object_filename()\n\n Optimize \"what are the object names already taken in an alternate\n object database?\" query that is used to derive the length of prefix\n an object name is uniquely abbreviated to.\n\n--------------------------------------------------\n[New Topics]\n\n* bc/object-id (2017-07-04) 12 commits\n - sha1_name: convert GET_SHA1* flags to GET_OID*\n - sha1_name: convert get_sha1* to get_oid*\n - Convert remaining callers of get_sha1 to get_oid.\n - builtin/verify-tag: convert to struct object_id\n - builtin/unpack-file: convert to struct object_id\n - bisect: convert bisect_checkout to struct object_id\n - builtin/update_ref: convert to struct object_id\n - sequencer: convert to struct object_id\n - remote: convert struct push_cas to struct object_id\n - submodule: convert submodule config lookup to use object_id\n - builtin/merge-tree: convert remaining caller of get_sha1 to object_id\n - builtin/fsck: convert remaining caller of get_sha1 to object_id\n\n Conversion from uchar[20] to struct object_id continues.\n\n\n* jk/reflog-walk (2017-07-05) 7 commits\n - reflog-walk: stop using fake parents\n - rev-list: check reflog_info before showing usage\n - get_revision_1(): replace do-while with an early return\n - log: do not free parents when walking reflog\n - [SQUASH LOG MESSAGE ONLY] t1414: document some reflog-walk oddities\n - t1414: document some reflog-walk oddities\n - Merge branch 'jk/reflog-walk-maint' into jk/reflog-walk\n (this branch uses jk/reflog-walk-maint.)\n\n Numerous bugs in walking of reflogs via \"log -g\" and friends have\n been fixed.\n\n\n* jk/reflog-walk-maint (2017-07-05) 1 commit\n - reflog-walk: skip over double-null oid due to HEAD rename\n (this branch is used by jk/reflog-walk.)\n\n After \"git branch --move\" of the currently checked out branch, the\n code to walk the reflog of HEAD via \"log -g\" and friends\n incorrectly stopped at the reflog entry that records the renaming\n of the branch.\n\n\n* js/t5534-rev-parse-gives-multi-line-output-fix (2017-07-05) 1 commit\n  (merged to 'next' on 2017-07-05 at 5f964c44ba)\n + t5534: fix misleading grep invocation\n\n A few tests that tried to verify the contents of push certificates\n did not use 'git rev-parse' to formulate the line to look for in\n the certificate correctly.\n\n Will merge to 'master'.\n\n\n* rs/apply-avoid-over-reading (2017-07-01) 1 commit\n  (merged to 'next' on 2017-07-05 at 35730f3a47)\n + apply: use starts_with() in gitdiff_verify_name()\n\n Code clean-up to fix possible buffer over-reading.\n\n Will merge to 'master'.\n\n\n* sb/hashmap-cleanup (2017-07-05) 10 commits\n - t/helper/test-hashmap: use custom data instead of duplicate cmp functions\n - name-hash.c: drop hashmap_cmp_fn cast\n - submodule-config.c: drop hashmap_cmp_fn cast\n - remote.c: drop hashmap_cmp_fn cast\n - patch-ids.c: drop hashmap_cmp_fn cast\n - convert/sub-process: drop cast to hashmap_cmp_fn\n - config.c: drop hashmap_cmp_fn cast\n - builtin/describe: drop hashmap_cmp_fn cast\n - builtin/difftool.c: drop hashmap_cmp_fn cast\n - attr.c: drop hashmap_cmp_fn cast\n (this branch uses sb/hashmap-customize-comparison; is tangled with sb/diff-color-move.)\n\n Many uses of comparision callback function the hashmap API uses\n cast the callback function type when registering it to\n hashmap_init(), which defeats the compile time type checking when\n the callback interface changes (e.g. gaining more parameters).\n The callback implementations have been updated to take \"void *\"\n pointers and cast them to the type they expect instead.\n\n Will wait for feedback, then merge to and cook in 'next'.\n\n\n* tb/push-to-cygwin-unc-path (2017-07-05) 1 commit\n - cygwin: allow pushing to UNC paths\n\n On Cygwin, similar to Windows, \"git push //server/share/repository\"\n ought to mean a repository on a network share that can be accessed\n locally, but this did not work correctly due to stripping the double\n slashes at the beginning.\n\n This may need to be heavily tested before it gets unleashed to the\n wild, as the change is at a fairly low-level code and would affect\n not just the code to decide if the push destination is local.  There\n may be unexpected fallouts in the path normalization.\n\n Will wait for feedback, then merge to and cook in 'next'.\n\n\n* ab/sha1dc-maint (2017-07-03) 1 commit\n  (merged to 'next' on 2017-07-05 at ac69c90b7e)\n + sha1dc: update from upstream\n (this branch is used by ab/sha1dc.)\n\n Update the sha1dc again to fix portability glitches.\n\n Will merge to 'master'.\n\n--------------------------------------------------\n[Stalled]\n\n* mg/status-in-progress-info (2017-05-10) 2 commits\n - status --short --inprogress: spell it as --in-progress\n - status: show in-progress info for short status\n\n \"git status\" learns an option to report various operations\n (e.g. \"merging\") that the user is in the middle of.\n\n cf. <xmqqmvakcdqw.fsf@gitster.mtv.corp.google.com>\n\n\n* nd/worktree-move (2017-04-20) 6 commits\n - worktree remove: new command\n - worktree move: refuse to move worktrees with submodules\n - worktree move: accept destination as directory\n - worktree move: new command\n - worktree.c: add update_worktree_location()\n - worktree.c: add validate_worktree()\n\n \"git worktree\" learned move and remove subcommands.\n\n Expecting a reroll.\n cf. <20170420101024.7593-1-pclouds@gmail.com>\n cf. <20170421145916.mknekgqzhxffu7di@sigill.intra.peff.net>\n cf. <d0e81b1e-5869-299e-f462-4d43dc997bd1@ramsayjones.plus.com>\n\n\n* xz/send-email-batch-size (2017-07-05) 1 commit\n  (merged to 'next' on 2017-07-05 at 92f3c31fbd)\n + send-email: --batch-size to work around some SMTP server limit\n\n \"git send-email\" learned to overcome some SMTP server limitation\n that does not allow many pieces of e-mails to be sent over a single\n session.\n\n Will merge to 'master'.\n\n\n* sg/clone-refspec-from-command-line-config (2017-06-16) 2 commits\n - Documentation/clone: document ignored configuration variables\n - clone: respect additional configured fetch refspecs during initial fetch\n (this branch is used by sg/remote-no-string-refspecs.)\n\n \"git clone -c var=val\" is a way to set configuration variables in\n the resulting repository, but it is more useful to also make these\n variables take effect while the initial clone is happening,\n e.g. these configuration variables could be fetch refspecs.\n\n Waiting for a response.\n cf. <20170617112228.vugswym4o4owf6wj@sigill.intra.peff.net>\n cf. <xmqqmv8zhdap.fsf@gitster.mtv.corp.google.com>\n\n\n* js/rebase-i-final (2017-06-15) 10 commits\n - rebase -i: rearrange fixup/squash lines using the rebase--helper\n - t3415: test fixup with wrapped oneline\n - rebase -i: skip unnecessary picks using the rebase--helper\n - rebase -i: check for missing commits in the rebase--helper\n - t3404: relax rebase.missingCommitsCheck tests\n - rebase -i: also expand/collapse the SHA-1s via the rebase--helper\n - rebase -i: do not invent onelines when expanding/collapsing SHA-1s\n - rebase -i: remove useless indentation\n - rebase -i: generate the script via rebase--helper\n - t3415: verify that an empty instructionFormat is handled as before\n\n The final batch to \"git rebase -i\" updates to move more code from\n the shell script to C.\n\n Expecting a reroll.\n This is at its v5.\n cf. <cover.1497444257.git.johannes.schindelin@gmx.de>\n\n--------------------------------------------------\n[Cooking]\n\n* ab/grep-lose-opt-regflags (2017-06-30) 6 commits\n  (merged to 'next' on 2017-07-05 at 375c0b92ea)\n + grep: remove redundant REG_NEWLINE when compiling fixed regex\n + grep: remove regflags from the public grep_opt API\n + grep: remove redundant and verbose re-assignments to 0\n + grep: remove redundant \"fixed\" field re-assignment to 0\n + grep: adjust a redundant grep pattern type assignment\n + grep: remove redundant double assignment to 0\n\n Code cleanup.\n\n Will merge to 'master'.\n\n\n* ks/commit-assuming-only-warning-removal (2017-06-30) 2 commits\n  (merged to 'next' on 2017-07-05 at 462a72df95)\n + commit-template: distinguish status information unconditionally\n + commit-template: remove outdated notice about explicit paths\n\n An old message shown in the commit log template was removed, as it\n has outlived its usefulness.\n\n Will merge to 'master'.\n\n\n* sb/hashmap-customize-comparison (2017-06-30) 3 commits\n - hashmap: migrate documentation from Documentation/technical into header\n - patch-ids.c: use hashmap correctly\n - hashmap.h: compare function has access to a data field\n (this branch is used by sb/diff-color-move and sb/hashmap-cleanup.)\n\n Update the hashmap API so that data to customize the behaviour of\n the comparison function can be specified at the time a hashmap is\n initialized.\n\n Will merge to 'next'.\n\n\n* sb/merge-recursive-code-cleanup (2017-06-30) 1 commit\n  (merged to 'next' on 2017-07-05 at 4228240520)\n + merge-recursive: use DIFF_XDL_SET macro\n\n Code clean-up.\n\n Will merge to 'master'.\n\n\n* ab/strbuf-addftime-tzname-boolify (2017-07-01) 2 commits\n  (merged to 'next' on 2017-07-05 at 81e6795eb3)\n + strbuf: change an always NULL/\"\" strbuf_addftime() param to bool\n + strbuf.h comment: discuss strbuf_addftime() arguments in order\n\n strbuf_addftime() is further getting tweaked.\n\n Will merge to 'master'.\n\n\n* mt/p4-parse-G-output (2017-07-05) 1 commit\n - git-p4: parse marshal output \"p4 -G\" in p4 changes\n\n Use \"p4 -G\" to make \"p4 changes\" output more Python-friendly\n to parse.\n\n Needs review/ack from git-p4 folks.\n\n\n* aw/contrib-subtree-doc-asciidoctor (2017-06-27) 1 commit\n  (merged to 'next' on 2017-06-30 at af23bd111b)\n + subtree: honour USE_ASCIIDOCTOR when set\n\n The Makefile rule in contrib/subtree for building documentation\n learned to honour USE_ASCIIDOCTOR just like the main documentation\n set does.\n\n Will merge to 'master'.\n\n\n* js/fsck-name-object (2017-06-28) 1 commit\n  (merged to 'next' on 2017-06-30 at 9a08514cf2)\n + t1450: use egrep for regexp \"alternation\"\n\n Test fix.\n\n Will merge to 'master'.\n\n\n* jc/utf8-fprintf (2017-06-28) 1 commit\n  (merged to 'next' on 2017-06-30 at a8cc490818)\n + submodule--helper: do not call utf8_fprintf() unnecessarily\n\n Code cleanup.\n\n Will merge to 'master'.\n\n\n* rs/free-and-null (2017-06-29) 1 commit\n - coccinelle: polish FREE_AND_NULL rules\n\n Code cleanup.\n\n\n* ab/wildmatch (2017-06-23) 1 commit\n - wildmatch: remove unused wildopts parameter\n\n Prepare the wildmatch API for future enhancements to allow a\n pattern that is repeatedly matched against many strings to be\n precompiled.\n\n\n* ex/deprecate-empty-pathspec-as-match-all (2017-06-23) 2 commits\n  (merged to 'next' on 2017-06-26 at d026281517)\n + pathspec: die on empty strings as pathspec\n + t0027: do not use an empty string as a pathspec element\n\n The final step to make an empty string as a pathspec element\n illegal.  We started this by first deprecating and warning a\n pathspec that has such an element in 2.11 (Nov 2016).\n\n Hopefully we can merge this down to the 'master' by the end of the\n year?  A deprecation warning period that is about 1 year does not\n sound too bad.\n\n Will cook in 'next'.\n\n\n* sb/pull-rebase-submodule (2017-06-27) 4 commits\n - builtin/fetch cleanup: always set default value for submodule recursing\n - pull: optionally rebase submodules (remote submodule changes only)\n - builtin/fetch: parse recurse-submodules-default at default options parsing\n - builtin/fetch: factor submodule recurse parsing out to submodule config\n\n \"git pull --rebase --recurse-submodules\" learns to rebase the\n branch in the submodules to an updated base.\n\n\n* pw/unquote-path-in-git-pm (2017-06-30) 4 commits\n  (merged to 'next' on 2017-07-05 at 538ab4d599)\n + t9700: add tests for Git::unquote_path()\n + Git::unquote_path(): throw an exception on bad path\n + Git::unquote_path(): handle '\\a'\n + add -i: move unquote_path() to Git.pm\n\n Code refactoring.\n\n Will merge to 'master'.\n\n\n* mh/packed-ref-store (2017-07-03) 30 commits\n  (merged to 'next' on 2017-07-05 at 6c68c603cc)\n + read_packed_refs(): die if `packed-refs` contains bogus data\n + t3210: add some tests of bogus packed-refs file contents\n + repack_without_refs(): don't lock or unlock the packed refs\n + commit_packed_refs(): remove call to `packed_refs_unlock()`\n + clear_packed_ref_cache(): don't protest if the lock is held\n + packed_refs_unlock(), packed_refs_is_locked(): new functions\n + packed_refs_lock(): report errors via a `struct strbuf *err`\n + packed_refs_lock(): function renamed from lock_packed_refs()\n + commit_packed_refs(): use a staging file separate from the lockfile\n + commit_packed_refs(): report errors rather than dying\n + packed_ref_store: make class into a subclass of `ref_store`\n + packed-backend: new module for handling packed references\n + packed_read_raw_ref(): new function, replacing `resolve_packed_ref()`\n + packed_ref_store: support iteration\n + packed_peel_ref(): new function, extracted from `files_peel_ref()`\n + repack_without_refs(): take a `packed_ref_store *` parameter\n + get_packed_ref(): take a `packed_ref_store *` parameter\n + rollback_packed_refs(): take a `packed_ref_store *` parameter\n + commit_packed_refs(): take a `packed_ref_store *` parameter\n + lock_packed_refs(): take a `packed_ref_store *` parameter\n + add_packed_ref(): take a `packed_ref_store *` parameter\n + get_packed_refs(): take a `packed_ref_store *` parameter\n + get_packed_ref_cache(): take a `packed_ref_store *` parameter\n + validate_packed_ref_cache(): take a `packed_ref_store *` parameter\n + clear_packed_ref_cache(): take a `packed_ref_store *` parameter\n + packed_ref_store: move `packed_refs_lock` member here\n + packed_ref_store: move `packed_refs_path` here\n + packed_ref_store: new struct\n + add_packed_ref(): teach function to overwrite existing refs\n + t1408: add a test of stale packed refs covered by loose refs\n\n The \"ref-store\" code reorganization continues.\n\n Will merge to 'master'.\n\n\n* sb/submodule-doc (2017-06-22) 1 commit\n - submodules: overhaul documentation\n\n Doc update.\n\n What's the status of this thing?\n\n\n* sd/branch-copy (2017-06-18) 3 commits\n - branch: add a --copy (-c) option to go with --move (-m)\n - branch: add test for -m renaming multiple config sections\n - config: create a function to format section headers\n\n \"git branch\" learned \"-c/-C\" to create and switch to a new branch\n by copying an existing one.\n\n Has a bit of interaction with mh/packed-ref-store and bw/config-h,\n so perhaps needs to wait for the former to stabilize a bit more\n and possibly rebasing on them.\n\n\n* ls/filter-process-delayed (2017-06-30) 7 commits\n  (merged to 'next' on 2017-07-05 at a35e644082)\n + convert: add \"status=delayed\" to filter process protocol\n + convert: refactor capabilities negotiation\n + convert: move multiple file filter error handling to separate function\n + convert: put the flags field before the flag itself for consistent style\n + t0021: write \"OUT <size>\" only on success\n + t0021: make debug log file name configurable\n + t0021: keep filter log files on comparison\n\n The filter-process interface learned to allow a process with long\n latency give a \"delayed\" response.\n\n Will merge to 'master'.\n\n\n* ab/sha1dc (2017-07-03) 2 commits\n - sha1collisiondetection: automatically enable when submodule is populated\n - sha1dc: optionally use sha1collisiondetection as a submodule\n (this branch uses ab/sha1dc-maint.)\n\n The \"collission-detecting\" implementation of SHA-1 hash we borrowed\n from is replaced by directly binding the upstream project as our\n submodule.  Glitches on minority platforms are still being worked out.\n\n Will merge to 'next'.\n\n\n* bp/fsmonitor (2017-06-12) 6 commits\n - fsmonitor: add a sample query-fsmonitor hook script for Watchman\n - fsmonitor: add documentation for the fsmonitor extension.\n - fsmonitor: add test cases for fsmonitor extension\n - fsmonitor: teach git to optionally utilize a file system monitor to speed up detecting new or changed files.\n - dir: make lookup_untracked() available outside of dir.c\n - bswap: add 64 bit endianness helper get_be64\n\n We learned to talk to watchman to speed up \"git status\".\n\n No more comments or updates?\n\n\n* sb/diff-color-move (2017-06-30) 26 commits\n - diff: document the new --color-moved setting\n - diff.c: add dimming to moved line detection\n - diff.c: color moved lines differently, plain mode\n - diff.c: color moved lines differently\n - diff.c: buffer all output if asked to\n - diff.c: emit_diff_symbol learns about DIFF_SYMBOL_SUMMARY\n - diff.c: emit_diff_symbol learns about DIFF_SYMBOL_STAT_SEP\n - diff.c: convert word diffing to use emit_diff_symbol\n - diff.c: convert show_stats to use emit_diff_symbol\n - diff.c: convert emit_binary_diff_body to use emit_diff_symbol\n - submodule.c: migrate diff output to use emit_diff_symbol\n - diff.c: emit_diff_symbol learns DIFF_SYMBOL_REWRITE_DIFF\n - diff.c: emit_diff_symbol learns about DIFF_SYMBOL_BINARY_FILES\n - diff.c: emit_diff_symbol learns DIFF_SYMBOL_HEADER\n - diff.c: emit_diff_symbol learns DIFF_SYMBOL_FILEPAIR_{PLUS, MINUS}\n - diff.c: emit_diff_symbol learns DIFF_SYMBOL_CONTEXT_INCOMPLETE\n - diff.c: emit_diff_symbol learns DIFF_SYMBOL_WORDS[_PORCELAIN]\n - diff.c: migrate emit_line_checked to use emit_diff_symbol\n - diff.c: emit_diff_symbol learns DIFF_SYMBOL_NO_LF_EOF\n - diff.c: emit_diff_symbol learns DIFF_SYMBOL_CONTEXT_FRAGINFO\n - diff.c: emit_diff_symbol learns DIFF_SYMBOL_CONTEXT_MARKER\n - diff.c: introduce emit_diff_symbol\n - diff.c: factor out diff_flush_patch_all_file_pairs\n - diff.c: move line ending check into emit_hunk_header\n - diff.c: readability fix\n - Merge branch 'sb/hashmap-customize-comparison' into sb/diff-color-move\n (this branch uses sb/hashmap-customize-comparison; is tangled with sb/hashmap-cleanup.)\n\n \"git diff\" has been taught to optionally paint new lines that are\n the same as deleted lines elsewhere differently from genuinely new\n lines.\n\n Will merge to 'next'.\n\n--------------------------------------------------\n[Discarded]\n\n* mh/packed-ref-store-prep-extra (2017-06-18) 1 commit\n . prefix_ref_iterator_advance(): relax the check of trim length\n\n Split out of mh/packed-ref-store-prep.\n\n\n* nd/prune-in-worktree (2017-04-24) 12 commits\n . rev-list: expose and document --single-worktree\n . revision.c: --reflog add HEAD reflog from all worktrees\n . files-backend: make reflog iterator go through per-worktree reflog\n . revision.c: --all adds HEAD from all worktrees\n . refs: remove dead for_each_*_submodule()\n . revision.c: use refs_for_each*() instead of for_each_*_submodule()\n . refs: add refs_head_ref()\n . refs: move submodule slash stripping code to get_submodule_ref_store\n . refs.c: refactor get_submodule_ref_store(), share common free block\n . revision.c: --indexed-objects add objects from all worktrees\n . revision.c: refactor add_index_objects_to_pending()\n . revision.h: new flag in struct rev_info wrt. worktree-related refs\n\n \"git gc\" and friends when multiple worktrees are used off of a\n single repository did not consider the index and per-worktree refs\n of other worktrees as the root for reachability traversal, making\n objects that are in use only in other worktrees to be subject to\n garbage collection.\n\n\n* ab/free-and-null (2017-06-28) 2 commits\n  (merged to 'next' on 2017-06-28 at 1cf373c61a)\n + Revert \"coccinelle: add a rule to make \"expression\" code use FREE_AND_NULL()\"\n  (merged to 'next' on 2017-06-28 at 1785f88122)\n + coccinelle: add a rule to make \"expression\" code use FREE_AND_NULL()\n\n The coccinelle rule for free-and-null refactoring got simplified.\n"},{"id":"323875","messageId":"CAGZ79kamhPAQrE3GBC=fG=PNNtdHj7iStsqjOojkVMpNy_VEWw@mail.gmail.com","threadId":"46315","inReplyTo":"xmqqeftuh5q7.fsf@gitster.mtv.corp.google.com","subject":"Re: What's cooking in git.git (Jul 2017, #01; Wed, 5)","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2017-07-05T23:06:26Z","receivedAt":"2017-07-05T23:06:37Z","isPatch":false,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"> [Graduated to \"master\"]\n>\n> * bw/repo-object (2017-06-23) 21 commits\n...\n>\n>  Introduce a \"repository\" object to eventually make it easier to\n>  work in multiple repositories (the primary focus is to work with\n>  the superproject and its submodules) in a single process.\n\nIt's pretty rad to see this advancing to master.\nFYI: I started working on teaching the object store how to work\nwith repository objects. This would allow us to get rid of hacks in\nsubmodule.c: namely add_submodule_odb, which adds submodule\nobjects to the (main) object store for processing. Ideally we want\nto free the objects of a submodule once we are done with a submodule.\n(or integrate it into our try_to_free_routine)\n\n> * sb/hashmap-cleanup (2017-07-05) 10 commits\n...\n>  Will wait for feedback, then merge to and cook in 'next'.\n\nThanks.\n\n> * sb/pull-rebase-submodule (2017-06-27) 4 commits\n>  - builtin/fetch cleanup: always set default value for submodule recursing\n>  - pull: optionally rebase submodules (remote submodule changes only)\n>  - builtin/fetch: parse recurse-submodules-default at default options parsing\n>  - builtin/fetch: factor submodule recurse parsing out to submodule config\n>\n>  \"git pull --rebase --recurse-submodules\" learns to rebase the\n>  branch in the submodules to an updated base.\n\nSpeaking of submodules, It's not just features, but I also send bug fixes. ;)\nhttps://public-inbox.org/git/20170630003851.17288-1-sbeller@google.com/\n(That patch is not related to this series, except for working in the submodule\narea, but I consider that patch more important than e.g. this series.)\n\n> * sb/submodule-doc (2017-06-22) 1 commit\n>  - submodules: overhaul documentation\n>\n>  Doc update.\n>\n>  What's the status of this thing?\n\nThere was some review on the list (mostly from Brandon and Jonathan T.),\nbut I felt like it was bikeshedding, as there is no black/white correctness\nwith words. (Same for code, but for code it is easier to come to a\nconsensus at least.)\n\nSo I had a couple of internal rounds with them on a Google doc, hence\nI assume they agree on this patch being ok as-is.  But it has been a while\nI can reread it myself to check. But I guess most valuable input\nwould come from others.\n\n> * sb/diff-color-move (2017-06-30) 26 commits\n...\n>  Will merge to 'next'.\n\ncool. Let's see how a larger audience reacts to this one. Maybe there\nis more input for a good heuristic.\n\nThanks,\nStefan\n"},{"id":"323876","messageId":"20170705231454.15666-1-avarab@gmail.com","threadId":"46315","inReplyTo":"xmqqeftuh5q7.fsf@gitster.mtv.corp.google.com","subject":"[PATCH v2 0/3] branch: add a --copy to go with --move","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2017-07-05T23:14:51Z","receivedAt":"2017-07-05T23:15:32Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"On Wed, Jul 05 2017, Junio C. Hamano jotted:\n\n> * sd/branch-copy (2017-06-18) 3 commits\n>  - branch: add a --copy (-c) option to go with --move (-m)\n>  - branch: add test for -m renaming multiple config sections\n>  - config: create a function to format section headers\n>\n>  \"git branch\" learned \"-c/-C\" to create and switch to a new branch\n>  by copying an existing one.\n>\n>  Has a bit of interaction with mh/packed-ref-store and bw/config-h,\n>  so perhaps needs to wait for the former to stabilize a bit more\n>  and possibly rebasing on them.\n\nNow that bw/config-h has landed in master here's a version that's\nrebased on that. No changes from v1 except:\n\n - moving the new config header addition from cache.h to config.h,\n   corresponding to what was done in bw/config-h.\n\n - fixing a trivial comment whitespace issue which I see you applied\n   locally.\n\nEven though this modifies some of the same files as\nmh/packed-ref-store it looks to me like this doesn't conflict with\nthat topic in any meaningful way, but I may be missing something. I\ncan't get a merge between this & gitster/mh/packed-ref-store\ncompiling, but that's due to issues in the latter which seem to be\nfixed by some subsequent merge/fixup in pu, not something to do with a\ngenuine conflict with this topic.\n\nHopefully this'll allow this topic to land in 2.14.\n\nSahil Dua (2):\n  config: create a function to format section headers\n  branch: add a --copy (-c) option to go with --move (-m)\n\nÆvar Arnfjörð Bjarmason (1):\n  branch: add test for -m renaming multiple config sections\n\n Documentation/git-branch.txt |  14 ++-\n builtin/branch.c             |  67 ++++++++---\n config.c                     | 115 +++++++++++++++----\n config.h                     |   2 +\n refs.c                       |  11 ++\n refs.h                       |   9 +-\n refs/files-backend.c         |  46 ++++++--\n refs/refs-internal.h         |   4 +\n t/t3200-branch.sh            | 256 +++++++++++++++++++++++++++++++++++++++++++\n 9 files changed, 476 insertions(+), 48 deletions(-)\n\n-- \n2.13.1.611.g7e3b11ae1\n\n"},{"id":"323877","messageId":"20170705231454.15666-2-avarab@gmail.com","threadId":"46315","inReplyTo":"20170705231454.15666-1-avarab@gmail.com","subject":"[PATCH v2 1/3] config: create a function to format section headers","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2017-07-05T23:14:52Z","receivedAt":"2017-07-05T23:15:35Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"From: Sahil Dua <sahildua2305@gmail.com>\n\nFactor out the logic which creates section headers in the config file,\ne.g. the 'branch.foo' key will be turned into '[branch \"foo\"]'.\n\nThis introduces no function changes, but is needed for a later change\nwhich adds support for copying branch sections in the config file.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\nSigned-off-by: Ramsay Jones <ramsay@ramsayjones.plus.com>\nSigned-off-by: Sahil Dua <sahildua2305@gmail.com>\n---\n config.c | 13 +++++++++++--\n 1 file changed, 11 insertions(+), 2 deletions(-)\n\ndiff --git a/config.c b/config.c\nindex 4638b0696a..b7afb5941b 100644\n--- a/config.c\n+++ b/config.c\n@@ -2244,10 +2244,10 @@ static int write_error(const char *filename)\n \treturn 4;\n }\n \n-static int store_write_section(int fd, const char *key)\n+static struct strbuf store_create_section(const char *key)\n {\n \tconst char *dot;\n-\tint i, success;\n+\tint i;\n \tstruct strbuf sb = STRBUF_INIT;\n \n \tdot = memchr(key, '.', store.baselen);\n@@ -2263,6 +2263,15 @@ static int store_write_section(int fd, const char *key)\n \t\tstrbuf_addf(&sb, \"[%.*s]\\n\", store.baselen, key);\n \t}\n \n+\treturn sb;\n+}\n+\n+static int store_write_section(int fd, const char *key)\n+{\n+\tint success;\n+\n+\tstruct strbuf sb = store_create_section(key);\n+\n \tsuccess = write_in_full(fd, sb.buf, sb.len) == sb.len;\n \tstrbuf_release(&sb);\n \n-- \n2.13.1.611.g7e3b11ae1\n\n"},{"id":"323878","messageId":"20170705231454.15666-3-avarab@gmail.com","threadId":"46315","inReplyTo":"20170705231454.15666-1-avarab@gmail.com","subject":"[PATCH v2 2/3] branch: add test for -m renaming multiple config sections","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2017-07-05T23:14:53Z","receivedAt":"2017-07-05T23:15:36Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a test for how 'git branch -m' handles the renaming of multiple\nconfig sections existing for one branch.\n\nThe config format we use is hybrid machine/human editable, and we do\nour best to preserve the likes of comments and formatting when editing\nthe file with git-config.\n\nThis adds a test for the currently expected semantics in the face of\nsome rather obscure edge cases which are unlikely to occur in\npractice.\n\nHelped-by: Sahil Dua <sahildua2305@gmail.com>\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\nSigned-off-by: Sahil Dua <sahildua2305@gmail.com>\n---\n t/t3200-branch.sh | 41 +++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 41 insertions(+)\n\ndiff --git a/t/t3200-branch.sh b/t/t3200-branch.sh\nindex 48d152b9a9..596fbc8483 100755\n--- a/t/t3200-branch.sh\n+++ b/t/t3200-branch.sh\n@@ -358,6 +358,47 @@ test_expect_success 'config information was renamed, too' '\n \ttest_must_fail git config branch.s/s.dummy\n '\n \n+test_expect_success 'git branch -m correctly renames multiple config sections' '\n+\ttest_when_finished \"git checkout master\" &&\n+\tgit checkout -b source master &&\n+\n+\t# Assert that a config file with multiple config sections has\n+\t# those sections preserved...\n+\tcat >expect <<-\\EOF &&\n+\tbranch.dest.key1=value1\n+\tsome.gar.b=age\n+\tbranch.dest.key2=value2\n+\tEOF\n+\tcat >config.branch <<\\EOF &&\n+;; Note the lack of -\\EOF above & mixed indenting here. This is\n+;; intentional, we are also testing that the formatting of copied\n+;; sections is preserved.\n+\n+;; Comment for source. Tabs\n+[branch \"source\"]\n+\t;; Comment for the source value\n+\tkey1 = value1\n+;; Comment for some.gar. Spaces\n+[some \"gar\"]\n+    ;; Comment for the some.gar value\n+    b = age\n+;; Comment for source, again. Mixed tabs/spaces.\n+[branch \"source\"]\n+    ;; Comment for the source value, again\n+\tkey2 = value2\n+EOF\n+\tcat config.branch >>.git/config &&\n+\tgit branch -m source dest &&\n+\tgit config -f .git/config -l | grep -F -e source -e dest -e some.gar >actual &&\n+\ttest_cmp expect actual &&\n+\n+\t# ...and that the comments for those sections are also\n+\t# preserved.\n+\tcat config.branch | sed \"s/\\\"source\\\"/\\\"dest\\\"/\" >expect &&\n+\tsed -n -e \"/Note the lack/,\\$p\" .git/config >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_expect_success 'deleting a symref' '\n \tgit branch target &&\n \tgit symbolic-ref refs/heads/symref refs/heads/target &&\n-- \n2.13.1.611.g7e3b11ae1\n\n"},{"id":"323879","messageId":"20170705231454.15666-4-avarab@gmail.com","threadId":"46315","inReplyTo":"20170705231454.15666-1-avarab@gmail.com","subject":"[PATCH v2 3/3] branch: add a --copy (-c) option to go with --move (-m)","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2017-07-05T23:14:54Z","receivedAt":"2017-07-05T23:15:38Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"From: Sahil Dua <sahildua2305@gmail.com>\n\nAdd the ability to --copy a branch and its reflog and configuration,\nthis uses the same underlying machinery as the --move (-m) option\nexcept the reflog and configuration is copied instead of being moved.\n\nThis is useful for e.g. copying a topic branch to a new version,\ne.g. work to work-2 after submitting the work topic to the list, while\npreserving all the tracking info and other configuration that goes\nwith the branch, and unlike --move keeping the other already-submitted\nbranch around for reference.\n\nLike --move, when the source branch is the currently checked out\nbranch the HEAD is moved to the destination branch. In the case of\n--move we don't really have a choice (other than remaining on a\ndetached HEAD) and in order to keep the functionality consistent, we\nare doing it in similar way for --copy too.\n\nThe most common usage of this feature is expected to be moving to a\nnew topic branch which is a copy of the current one, in that case\nmoving to the target branch is what the user wants, and doesn't\nunexpectedly behave differently than --move would.\n\nOne outstanding caveat of this implementation is that:\n\n    git checkout maint &&\n    git checkout master &&\n    git branch -c topic &&\n    git checkout -\n\nWill check out 'maint' instead of 'master'. This is because the @{-N}\nfeature (or its -1 shorthand \"-\") relies on HEAD reflogs created by\nthe checkout command, so in this case we'll checkout maint instead of\nmaster, as the user might expect. What to do about that is left to a\nfuture change.\n\nHelped-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\nSigned-off-by: Sahil Dua <sahildua2305@gmail.com>\n---\n Documentation/git-branch.txt |  14 ++-\n builtin/branch.c             |  67 ++++++++++----\n config.c                     | 102 ++++++++++++++++----\n config.h                     |   2 +\n refs.c                       |  11 +++\n refs.h                       |   9 +-\n refs/files-backend.c         |  46 +++++++--\n refs/refs-internal.h         |   4 +\n t/t3200-branch.sh            | 215 +++++++++++++++++++++++++++++++++++++++++++\n 9 files changed, 424 insertions(+), 46 deletions(-)\n\ndiff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt\nindex 81bd0a7b77..94fd89ddc5 100644\n--- a/Documentation/git-branch.txt\n+++ b/Documentation/git-branch.txt\n@@ -18,6 +18,7 @@ SYNOPSIS\n 'git branch' (--set-upstream-to=<upstream> | -u <upstream>) [<branchname>]\n 'git branch' --unset-upstream [<branchname>]\n 'git branch' (-m | -M) [<oldbranch>] <newbranch>\n+'git branch' (-c | -C) [<oldbranch>] <newbranch>\n 'git branch' (-d | -D) [-r] <branchname>...\n 'git branch' --edit-description [<branchname>]\n \n@@ -64,6 +65,10 @@ If <oldbranch> had a corresponding reflog, it is renamed to match\n renaming. If <newbranch> exists, -M must be used to force the rename\n to happen.\n \n+The `-c` and `-C` options have the exact same semantics as `-m` and\n+`-M`, except instead of the branch being renamed it along with its\n+config and reflog will be copied to a new name.\n+\n With a `-d` or `-D` option, `<branchname>` will be deleted.  You may\n specify more than one branch for deletion.  If the branch currently\n has a reflog then the reflog will also be deleted.\n@@ -104,7 +109,7 @@ OPTIONS\n \tIn combination with `-d` (or `--delete`), allow deleting the\n \tbranch irrespective of its merged status. In combination with\n \t`-m` (or `--move`), allow renaming the branch even if the new\n-\tbranch name already exists.\n+\tbranch name already exists, the same applies for `-c` (or `--copy`).\n \n -m::\n --move::\n@@ -113,6 +118,13 @@ OPTIONS\n -M::\n \tShortcut for `--move --force`.\n \n+-c::\n+--copy::\n+\tCopy a branch and the corresponding reflog.\n+\n+-C::\n+\tShortcut for `--copy --force`.\n+\n --color[=<when>]::\n \tColor branches to highlight current, local, and\n \tremote-tracking branches.\ndiff --git a/builtin/branch.c b/builtin/branch.c\nindex c958e93257..684f2c4f42 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -28,6 +28,7 @@ static const char * const builtin_branch_usage[] = {\n \tN_(\"git branch [<options>] [-l] [-f] <branch-name> [<start-point>]\"),\n \tN_(\"git branch [<options>] [-r] (-d | -D) <branch-name>...\"),\n \tN_(\"git branch [<options>] (-m | -M) [<old-branch>] <new-branch>\"),\n+\tN_(\"git branch [<options>] (-c | -C) [<old-branch>] <new-branch>\"),\n \tN_(\"git branch [<options>] [-r | -a] [--points-at]\"),\n \tN_(\"git branch [<options>] [-r | -a] [--format]\"),\n \tNULL\n@@ -450,15 +451,19 @@ static void reject_rebase_or_bisect_branch(const char *target)\n \tfree_worktrees(worktrees);\n }\n \n-static void rename_branch(const char *oldname, const char *newname, int force)\n+static void copy_or_rename_branch(const char *oldname, const char *newname, int copy, int force)\n {\n \tstruct strbuf oldref = STRBUF_INIT, newref = STRBUF_INIT, logmsg = STRBUF_INIT;\n \tstruct strbuf oldsection = STRBUF_INIT, newsection = STRBUF_INIT;\n \tint recovery = 0;\n \tint clobber_head_ok;\n \n-\tif (!oldname)\n-\t\tdie(_(\"cannot rename the current branch while not on any.\"));\n+\tif (!oldname) {\n+\t\tif (copy)\n+\t\t\tdie(_(\"cannot copy the current branch while not on any.\"));\n+\t\telse\n+\t\t\tdie(_(\"cannot rename the current branch while not on any.\"));\n+\t}\n \n \tif (strbuf_check_branch_ref(&oldref, oldname)) {\n \t\t/*\n@@ -481,17 +486,33 @@ static void rename_branch(const char *oldname, const char *newname, int force)\n \n \treject_rebase_or_bisect_branch(oldref.buf);\n \n-\tstrbuf_addf(&logmsg, \"Branch: renamed %s to %s\",\n-\t\t oldref.buf, newref.buf);\n+\tif (copy)\n+\t\tstrbuf_addf(&logmsg, \"Branch: copied %s to %s\",\n+\t\t\t    oldref.buf, newref.buf);\n+\telse\n+\t\tstrbuf_addf(&logmsg, \"Branch: renamed %s to %s\",\n+\t\t\t    oldref.buf, newref.buf);\n \n-\tif (rename_ref(oldref.buf, newref.buf, logmsg.buf))\n+\tif (!copy && rename_ref(oldref.buf, newref.buf, logmsg.buf))\n \t\tdie(_(\"Branch rename failed\"));\n+\tif (copy && copy_existing_ref(oldref.buf, newref.buf, logmsg.buf))\n+\t\tdie(_(\"Branch copy failed\"));\n \n-\tif (recovery)\n-\t\twarning(_(\"Renamed a misnamed branch '%s' away\"), oldref.buf + 11);\n+\tif (recovery) {\n+\t\tif (copy)\n+\t\t\twarning(_(\"Copied a misnamed branch '%s' away\"),\n+\t\t\t\toldref.buf + 11);\n+\t\telse\n+\t\t\twarning(_(\"Renamed a misnamed branch '%s' away\"),\n+\t\t\t\toldref.buf + 11);\n+\t}\n \n-\tif (replace_each_worktree_head_symref(oldref.buf, newref.buf, logmsg.buf))\n-\t\tdie(_(\"Branch renamed to %s, but HEAD is not updated!\"), newname);\n+\tif (replace_each_worktree_head_symref(oldref.buf, newref.buf, logmsg.buf)) {\n+\t\tif (copy)\n+\t\t\tdie(_(\"Branch copied to %s, but HEAD is not updated!\"), newname);\n+\t\telse\n+\t\t\tdie(_(\"Branch renamed to %s, but HEAD is not updated!\"), newname);\n+\t}\n \n \tstrbuf_release(&logmsg);\n \n@@ -499,8 +520,10 @@ static void rename_branch(const char *oldname, const char *newname, int force)\n \tstrbuf_release(&oldref);\n \tstrbuf_addf(&newsection, \"branch.%s\", newref.buf + 11);\n \tstrbuf_release(&newref);\n-\tif (git_config_rename_section(oldsection.buf, newsection.buf) < 0)\n+\tif (!copy && git_config_rename_section(oldsection.buf, newsection.buf) < 0)\n \t\tdie(_(\"Branch is renamed, but update of config-file failed\"));\n+\tif (copy && strcmp(oldname, newname) && git_config_copy_section(oldsection.buf, newsection.buf) < 0)\n+\t\tdie(_(\"Branch is copied, but update of config-file failed\"));\n \tstrbuf_release(&oldsection);\n \tstrbuf_release(&newsection);\n }\n@@ -538,7 +561,7 @@ static int edit_branch_description(const char *branch_name)\n \n int cmd_branch(int argc, const char **argv, const char *prefix)\n {\n-\tint delete = 0, rename = 0, force = 0, list = 0;\n+\tint delete = 0, rename = 0, copy = 0, force = 0, list = 0;\n \tint reflog = 0, edit_description = 0;\n \tint quiet = 0, unset_upstream = 0;\n \tconst char *new_upstream = NULL;\n@@ -575,6 +598,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix)\n \t\tOPT_BIT('D', NULL, &delete, N_(\"delete branch (even if not merged)\"), 2),\n \t\tOPT_BIT('m', \"move\", &rename, N_(\"move/rename a branch and its reflog\"), 1),\n \t\tOPT_BIT('M', NULL, &rename, N_(\"move/rename a branch, even if target exists\"), 2),\n+\t\tOPT_BIT('c', \"copy\", &copy, N_(\"copy a branch and its reflog\"), 1),\n+\t\tOPT_BIT('C', NULL, &copy, N_(\"copy a branch, even if target exists\"), 2),\n \t\tOPT_BOOL(0, \"list\", &list, N_(\"list branch names\")),\n \t\tOPT_BOOL('l', \"create-reflog\", &reflog, N_(\"create the branch's reflog\")),\n \t\tOPT_BOOL(0, \"edit-description\", &edit_description,\n@@ -618,14 +643,14 @@ int cmd_branch(int argc, const char **argv, const char *prefix)\n \targc = parse_options(argc, argv, prefix, options, builtin_branch_usage,\n \t\t\t     0);\n \n-\tif (!delete && !rename && !edit_description && !new_upstream && !unset_upstream && argc == 0)\n+\tif (!delete && !rename && !copy && !edit_description && !new_upstream && !unset_upstream && argc == 0)\n \t\tlist = 1;\n \n \tif (filter.with_commit || filter.merge != REF_FILTER_MERGED_NONE || filter.points_at.nr ||\n \t    filter.no_commit)\n \t\tlist = 1;\n \n-\tif (!!delete + !!rename + !!new_upstream +\n+\tif (!!delete + !!rename + !!copy + !!new_upstream +\n \t    list + unset_upstream > 1)\n \t\tusage_with_options(builtin_branch_usage, options);\n \n@@ -643,6 +668,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)\n \tif (force) {\n \t\tdelete *= 2;\n \t\trename *= 2;\n+\t\tcopy *= 2;\n \t}\n \n \tif (delete) {\n@@ -697,13 +723,22 @@ int cmd_branch(int argc, const char **argv, const char *prefix)\n \n \t\tif (edit_branch_description(branch_name))\n \t\t\treturn 1;\n+\t} else if (copy) {\n+\t\tif (!argc)\n+\t\t\tdie(_(\"branch name required\"));\n+\t\telse if (argc == 1)\n+\t\t\tcopy_or_rename_branch(head, argv[0], 1, copy > 1);\n+\t\telse if (argc == 2)\n+\t\t\tcopy_or_rename_branch(argv[0], argv[1], 1, copy > 1);\n+\t\telse\n+\t\t\tdie(_(\"too many branches for a copy operation\"));\n \t} else if (rename) {\n \t\tif (!argc)\n \t\t\tdie(_(\"branch name required\"));\n \t\telse if (argc == 1)\n-\t\t\trename_branch(head, argv[0], rename > 1);\n+\t\t\tcopy_or_rename_branch(head, argv[0], 0, rename > 1);\n \t\telse if (argc == 2)\n-\t\t\trename_branch(argv[0], argv[1], rename > 1);\n+\t\t\tcopy_or_rename_branch(argv[0], argv[1], 0, rename > 1);\n \t\telse\n \t\t\tdie(_(\"too many branches for a rename operation\"));\n \t} else if (new_upstream) {\ndiff --git a/config.c b/config.c\nindex b7afb5941b..9ee3fcafcf 100644\n--- a/config.c\n+++ b/config.c\n@@ -2713,8 +2713,8 @@ static int section_name_is_ok(const char *name)\n }\n \n /* if new_name == NULL, the section is removed instead */\n-int git_config_rename_section_in_file(const char *config_filename,\n-\t\t\t\t      const char *old_name, const char *new_name)\n+static int git_config_copy_or_rename_section_in_file(const char *config_filename,\n+\t\t\t\t      const char *old_name, const char *new_name, int copy)\n {\n \tint ret = 0, remove = 0;\n \tchar *filename_buf = NULL;\n@@ -2723,6 +2723,7 @@ int git_config_rename_section_in_file(const char *config_filename,\n \tchar buf[1024];\n \tFILE *config_file = NULL;\n \tstruct stat st;\n+\tstruct strbuf copystr = STRBUF_INIT;\n \n \tif (new_name && !section_name_is_ok(new_name)) {\n \t\tret = error(\"invalid section name: %s\", new_name);\n@@ -2761,12 +2762,30 @@ int git_config_rename_section_in_file(const char *config_filename,\n \twhile (fgets(buf, sizeof(buf), config_file)) {\n \t\tint i;\n \t\tint length;\n+\t\tint is_section = 0;\n \t\tchar *output = buf;\n \t\tfor (i = 0; buf[i] && isspace(buf[i]); i++)\n \t\t\t; /* do nothing */\n \t\tif (buf[i] == '[') {\n \t\t\t/* it's a section */\n-\t\t\tint offset = section_name_match(&buf[i], old_name);\n+\t\t\tint offset;\n+\t\t\tis_section = 1;\n+\n+\t\t\t/*\n+\t\t\t * When encountering a new section under -c we\n+\t\t\t * need to flush out any section we're already\n+\t\t\t * coping and begin anew. There might be\n+\t\t\t * multiple [branch \"$name\"] sections.\n+\t\t\t */\n+\t\t\tif (copystr.len > 0) {\n+\t\t\t\tif (write_in_full(out_fd, copystr.buf, copystr.len) != copystr.len) {\n+\t\t\t\t\tret = write_error(get_lock_file_path(lock));\n+\t\t\t\t\tgoto out;\n+\t\t\t\t}\n+\t\t\t\tstrbuf_reset(&copystr);\n+\t\t\t}\n+\n+\t\t\toffset = section_name_match(&buf[i], old_name);\n \t\t\tif (offset > 0) {\n \t\t\t\tret++;\n \t\t\t\tif (new_name == NULL) {\n@@ -2774,25 +2793,30 @@ int git_config_rename_section_in_file(const char *config_filename,\n \t\t\t\t\tcontinue;\n \t\t\t\t}\n \t\t\t\tstore.baselen = strlen(new_name);\n-\t\t\t\tif (!store_write_section(out_fd, new_name)) {\n-\t\t\t\t\tret = write_error(get_lock_file_path(lock));\n-\t\t\t\t\tgoto out;\n-\t\t\t\t}\n-\t\t\t\t/*\n-\t\t\t\t * We wrote out the new section, with\n-\t\t\t\t * a newline, now skip the old\n-\t\t\t\t * section's length\n-\t\t\t\t */\n-\t\t\t\toutput += offset + i;\n-\t\t\t\tif (strlen(output) > 0) {\n+\t\t\t\tif (!copy) {\n+\t\t\t\t\tif (!store_write_section(out_fd, new_name)) {\n+\t\t\t\t\t\tret = write_error(get_lock_file_path(lock));\n+\t\t\t\t\t\tgoto out;\n+\t\t\t\t\t}\n+\n \t\t\t\t\t/*\n-\t\t\t\t\t * More content means there's\n-\t\t\t\t\t * a declaration to put on the\n-\t\t\t\t\t * next line; indent with a\n-\t\t\t\t\t * tab\n+\t\t\t\t\t * We wrote out the new section, with\n+\t\t\t\t\t * a newline, now skip the old\n+\t\t\t\t\t * section's length\n \t\t\t\t\t */\n-\t\t\t\t\toutput -= 1;\n-\t\t\t\t\toutput[0] = '\\t';\n+\t\t\t\t\toutput += offset + i;\n+\t\t\t\t\tif (strlen(output) > 0) {\n+\t\t\t\t\t\t/*\n+\t\t\t\t\t\t * More content means there's\n+\t\t\t\t\t\t * a declaration to put on the\n+\t\t\t\t\t\t * next line; indent with a\n+\t\t\t\t\t\t * tab\n+\t\t\t\t\t\t */\n+\t\t\t\t\t\toutput -= 1;\n+\t\t\t\t\t\toutput[0] = '\\t';\n+\t\t\t\t\t}\n+\t\t\t\t} else {\n+\t\t\t\t\tcopystr = store_create_section(new_name);\n \t\t\t\t}\n \t\t\t}\n \t\t\tremove = 0;\n@@ -2800,11 +2824,30 @@ int git_config_rename_section_in_file(const char *config_filename,\n \t\tif (remove)\n \t\t\tcontinue;\n \t\tlength = strlen(output);\n+\n+\t\tif (!is_section && copystr.len > 0) {\n+\t\t\tstrbuf_add(&copystr, output, length);\n+\t\t}\n+\n \t\tif (write_in_full(out_fd, output, length) != length) {\n \t\t\tret = write_error(get_lock_file_path(lock));\n \t\t\tgoto out;\n \t\t}\n \t}\n+\n+\t/*\n+\t * Copy a trailing section at the end of the config, won't be\n+\t * flushed by the usual \"flush because we have a new section\n+\t * logic in the loop above.\n+\t */\n+\tif (copystr.len > 0) {\n+\t\tif (write_in_full(out_fd, copystr.buf, copystr.len) != copystr.len) {\n+\t\t\tret = write_error(get_lock_file_path(lock));\n+\t\t\tgoto out;\n+\t\t}\n+\t\tstrbuf_reset(&copystr);\n+\t}\n+\n \tfclose(config_file);\n \tconfig_file = NULL;\n commit_and_out:\n@@ -2820,11 +2863,30 @@ int git_config_rename_section_in_file(const char *config_filename,\n \treturn ret;\n }\n \n+int git_config_rename_section_in_file(const char *config_filename,\n+\t\t\t\t      const char *old_name, const char *new_name)\n+{\n+\treturn git_config_copy_or_rename_section_in_file(config_filename,\n+\t\t\t\t\t old_name, new_name, 0);\n+}\n+\n int git_config_rename_section(const char *old_name, const char *new_name)\n {\n \treturn git_config_rename_section_in_file(NULL, old_name, new_name);\n }\n \n+int git_config_copy_section_in_file(const char *config_filename,\n+\t\t\t\t      const char *old_name, const char *new_name)\n+{\n+\treturn git_config_copy_or_rename_section_in_file(config_filename,\n+\t\t\t\t\t old_name, new_name, 1);\n+}\n+\n+int git_config_copy_section(const char *old_name, const char *new_name)\n+{\n+\treturn git_config_copy_section_in_file(NULL, old_name, new_name);\n+}\n+\n /*\n  * Call this to report error for your variable that should not\n  * get a boolean value (i.e. \"[my] var\" means \"true\").\ndiff --git a/config.h b/config.h\nindex 0352da117b..e9bdecbf88 100644\n--- a/config.h\n+++ b/config.h\n@@ -71,6 +71,8 @@ extern int git_config_set_multivar_in_file_gently(const char *, const char *, co\n extern void git_config_set_multivar_in_file(const char *, const char *, const char *, const char *, int);\n extern int git_config_rename_section(const char *, const char *);\n extern int git_config_rename_section_in_file(const char *, const char *, const char *);\n+extern int git_config_copy_section(const char *, const char *);\n+extern int git_config_copy_section_in_file(const char *, const char *, const char *);\n extern const char *git_etc_gitconfig(void);\n extern int git_env_bool(const char *, int);\n extern unsigned long git_env_ulong(const char *, unsigned long);\ndiff --git a/refs.c b/refs.c\nindex 88658ba769..8a334a2ff0 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -2045,3 +2045,14 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg)\n {\n \treturn refs_rename_ref(get_main_ref_store(), oldref, newref, logmsg);\n }\n+\n+int refs_copy_existing_ref(struct ref_store *refs, const char *oldref,\n+\t\t    const char *newref, const char *logmsg)\n+{\n+\treturn refs->be->copy_ref(refs, oldref, newref, logmsg);\n+}\n+\n+int copy_existing_ref(const char *oldref, const char *newref, const char *logmsg)\n+{\n+\treturn refs_copy_existing_ref(get_main_ref_store(), oldref, newref, logmsg);\n+}\ndiff --git a/refs.h b/refs.h\nindex 6daa78eb50..80da9fe389 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -443,7 +443,14 @@ char *shorten_unambiguous_ref(const char *refname, int strict);\n /** rename ref, return 0 on success **/\n int refs_rename_ref(struct ref_store *refs, const char *oldref,\n \t\t    const char *newref, const char *logmsg);\n-int rename_ref(const char *oldref, const char *newref, const char *logmsg);\n+int rename_ref(const char *oldref, const char *newref,\n+\t\t\tconst char *logmsg);\n+\n+/** copy ref, return 0 on success **/\n+int refs_copy_existing_ref(struct ref_store *refs, const char *oldref,\n+\t\t    const char *newref, const char *logmsg);\n+int copy_existing_ref(const char *oldref, const char *newref,\n+\t\t\tconst char *logmsg);\n \n int refs_create_symref(struct ref_store *refs, const char *refname,\n \t\t       const char *target, const char *logmsg);\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 0404f2c233..000362b9d8 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -1748,9 +1748,9 @@ static int commit_ref_update(struct files_ref_store *refs,\n \t\t\t     const struct object_id *oid, const char *logmsg,\n \t\t\t     struct strbuf *err);\n \n-static int files_rename_ref(struct ref_store *ref_store,\n+static int files_copy_or_rename_ref(struct ref_store *ref_store,\n \t\t\t    const char *oldrefname, const char *newrefname,\n-\t\t\t    const char *logmsg)\n+\t\t\t    const char *logmsg, int copy)\n {\n \tstruct files_ref_store *refs =\n \t\tfiles_downcast(ref_store, REF_STORE_WRITE, \"rename_ref\");\n@@ -1782,8 +1782,12 @@ static int files_rename_ref(struct ref_store *ref_store,\n \t}\n \n \tif (flag & REF_ISSYMREF) {\n-\t\tret = error(\"refname %s is a symbolic ref, renaming it is not supported\",\n-\t\t\t    oldrefname);\n+\t\tif (copy)\n+\t\t\tret = error(\"refname %s is a symbolic ref, copying it is not supported\",\n+\t\t\t\t    oldrefname);\n+\t\telse\n+\t\t\tret = error(\"refname %s is a symbolic ref, renaming it is not supported\",\n+\t\t\t\t    oldrefname);\n \t\tgoto out;\n \t}\n \tif (!refs_rename_ref_available(&refs->base, oldrefname, newrefname)) {\n@@ -1791,13 +1795,19 @@ static int files_rename_ref(struct ref_store *ref_store,\n \t\tgoto out;\n \t}\n \n-\tif (log && rename(sb_oldref.buf, tmp_renamed_log.buf)) {\n+\tif (!copy && log && rename(sb_oldref.buf, tmp_renamed_log.buf)) {\n \t\tret = error(\"unable to move logfile logs/%s to logs/\"TMP_RENAMED_LOG\": %s\",\n \t\t\t    oldrefname, strerror(errno));\n \t\tgoto out;\n \t}\n \n-\tif (refs_delete_ref(&refs->base, logmsg, oldrefname,\n+\tif (copy && log && copy_file(tmp_renamed_log.buf, sb_oldref.buf, 0644)) {\n+\t\tret = error(\"unable to copy logfile logs/%s to logs/\"TMP_RENAMED_LOG\": %s\",\n+\t\t\t    oldrefname, strerror(errno));\n+\t\tgoto out;\n+\t}\n+\n+\tif (!copy && refs_delete_ref(&refs->base, logmsg, oldrefname,\n \t\t\t    orig_oid.hash, REF_NODEREF)) {\n \t\terror(\"unable to delete old %s\", oldrefname);\n \t\tgoto rollback;\n@@ -1810,7 +1820,7 @@ static int files_rename_ref(struct ref_store *ref_store,\n \t * the safety anyway; we want to delete the reference whatever\n \t * its current value.\n \t */\n-\tif (!refs_read_ref_full(&refs->base, newrefname,\n+\tif (!copy && !refs_read_ref_full(&refs->base, newrefname,\n \t\t\t\tRESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,\n \t\t\t\toid.hash, NULL) &&\n \t    refs_delete_ref(&refs->base, NULL, newrefname,\n@@ -1841,7 +1851,10 @@ static int files_rename_ref(struct ref_store *ref_store,\n \tlock = lock_ref_sha1_basic(refs, newrefname, NULL, NULL, NULL,\n \t\t\t\t   REF_NODEREF, NULL, &err);\n \tif (!lock) {\n-\t\terror(\"unable to rename '%s' to '%s': %s\", oldrefname, newrefname, err.buf);\n+\t\tif (copy)\n+\t\t\terror(\"unable to copy '%s' to '%s': %s\", oldrefname, newrefname, err.buf);\n+\t\telse\n+\t\t\terror(\"unable to rename '%s' to '%s': %s\", oldrefname, newrefname, err.buf);\n \t\tstrbuf_release(&err);\n \t\tgoto rollback;\n \t}\n@@ -1892,6 +1905,22 @@ static int files_rename_ref(struct ref_store *ref_store,\n \treturn ret;\n }\n \n+static int files_rename_ref(struct ref_store *ref_store,\n+\t\t\t    const char *oldrefname, const char *newrefname,\n+\t\t\t    const char *logmsg)\n+{\n+\treturn files_copy_or_rename_ref(ref_store, oldrefname,\n+\t\t\t\t newrefname, logmsg, 0);\n+}\n+\n+static int files_copy_ref(struct ref_store *ref_store,\n+\t\t\t    const char *oldrefname, const char *newrefname,\n+\t\t\t    const char *logmsg)\n+{\n+\treturn files_copy_or_rename_ref(ref_store, oldrefname,\n+\t\t\t\t newrefname, logmsg, 1);\n+}\n+\n static int close_ref(struct ref_lock *lock)\n {\n \tif (close_lock_file(lock->lk))\n@@ -3397,6 +3426,7 @@ struct ref_storage_be refs_be_files = {\n \tfiles_create_symref,\n \tfiles_delete_refs,\n \tfiles_rename_ref,\n+\tfiles_copy_ref,\n \n \tfiles_ref_iterator_begin,\n \tfiles_read_raw_ref,\ndiff --git a/refs/refs-internal.h b/refs/refs-internal.h\nindex 192f9f85c9..3c51194edb 100644\n--- a/refs/refs-internal.h\n+++ b/refs/refs-internal.h\n@@ -543,6 +543,9 @@ typedef int delete_refs_fn(struct ref_store *ref_store, const char *msg,\n typedef int rename_ref_fn(struct ref_store *ref_store,\n \t\t\t  const char *oldref, const char *newref,\n \t\t\t  const char *logmsg);\n+typedef int copy_ref_fn(struct ref_store *ref_store,\n+\t\t\t  const char *oldref, const char *newref,\n+\t\t\t  const char *logmsg);\n \n /*\n  * Iterate over the references in `ref_store` whose names start with\n@@ -641,6 +644,7 @@ struct ref_storage_be {\n \tcreate_symref_fn *create_symref;\n \tdelete_refs_fn *delete_refs;\n \trename_ref_fn *rename_ref;\n+\tcopy_ref_fn *copy_ref;\n \n \tref_iterator_begin_fn *iterator_begin;\n \tread_raw_ref_fn *read_raw_ref;\ndiff --git a/t/t3200-branch.sh b/t/t3200-branch.sh\nindex 596fbc8483..5402933877 100755\n--- a/t/t3200-branch.sh\n+++ b/t/t3200-branch.sh\n@@ -399,6 +399,221 @@ EOF\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'git branch -c dumps usage' '\n+\ttest_expect_code 128 git branch -c 2>err &&\n+\ttest_i18ngrep \"branch name required\" err\n+'\n+\n+test_expect_success 'git branch --copy dumps usage' '\n+\ttest_expect_code 128 git branch --copy 2>err &&\n+\ttest_i18ngrep \"branch name required\" err\n+'\n+\n+test_expect_success 'git branch -c d e should work' '\n+\tgit branch -l d &&\n+\tgit reflog exists refs/heads/d &&\n+\tgit config branch.d.dummy Hello &&\n+\tgit branch -c d e &&\n+\tgit reflog exists refs/heads/d &&\n+\tgit reflog exists refs/heads/e &&\n+\techo Hello >expect &&\n+\tgit config branch.e.dummy >actual &&\n+\ttest_cmp expect actual &&\n+\techo Hello >expect &&\n+\tgit config branch.d.dummy >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'git branch --copy is a synonym for -c' '\n+\tgit branch -l copy &&\n+\tgit reflog exists refs/heads/copy &&\n+\tgit config branch.copy.dummy Hello &&\n+\tgit branch --copy copy copy-to &&\n+\tgit reflog exists refs/heads/copy &&\n+\tgit reflog exists refs/heads/copy-to &&\n+\techo Hello >expect &&\n+\tgit config branch.copy.dummy >actual &&\n+\ttest_cmp expect actual &&\n+\techo Hello >expect &&\n+\tgit config branch.copy-to.dummy >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'git branch -c ee ef should copy and checkout branch ef' '\n+\tgit checkout -b ee &&\n+\tgit reflog exists refs/heads/ee &&\n+\tgit config branch.ee.dummy Hello &&\n+\tgit branch -c ee ef &&\n+\tgit reflog exists refs/heads/ee &&\n+\tgit reflog exists refs/heads/ef &&\n+\ttest $(git config branch.ee.dummy) = Hello &&\n+\ttest $(git config branch.ef.dummy) = Hello &&\n+\ttest $(git rev-parse --abbrev-ref HEAD) = ef\n+'\n+\n+test_expect_success 'git branch -c f/f g/g should work' '\n+\tgit branch -l f/f &&\n+\tgit reflog exists refs/heads/f/f &&\n+\tgit config branch.f/f.dummy Hello &&\n+\tgit branch -c f/f g/g &&\n+\tgit reflog exists refs/heads/f/f &&\n+\tgit reflog exists refs/heads/g/g &&\n+\ttest $(git config branch.f/f.dummy) = Hello &&\n+\ttest $(git config branch.g/g.dummy) = Hello\n+'\n+\n+test_expect_success 'git branch -c m2 m2 should work' '\n+\tgit branch -l m2 &&\n+\tgit reflog exists refs/heads/m2 &&\n+\tgit config branch.m2.dummy Hello &&\n+\tgit branch -c m2 m2 &&\n+\tgit reflog exists refs/heads/m2 &&\n+\ttest $(git config branch.m2.dummy) = Hello\n+'\n+\n+test_expect_success 'git branch -c zz zz/zz should fail' '\n+\tgit branch -l zz &&\n+\tgit reflog exists refs/heads/zz &&\n+\ttest_must_fail git branch -c zz zz/zz\n+'\n+\n+test_expect_success 'git branch -c b/b b should fail' '\n+\tgit branch -l b/b &&\n+\ttest_must_fail git branch -c b/b b\n+'\n+\n+test_expect_success 'git branch -C o/q o/p should work when o/p exists' '\n+\tgit branch -l o/q &&\n+\tgit reflog exists refs/heads/o/q &&\n+\tgit reflog exists refs/heads/o/p &&\n+\tgit branch -C o/q o/p\n+'\n+\n+test_expect_success 'git branch -c -f o/q o/p should work when o/p exists' '\n+\tgit reflog exists refs/heads/o/q &&\n+\tgit reflog exists refs/heads/o/p &&\n+\tgit branch -c -f o/q o/p\n+'\n+\n+test_expect_success 'git branch -c qq rr/qq should fail when r exists' '\n+\tgit branch qq &&\n+\tgit branch rr &&\n+\ttest_must_fail git branch -c qq rr/qq\n+'\n+\n+test_expect_success 'git branch -C b1 b2 should fail when b2 is checked out' '\n+\tgit branch b1 &&\n+\tgit checkout -b b2 &&\n+\ttest_must_fail git branch -C b1 b2\n+'\n+\n+test_expect_success 'git branch -C c1 c2 should succeed when c1 is checked out' '\n+\tgit checkout -b c1 &&\n+\tgit branch c2 &&\n+\tgit branch -C c1 c2 &&\n+\ttest $(git rev-parse --abbrev-ref HEAD) = c2\n+'\n+\n+test_expect_success 'git branch -C c1 c2 should add entries to .git/logs/HEAD' '\n+\tmsg=\"Branch: copied refs/heads/c1 to refs/heads/c2\" &&\n+\tgrep \"$msg$\" .git/logs/HEAD\n+'\n+\n+test_expect_success 'git branch -C master should work when master is checked out' '\n+\tgit checkout master &&\n+\tgit branch -C master\n+'\n+\n+test_expect_success 'git branch -C master master should work when master is checked out' '\n+\tgit checkout master &&\n+\tgit branch -C master master\n+'\n+\n+test_expect_success 'git branch -C master5 master5 should work when master is checked out' '\n+\tgit checkout master &&\n+\tgit branch master5 &&\n+\tgit branch -C master5 master5\n+'\n+\n+test_expect_success 'git branch -C ab cd should overwrite existing config for cd' '\n+\tgit branch -l cd &&\n+\tgit reflog exists refs/heads/cd &&\n+\tgit config branch.cd.dummy CD &&\n+\tgit branch -l ab &&\n+\tgit reflog exists refs/heads/ab &&\n+\tgit config branch.ab.dummy AB &&\n+\tgit branch -C ab cd &&\n+\tgit reflog exists refs/heads/ab &&\n+\tgit reflog exists refs/heads/cd &&\n+\ttest $(git config branch.ab.dummy) = AB &&\n+\ttest $(git config branch.cd.dummy) = AB\n+'\n+\n+test_expect_success 'git branch -c correctly copies multiple config sections' '\n+\tFOO=1 &&\n+\texport FOO &&\n+\ttest_when_finished \"git checkout master\" &&\n+\tgit checkout -b source2 master &&\n+\n+\t# Assert that a config file with multiple config sections has\n+\t# those sections preserved...\n+\tcat >expect <<-\\EOF &&\n+\tbranch.source2.key1=value1\n+\tbranch.dest2.key1=value1\n+\tmore.gar.b=age\n+\tbranch.source2.key2=value2\n+\tbranch.dest2.key2=value2\n+\tEOF\n+\tcat >config.branch <<\\EOF &&\n+;; Note the lack of -\\EOF above & mixed indenting here. This is\n+;; intentional, we are also testing that the formatting of copied\n+;; sections is preserved.\n+\n+;; Comment for source2. Tabs\n+[branch \"source2\"]\n+\t;; Comment for the source2 value\n+\tkey1 = value1\n+;; Comment for more.gar. Spaces\n+[more \"gar\"]\n+    ;; Comment for the more.gar value\n+    b = age\n+;; Comment for source2, again. Mixed tabs/spaces.\n+[branch \"source2\"]\n+    ;; Comment for the source2 value, again\n+\tkey2 = value2\n+EOF\n+\tcat config.branch >>.git/config &&\n+\tgit branch -c source2 dest2 &&\n+\tgit config -f .git/config -l | grep -F -e source2 -e dest2 -e more.gar >actual &&\n+\ttest_cmp expect actual &&\n+\n+\t# ...and that the comments and formatting for those sections\n+\t# is also preserved.\n+\tcat >expect <<\\EOF &&\n+;; Comment for source2. Tabs\n+[branch \"source2\"]\n+\t;; Comment for the source2 value\n+\tkey1 = value1\n+;; Comment for more.gar. Spaces\n+[branch \"dest2\"]\n+\t;; Comment for the source2 value\n+\tkey1 = value1\n+;; Comment for more.gar. Spaces\n+[more \"gar\"]\n+    ;; Comment for the more.gar value\n+    b = age\n+;; Comment for source2, again. Mixed tabs/spaces.\n+[branch \"source2\"]\n+    ;; Comment for the source2 value, again\n+\tkey2 = value2\n+[branch \"dest2\"]\n+    ;; Comment for the source2 value, again\n+\tkey2 = value2\n+EOF\n+\tsed -n -e \"/Comment for source2/,\\$p\" .git/config >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_expect_success 'deleting a symref' '\n \tgit branch target &&\n \tgit symbolic-ref refs/heads/symref refs/heads/target &&\n-- \n2.13.1.611.g7e3b11ae1\n\n"},{"id":"323880","messageId":"xmqq60f6h03t.fsf@gitster.mtv.corp.google.com","threadId":"46315","inReplyTo":"20170705231454.15666-1-avarab@gmail.com","subject":"Re: [PATCH v2 0/3] branch: add a --copy to go with --move","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-07-06T00:37:10Z","receivedAt":"2017-07-06T00:37:19Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> Even though this modifies some of the same files as\n> mh/packed-ref-store it looks to me like this doesn't conflict with\n> that topic in any meaningful way, but I may be missing something. I\n> can't get a merge between this & gitster/mh/packed-ref-store\n> compiling,...\n\nThat is because each ref backend is required to implement a new\nmethod by that topic.  The following patch needs to be squashed in\nwhile merging the two topics, making the resulting commit an evil\nmerge.\n\nFWIW, the result of applying these three patches on 'master' and\nmerging the result to a commit on 'pu' that is just before the merge\nof previous round of the same topic, with the attached fix-up, exactly\nmatches the commit on 'pu' that merges the previous round, iow, I see\nthere is no meaningful change (perhaps other than log message?  I\ndidn't check) in this new round.\n\nNote that I am not complaining that this new round does not bring\nany improvements---I am not commending that you didn't screw up\nwhile rebasing to a new commit, either ;-).\n\ndiff --git b/refs/packed-backend.c a/refs/packed-backend.c\nindex dc09012300..96f9141656 100644\n--- b/refs/packed-backend.c\n+++ a/refs/packed-backend.c\n@@ -794,6 +794,13 @@ static int packed_rename_ref(struct ref_store *ref_store,\n \tdie(\"BUG: packed reference store does not support renaming references\");\n }\n \n+static int packed_copy_ref(struct ref_store *ref_store,\n+\t\t\t   const char *oldrefname, const char *newrefname,\n+\t\t\t   const char *logmsg)\n+{\n+\tdie(\"BUG: packed reference store does not support copying references\");\n+}\n+\n static struct ref_iterator *packed_reflog_iterator_begin(struct ref_store *ref_store)\n {\n \treturn empty_ref_iterator_begin();\n@@ -859,6 +866,7 @@ struct ref_storage_be refs_be_packed = {\n \tpacked_create_symref,\n \tpacked_delete_refs,\n \tpacked_rename_ref,\n+\tpacked_copy_ref,\n \n \tpacked_ref_iterator_begin,\n \tpacked_read_raw_ref,\n\n"},{"id":"323920","messageId":"xmqqbmoxdmea.fsf@gitster.mtv.corp.google.com","threadId":"46315","inReplyTo":"CAGZ79kamhPAQrE3GBC=fG=PNNtdHj7iStsqjOojkVMpNy_VEWw@mail.gmail.com","subject":"Re: What's cooking in git.git (Jul 2017, #01; Wed, 5)","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-07-07T02:13:49Z","receivedAt":"2017-07-07T02:13:56Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Stefan Beller <sbeller@google.com> writes:\n\n> Speaking of submodules, It's not just features, but I also send bug fixes. ;)\n> https://public-inbox.org/git/20170630003851.17288-1-sbeller@google.com/\n> (That patch is not related to this series, except for working in the submodule\n> area, but I consider that patch more important than e.g. this series.)\n\nI did not see the patch as fixing a bug, though.  \n\nI do agree that overwriting the branch tips in the submodule\nrepositories, possibly rewinding and discarding user's work done on\nthe local branches, is indeed a problem.  It however is unclear why\ndetaching HEAD is a good solution to solve that problem.\n\nAfter all, there must have been a reason why the user had checked\nout a branch and had pointed it at a specific commit (presumably,\nso that further work would be done while on the branch, to make it\neasier and safer to eventually push the result back to the upstream\nof the submodule's project).  So another solution that seems equally\nviable, if not even more so, could be to fail the recursive checkout\nsaying why the checkout cannot be done, just like we fail a checkout\nwhen a local change interferes with updating the contents in the\nworking tree and the index with an error message explaining which\npaths are problematic.\n\nI am *not* saying which one among the above two is better; I am not\neven saying that there could be only these two possible solutions.\nI just found the posted patch unsatisfactory because it did not make\nit clear why the chosen solution is a good one.\n\nPerhaps I misread the description; but that would mean the\ndescription was prone to be misread and has room for improvement ;-)\n\n\n\n\n"},{"id":"324088","messageId":"CAGZ79kZMC4DcnF-6nT49uZT8NtiWWkKyUm44BHRBPt98idj_Pw@mail.gmail.com","threadId":"46315","inReplyTo":"xmqqbmoxdmea.fsf@gitster.mtv.corp.google.com","subject":"Re: What's cooking in git.git (Jul 2017, #01; Wed, 5)","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2017-07-10T17:27:46Z","receivedAt":"2017-07-10T17:28:36Z","isPatch":false,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Thu, Jul 6, 2017 at 7:13 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> Stefan Beller <sbeller@google.com> writes:\n>\n>> Speaking of submodules, It's not just features, but I also send bug fixes. ;)\n>> https://public-inbox.org/git/20170630003851.17288-1-sbeller@google.com/\n>> (That patch is not related to this series, except for working in the submodule\n>> area, but I consider that patch more important than e.g. this series.)\n>\n> I did not see the patch as fixing a bug, though.\n>\n> I do agree that overwriting the branch tips in the submodule\n> repositories, possibly rewinding and discarding user's work done on\n> the local branches, is indeed a problem.  It however is unclear why\n> detaching HEAD is a good solution to solve that problem.\n\nI am not saying detaching a HEAD is a good solution, but I am saying\nit is a better solution than corrupting the submodule branch, such\nthat commits are lost in the submodule, only to be recorvered via the\nreflog.\n\n> After all, there must have been a reason why the user had checked\n> out a branch and had pointed it at a specific commit (presumably,\n> so that further work would be done while on the branch, to make it\n> easier and safer to eventually push the result back to the upstream\n> of the submodule's project).  So another solution that seems equally\n> viable, if not even more so, could be to fail the recursive checkout\n> saying why the checkout cannot be done, just like we fail a checkout\n> when a local change interferes with updating the contents in the\n> working tree and the index with an error message explaining which\n> paths are problematic.\n\nThat seems like a better model to me for now.\n\n> I am *not* saying which one among the above two is better; I am not\n> even saying that there could be only these two possible solutions.\n> I just found the posted patch unsatisfactory because it did not make\n> it clear why the chosen solution is a good one.\n\nok. My long term plan is to introduce another type of symbolic ref,\nwhich references a gitlink in another repository, such that the submodule\ncan have a clear distinction between \"follows the superproject\",\n\"has its own authoritative branch\" and \"its detached HEAD can mean\nanything, e.g. historical submodule behavior\"\n\n> Perhaps I misread the description; but that would mean the\n> description was prone to be misread and has room for improvement ;-)\n\nok.\n"},{"id":"328621","messageId":"xmqqvakbjpo5.fsf_-_@gitster.mtv.corp.google.com","threadId":"46315","inReplyTo":"xmqq60f6h03t.fsf@gitster.mtv.corp.google.com","subject":"[PATCH 4/3] branch: fix \"copy\" to never touch HEAD","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-09-22T04:57:46Z","receivedAt":"2017-09-22T04:57:54Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"When creating a new branch B by copying the branch A that happens to\nbe the current branch, it also updates HEAD to point at the new\nbranch.  It probably was made this way because \"git branch -c A B\"\npiggybacked its implementation on \"git branch -m A B\",\n\nThis does not match the usual expectation.  If I were sitting on a\nblue chair, and somebody comes and repaints it to red, I would\naccept ending up sitting on a chair that is now red (I am also OK to\nstand, instead, as there no longer is my favourite blue chair).  But\nif somebody creates a new red chair, modelling it after the blue\nchair I am sitting on, I do not expect to be booted off of the blue\nchair and ending up on sitting on the new red one.\n\nLet's fix this before it hits 'next'.  Those who want to create a\nnew branch and switch to it can do \"git checkout B\" after doing a\n\"git branch -c B\", and if that operation is so useful and deserves a\nshort-hand way to do so, perhaps extend \"git checkout -b B\" to copy\nconfigurations while creating the new branch B.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n\n * Let's send an updated one as a follow-up to the discussion thread\n   that it is a follow-up to.  The patch in this message is the same\n   as the one I previously sent; the proposed log message has been\n   updated somewhat.\n\n builtin/branch.c  |  9 +++------\n t/t3200-branch.sh | 10 +++++-----\n 2 files changed, 8 insertions(+), 11 deletions(-)\n\ndiff --git a/builtin/branch.c b/builtin/branch.c\nindex 89f64f4123..e2e3692838 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -506,12 +506,9 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int\n \t\t\t\toldref.buf + 11);\n \t}\n \n-\tif (replace_each_worktree_head_symref(oldref.buf, newref.buf, logmsg.buf)) {\n-\t\tif (copy)\n-\t\t\tdie(_(\"Branch copied to %s, but HEAD is not updated!\"), newname);\n-\t\telse\n-\t\t\tdie(_(\"Branch renamed to %s, but HEAD is not updated!\"), newname);\n-\t}\n+\tif (!copy &&\n+\t    replace_each_worktree_head_symref(oldref.buf, newref.buf, logmsg.buf))\n+\t\tdie(_(\"Branch renamed to %s, but HEAD is not updated!\"), newname);\n \n \tstrbuf_release(&logmsg);\n \ndiff --git a/t/t3200-branch.sh b/t/t3200-branch.sh\nindex 5d03ad16f6..be9b3784c6 100755\n--- a/t/t3200-branch.sh\n+++ b/t/t3200-branch.sh\n@@ -422,7 +422,7 @@ test_expect_success 'git branch --copy is a synonym for -c' '\n \ttest_cmp expect actual\n '\n \n-test_expect_success 'git branch -c ee ef should copy and checkout branch ef' '\n+test_expect_success 'git branch -c ee ef should copy to create branch ef' '\n \tgit checkout -b ee &&\n \tgit reflog exists refs/heads/ee &&\n \tgit config branch.ee.dummy Hello &&\n@@ -431,7 +431,7 @@ test_expect_success 'git branch -c ee ef should copy and checkout branch ef' '\n \tgit reflog exists refs/heads/ef &&\n \ttest $(git config branch.ee.dummy) = Hello &&\n \ttest $(git config branch.ef.dummy) = Hello &&\n-\ttest $(git rev-parse --abbrev-ref HEAD) = ef\n+\ttest $(git rev-parse --abbrev-ref HEAD) = ee\n '\n \n test_expect_success 'git branch -c f/f g/g should work' '\n@@ -494,12 +494,12 @@ test_expect_success 'git branch -C c1 c2 should succeed when c1 is checked out'\n \tgit checkout -b c1 &&\n \tgit branch c2 &&\n \tgit branch -C c1 c2 &&\n-\ttest $(git rev-parse --abbrev-ref HEAD) = c2\n+\ttest $(git rev-parse --abbrev-ref HEAD) = c1\n '\n \n-test_expect_success 'git branch -C c1 c2 should add entries to .git/logs/HEAD' '\n+test_expect_success 'git branch -C c1 c2 should never touch HEAD' '\n \tmsg=\"Branch: copied refs/heads/c1 to refs/heads/c2\" &&\n-\tgrep \"$msg$\" .git/logs/HEAD\n+\t! grep \"$msg$\" .git/logs/HEAD\n '\n \n test_expect_success 'git branch -C master should work when master is checked out' '\n-- \n2.14.1-929-g25eae544e9\n\n"},{"id":"328647","messageId":"20170922163348.GC77641@google.com","threadId":"46315","inReplyTo":"xmqqvakbjpo5.fsf_-_@gitster.mtv.corp.google.com","subject":"Re: [PATCH 4/3] branch: fix \"copy\" to never touch HEAD","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2017-09-22T16:33:48Z","receivedAt":"2017-09-22T16:33:58Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"On 09/22, Junio C Hamano wrote:\n> When creating a new branch B by copying the branch A that happens to\n> be the current branch, it also updates HEAD to point at the new\n> branch.  It probably was made this way because \"git branch -c A B\"\n> piggybacked its implementation on \"git branch -m A B\",\n> \n> This does not match the usual expectation.  If I were sitting on a\n> blue chair, and somebody comes and repaints it to red, I would\n> accept ending up sitting on a chair that is now red (I am also OK to\n> stand, instead, as there no longer is my favourite blue chair).  But\n> if somebody creates a new red chair, modelling it after the blue\n> chair I am sitting on, I do not expect to be booted off of the blue\n> chair and ending up on sitting on the new red one.\n> \n> Let's fix this before it hits 'next'.  Those who want to create a\n> new branch and switch to it can do \"git checkout B\" after doing a\n> \"git branch -c B\", and if that operation is so useful and deserves a\n> short-hand way to do so, perhaps extend \"git checkout -b B\" to copy\n> configurations while creating the new branch B.\n> \n> Signed-off-by: Junio C Hamano <gitster@pobox.com>\n> ---\n> \n>  * Let's send an updated one as a follow-up to the discussion thread\n>    that it is a follow-up to.  The patch in this message is the same\n>    as the one I previously sent; the proposed log message has been\n>    updated somewhat.\n> \n>  builtin/branch.c  |  9 +++------\n>  t/t3200-branch.sh | 10 +++++-----\n>  2 files changed, 8 insertions(+), 11 deletions(-)\n> \n> diff --git a/builtin/branch.c b/builtin/branch.c\n> index 89f64f4123..e2e3692838 100644\n> --- a/builtin/branch.c\n> +++ b/builtin/branch.c\n> @@ -506,12 +506,9 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int\n>  \t\t\t\toldref.buf + 11);\n>  \t}\n>  \n> -\tif (replace_each_worktree_head_symref(oldref.buf, newref.buf, logmsg.buf)) {\n> -\t\tif (copy)\n> -\t\t\tdie(_(\"Branch copied to %s, but HEAD is not updated!\"), newname);\n> -\t\telse\n> -\t\t\tdie(_(\"Branch renamed to %s, but HEAD is not updated!\"), newname);\n> -\t}\n> +\tif (!copy &&\n> +\t    replace_each_worktree_head_symref(oldref.buf, newref.buf, logmsg.buf))\n> +\t\tdie(_(\"Branch renamed to %s, but HEAD is not updated!\"), newname);\n>  \n>  \tstrbuf_release(&logmsg);\n>  \n> diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh\n> index 5d03ad16f6..be9b3784c6 100755\n> --- a/t/t3200-branch.sh\n> +++ b/t/t3200-branch.sh\n> @@ -422,7 +422,7 @@ test_expect_success 'git branch --copy is a synonym for -c' '\n>  \ttest_cmp expect actual\n>  '\n>  \n> -test_expect_success 'git branch -c ee ef should copy and checkout branch ef' '\n> +test_expect_success 'git branch -c ee ef should copy to create branch ef' '\n\nThe new wording seems to be missing something.  Maybe it should read:\n  \n  'git branch -c ee ef should copy branch ee to create branch ef'\n\n>  \tgit checkout -b ee &&\n>  \tgit reflog exists refs/heads/ee &&\n>  \tgit config branch.ee.dummy Hello &&\n> @@ -431,7 +431,7 @@ test_expect_success 'git branch -c ee ef should copy and checkout branch ef' '\n>  \tgit reflog exists refs/heads/ef &&\n>  \ttest $(git config branch.ee.dummy) = Hello &&\n>  \ttest $(git config branch.ef.dummy) = Hello &&\n> -\ttest $(git rev-parse --abbrev-ref HEAD) = ef\n> +\ttest $(git rev-parse --abbrev-ref HEAD) = ee\n>  '\n>  \n>  test_expect_success 'git branch -c f/f g/g should work' '\n> @@ -494,12 +494,12 @@ test_expect_success 'git branch -C c1 c2 should succeed when c1 is checked out'\n>  \tgit checkout -b c1 &&\n>  \tgit branch c2 &&\n>  \tgit branch -C c1 c2 &&\n> -\ttest $(git rev-parse --abbrev-ref HEAD) = c2\n> +\ttest $(git rev-parse --abbrev-ref HEAD) = c1\n>  '\n>  \n> -test_expect_success 'git branch -C c1 c2 should add entries to .git/logs/HEAD' '\n> +test_expect_success 'git branch -C c1 c2 should never touch HEAD' '\n>  \tmsg=\"Branch: copied refs/heads/c1 to refs/heads/c2\" &&\n> -\tgrep \"$msg$\" .git/logs/HEAD\n> +\t! grep \"$msg$\" .git/logs/HEAD\n>  '\n>  \n>  test_expect_success 'git branch -C master should work when master is checked out' '\n> -- \n> 2.14.1-929-g25eae544e9\n> \n\nThe rest of the patch lgtm.  I agree that this is probably a better UI\nthan without this patch.  Especially since the vanilla behavior of git\nbranch is to create a new branch without moving you to that new branch.\n\n-- \nBrandon Williams\n"},{"id":"328965","messageId":"874lrpw34z.fsf@evledraar.booking.com","threadId":"46315","inReplyTo":"xmqqvakbjpo5.fsf_-_@gitster.mtv.corp.google.com","subject":"Re: [PATCH 4/3] branch: fix \"copy\" to never touch HEAD","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2017-09-26T21:39:40Z","receivedAt":"2017-09-26T21:39:51Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Fri, Sep 22 2017, Junio C. Hamano jotted:\n\n> When creating a new branch B by copying the branch A that happens to\n> be the current branch, it also updates HEAD to point at the new\n> branch.  It probably was made this way because \"git branch -c A B\"\n> piggybacked its implementation on \"git branch -m A B\",\n>\n> This does not match the usual expectation.  If I were sitting on a\n> blue chair, and somebody comes and repaints it to red, I would\n> accept ending up sitting on a chair that is now red (I am also OK to\n> stand, instead, as there no longer is my favourite blue chair).  But\n> if somebody creates a new red chair, modelling it after the blue\n> chair I am sitting on, I do not expect to be booted off of the blue\n> chair and ending up on sitting on the new red one.\n>\n> Let's fix this before it hits 'next'.  Those who want to create a\n> new branch and switch to it can do \"git checkout B\" after doing a\n> \"git branch -c B\", and if that operation is so useful and deserves a\n> short-hand way to do so, perhaps extend \"git checkout -b B\" to copy\n> configurations while creating the new branch B.\n>\n> Signed-off-by: Junio C Hamano <gitster@pobox.com>\n> ---\n>\n>  * Let's send an updated one as a follow-up to the discussion thread\n>    that it is a follow-up to.  The patch in this message is the same\n>    as the one I previously sent; the proposed log message has been\n>    updated somewhat.\n>\n>  builtin/branch.c  |  9 +++------\n>  t/t3200-branch.sh | 10 +++++-----\n>  2 files changed, 8 insertions(+), 11 deletions(-)\n\nThis mostly looks good to me.\n\nI've already spent several screenfuls here on-list arguing for not\napplying the equivalent of this patch, which I won't repeat here.\n\nBut just a brief comment: I think this doing this is good, and it's\nsomething I agree with given that the consensus is going this way. I\nthought \"do what -m does\" was less confusing, but since everyone (you,\nBrad, even Sahil) seems to disagree I'm happy to be shown to be wrong &\nto have this go in so thoroughly reviewed & thought about.\n\nI do think however that we also need to update the docs, the relevant\norigin/master...gitster/sd/branch-copy diff is currently:\n\n    +The `-c` and `-C` options have the exact same semantics as `-m` and\n    +`-M`, except instead of the branch being renamed it along with its\n    +config and reflog will be copied to a new name.\n\nSuggestions welcome, but I think that should probably be changed to\nsomething like the following as part of this patch:\n\n    The `-c` and `-C` options have the exact same semantics as `-m` and\n    `-M`, except instead of the branch being renamed it along with its\n    config and reflog will be copied to a new name. Furthermore, unlike\n    its `-m` and `-M` cousins the `-c` and `-C` options will never move\n    the HEAD, whereas the move option will do so if the source branch is\n    the currently checked-out branch.\n\n> diff --git a/builtin/branch.c b/builtin/branch.c\n> index 89f64f4123..e2e3692838 100644\n> --- a/builtin/branch.c\n> +++ b/builtin/branch.c\n> @@ -506,12 +506,9 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int\n>  \t\t\t\toldref.buf + 11);\n>  \t}\n>\n> -\tif (replace_each_worktree_head_symref(oldref.buf, newref.buf, logmsg.buf)) {\n> -\t\tif (copy)\n> -\t\t\tdie(_(\"Branch copied to %s, but HEAD is not updated!\"), newname);\n> -\t\telse\n> -\t\t\tdie(_(\"Branch renamed to %s, but HEAD is not updated!\"), newname);\n> -\t}\n> +\tif (!copy &&\n> +\t    replace_each_worktree_head_symref(oldref.buf, newref.buf, logmsg.buf))\n> +\t\tdie(_(\"Branch renamed to %s, but HEAD is not updated!\"), newname);\n>\n>  \tstrbuf_release(&logmsg);\n>\n> diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh\n> index 5d03ad16f6..be9b3784c6 100755\n> --- a/t/t3200-branch.sh\n> +++ b/t/t3200-branch.sh\n> @@ -422,7 +422,7 @@ test_expect_success 'git branch --copy is a synonym for -c' '\n>  \ttest_cmp expect actual\n>  '\n>\n> -test_expect_success 'git branch -c ee ef should copy and checkout branch ef' '\n> +test_expect_success 'git branch -c ee ef should copy to create branch ef' '\n>  \tgit checkout -b ee &&\n>  \tgit reflog exists refs/heads/ee &&\n>  \tgit config branch.ee.dummy Hello &&\n> @@ -431,7 +431,7 @@ test_expect_success 'git branch -c ee ef should copy and checkout branch ef' '\n>  \tgit reflog exists refs/heads/ef &&\n>  \ttest $(git config branch.ee.dummy) = Hello &&\n>  \ttest $(git config branch.ef.dummy) = Hello &&\n> -\ttest $(git rev-parse --abbrev-ref HEAD) = ef\n> +\ttest $(git rev-parse --abbrev-ref HEAD) = ee\n>  '\n>\n>  test_expect_success 'git branch -c f/f g/g should work' '\n> @@ -494,12 +494,12 @@ test_expect_success 'git branch -C c1 c2 should succeed when c1 is checked out'\n>  \tgit checkout -b c1 &&\n>  \tgit branch c2 &&\n>  \tgit branch -C c1 c2 &&\n> -\ttest $(git rev-parse --abbrev-ref HEAD) = c2\n> +\ttest $(git rev-parse --abbrev-ref HEAD) = c1\n>  '\n>\n> -test_expect_success 'git branch -C c1 c2 should add entries to .git/logs/HEAD' '\n> +test_expect_success 'git branch -C c1 c2 should never touch HEAD' '\n>  \tmsg=\"Branch: copied refs/heads/c1 to refs/heads/c2\" &&\n> -\tgrep \"$msg$\" .git/logs/HEAD\n> +\t! grep \"$msg$\" .git/logs/HEAD\n>  '\n>\n>  test_expect_success 'git branch -C master should work when master is checked out' '\n"},{"id":"328994","messageId":"xmqqvak47val.fsf@gitster.mtv.corp.google.com","threadId":"46315","inReplyTo":"874lrpw34z.fsf@evledraar.booking.com","subject":"Re: [PATCH 4/3] branch: fix \"copy\" to never touch HEAD","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-09-27T02:02:58Z","receivedAt":"2017-09-27T02:03:07Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n\n> I do think however that we also need to update the docs, the relevant\n> origin/master...gitster/sd/branch-copy diff is currently:\n>\n>     +The `-c` and `-C` options have the exact same semantics as `-m` and\n>     +`-M`, except instead of the branch being renamed it along with its\n>     +config and reflog will be copied to a new name.\n>\n> Suggestions welcome, but I think that should probably be changed to\n> something like the following as part of this patch:\n>\n>     The `-c` and `-C` options have the exact same semantics as `-m` and\n>     `-M`, except instead of the branch being renamed it along with its\n>     config and reflog will be copied to a new name. Furthermore, unlike\n>     its `-m` and `-M` cousins the `-c` and `-C` options will never move\n>     the HEAD, whereas the move option will do so if the source branch is\n>     the currently checked-out branch.\n\nI do not think anybody even _imagines_ copy to move HEAD, and do not\nthink \"unlike -m, it doesn't touch HEAD\" a worthwhile thing to say.\n\nIt is '-m' whose behaviour may look strange wrt HEAD, so _that_ may\nbe worth mentioning, though.\n\nI suspect that your reaction probably comes from being too married\nto the idea that HEAD has a string that is the same as the refname\nof the current branch.  But that is a mere implementation detail.\nUsers would think that HEAD points at the current branch and does\nnot even care how that pointing is implemented.\n\nConceptually, you can consider that each branch has its own\nidentity, separate from various traits it has, like what its\nupstream branch is, what commit it points at, what its reflog says,\nand (most notably) what its name is.\n\nThen you can think of \"branch -m old new\" to be (1) finding the\ninstance of branch that currently has name 'old' and (2) updating\nonly one of its trait, namely, its name, without changing anything\nelse.  Creating a new instance of a branch by copying an existing\none, on the other hand, would then be the matter of (1) finding the\ninstance of branch with name 'old' and (2) creating another instance\nof branch with the same traits as the original, modulo its name is\nset to 'new'.\n\nA necessary wrinkle for \"branch -m\" that falls out of the above\nworld model is that HEAD needs to be adjusted in order to keep\npointing at the same (renamed) instance of branch that now has an\nupdated name, if HEAD happened to be pointing at the instance of the\nbranch whose name trait has been updated.\n\nSo, from that point of view, I'd prefer to do something like the\nattached patch instead.  I notice that \"branch -m\" does not mention\nconfiguration variables carried over to the new branch, but I do not\nnecessarily think we want to exhaustively enumerate what traits are\ncarried over here, so perhaps it is OK as is.\n\n Documentation/git-branch.txt | 10 ++++++----\n 1 file changed, 6 insertions(+), 4 deletions(-)\n\ndiff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt\nindex fe029ac6fc..d425e3acd4 100644\n--- a/Documentation/git-branch.txt\n+++ b/Documentation/git-branch.txt\n@@ -63,11 +63,13 @@ With a `-m` or `-M` option, <oldbranch> will be renamed to <newbranch>.\n If <oldbranch> had a corresponding reflog, it is renamed to match\n <newbranch>, and a reflog entry is created to remember the branch\n renaming. If <newbranch> exists, -M must be used to force the rename\n-to happen.\n+to happen.  If you rename a branch that is currently checked out,\n+`HEAD` is adjusted so that the branch (whose name is now\n+<newbranch>) stays to be the current branch.\n \n-The `-c` and `-C` options have the exact same semantics as `-m` and\n-`-M`, except instead of the branch being renamed it along with its\n-config and reflog will be copied to a new name.\n+With a `-c` or`-C` option, a new branch <newbranch> is created by\n+copying the traits like the reflog contents and `branch.*.*`\n+configuration from an existing <oldbranch>.\n \n With a `-d` or `-D` option, `<branchname>` will be deleted.  You may\n specify more than one branch for deletion.  If the branch currently\n"},{"id":"329185","messageId":"873775tl0j.fsf@evledraar.booking.com","threadId":"46315","inReplyTo":"xmqqvak47val.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH 4/3] branch: fix \"copy\" to never touch HEAD","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2017-09-29T18:30:52Z","receivedAt":"2017-09-29T18:31:00Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Wed, Sep 27 2017, Junio C. Hamano jotted:\n\n> Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n>\n>> I do think however that we also need to update the docs, the relevant\n>> origin/master...gitster/sd/branch-copy diff is currently:\n>>\n>>     +The `-c` and `-C` options have the exact same semantics as `-m` and\n>>     +`-M`, except instead of the branch being renamed it along with its\n>>     +config and reflog will be copied to a new name.\n>>\n>> Suggestions welcome, but I think that should probably be changed to\n>> something like the following as part of this patch:\n>>\n>>     The `-c` and `-C` options have the exact same semantics as `-m` and\n>>     `-M`, except instead of the branch being renamed it along with its\n>>     config and reflog will be copied to a new name. Furthermore, unlike\n>>     its `-m` and `-M` cousins the `-c` and `-C` options will never move\n>>     the HEAD, whereas the move option will do so if the source branch is\n>>     the currently checked-out branch.\n>\n> I do not think anybody even _imagines_ copy to move HEAD, and do not\n> think \"unlike -m, it doesn't touch HEAD\" a worthwhile thing to say.\n>\n> It is '-m' whose behaviour may look strange wrt HEAD, so _that_ may\n> be worth mentioning, though.\n>\n> I suspect that your reaction probably comes from being too married\n> to the idea that HEAD has a string that is the same as the refname\n> of the current branch.  But that is a mere implementation detail.\n> Users would think that HEAD points at the current branch and does\n> not even care how that pointing is implemented.\n\nTo cut to the chase instead of pointlessly replying to this\npoint-by-point, I think your patch quoted below is good and solves the\nminor doc issue I had with your patch.\n\nYes HEAD is an implementation detail, but it's an exposed implementation\ndetail.\n\nThus before your patch it was true to say that \"[-c] has the exact same\nsemantics as [-m] [...] except [ s/move/rename/ ]\" since that was the\nonly behavior change, but with your patch adding another \"if (!copy &&\n...)\" we'd now have two things different in the code, but only one thing\nenumerated as being different in the docs.\n\nJust rephrasing it as you did is a better way out of that than my\nproposed patch. Thanks.\n\n> Conceptually, you can consider that each branch has its own\n> identity, separate from various traits it has, like what its\n> upstream branch is, what commit it points at, what its reflog says,\n> and (most notably) what its name is.\n>\n> Then you can think of \"branch -m old new\" to be (1) finding the\n> instance of branch that currently has name 'old' and (2) updating\n> only one of its trait, namely, its name, without changing anything\n> else.  Creating a new instance of a branch by copying an existing\n> one, on the other hand, would then be the matter of (1) finding the\n> instance of branch with name 'old' and (2) creating another instance\n> of branch with the same traits as the original, modulo its name is\n> set to 'new'.\n>\n> A necessary wrinkle for \"branch -m\" that falls out of the above\n> world model is that HEAD needs to be adjusted in order to keep\n> pointing at the same (renamed) instance of branch that now has an\n> updated name, if HEAD happened to be pointing at the instance of the\n> branch whose name trait has been updated.\n>\n> So, from that point of view, I'd prefer to do something like the\n> attached patch instead.  I notice that \"branch -m\" does not mention\n> configuration variables carried over to the new branch, but I do not\n> necessarily think we want to exhaustively enumerate what traits are\n> carried over here, so perhaps it is OK as is.\n>\n>  Documentation/git-branch.txt | 10 ++++++----\n>  1 file changed, 6 insertions(+), 4 deletions(-)\n>\n> diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt\n> index fe029ac6fc..d425e3acd4 100644\n> --- a/Documentation/git-branch.txt\n> +++ b/Documentation/git-branch.txt\n> @@ -63,11 +63,13 @@ With a `-m` or `-M` option, <oldbranch> will be renamed to <newbranch>.\n>  If <oldbranch> had a corresponding reflog, it is renamed to match\n>  <newbranch>, and a reflog entry is created to remember the branch\n>  renaming. If <newbranch> exists, -M must be used to force the rename\n> -to happen.\n> +to happen.  If you rename a branch that is currently checked out,\n> +`HEAD` is adjusted so that the branch (whose name is now\n> +<newbranch>) stays to be the current branch.\n>\n> -The `-c` and `-C` options have the exact same semantics as `-m` and\n> -`-M`, except instead of the branch being renamed it along with its\n> -config and reflog will be copied to a new name.\n> +With a `-c` or`-C` option, a new branch <newbranch> is created by\n> +copying the traits like the reflog contents and `branch.*.*`\n> +configuration from an existing <oldbranch>.\n>\n>  With a `-d` or `-D` option, `<branchname>` will be deleted.  You may\n>  specify more than one branch for deletion.  If the branch currently\n"}]}