Merge branch 'sg/fsck-config-in-doc' into maint
authorJunio C Hamano <gitster@pobox.com>
Fri, 9 Aug 2019 22:18:18 +0000 (15:18 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 9 Aug 2019 22:18:18 +0000 (15:18 -0700)
Doc update.

* sg/fsck-config-in-doc:
Documentation/git-fsck.txt: include fsck.* config variables

86 files changed:
Documentation/CodingGuidelines
Documentation/RelNotes/2.22.1.txt
Documentation/config/alias.txt
Documentation/config/gpg.txt
Documentation/git-branch.txt
Documentation/git-clone.txt
Documentation/git.txt
Documentation/gitweb.txt
Documentation/technical/api-trace2.txt
Documentation/technical/commit-graph.txt
README.md
builtin/am.c
builtin/branch.c
builtin/clean.c
builtin/clone.c
builtin/commit-graph.c
builtin/commit.c
builtin/fetch.c
builtin/gc.c
builtin/interpret-trailers.c
builtin/ls-files.c
builtin/merge.c
builtin/pack-objects.c
builtin/rebase.c
builtin/receive-pack.c
builtin/repack.c
builtin/rm.c
builtin/stash.c
builtin/submodule--helper.c
cache.h
ci/install-dependencies.sh
ci/lib.sh
commit-graph.c
commit-graph.h
commit.c
compat/mingw.c
compat/poll/poll.c
compat/winansi.c
config.c
contrib/coccinelle/array.cocci
contrib/completion/git-completion.bash
delta-islands.c
delta-islands.h
diff.c
diffcore-rename.c
dir.c
editor.c
fast-import.c
fsmonitor.c
git-add--interactive.perl
gpg-interface.c
kwset.c
name-hash.c
object.c
packfile.c
packfile.h
pager.c
pretty.c
progress.c
read-cache.c
sequencer.c
sh-i18n--envsubst.c
t/t0001-init.sh
t/t0061-run-command.sh
t/t3404-rebase-interactive.sh
t/t3418-rebase-continue.sh
t/t3420-rebase-autostash.sh
t/t3430-rebase-merges.sh
t/t3600-rm.sh
t/t3701-add-interactive.sh
t/t3903-stash.sh
t/t4015-diff-whitespace.sh
t/t5318-commit-graph.sh
t/t5541-http-push-smart.sh
t/t5551-http-fetch-smart.sh
t/t5801-remote-helpers.sh
t/t5801/git-remote-testgit
t/t7300-clean.sh
t/t7407-submodule-foreach.sh
t/t7513-interpret-trailers.sh
t/test-lib-functions.sh
transport-helper.c
transport.c
upload-pack.c
wrapper.c
xdiff/xemit.c
index 1169ff6c8eb87e6e372110a89f31273879bc862f..f45db5b72740b692a87a050271f5930743e4c4aa 100644 (file)
@@ -195,10 +195,30 @@ For C programs:
    by e.g. "echo DEVELOPER=1 >>config.mak".
 
  - We try to support a wide range of C compilers to compile Git with,
-   including old ones. That means that you should not use C99
-   initializers, even if a lot of compilers grok it.
+   including old ones.  You should not use features from newer C
+   standard, even if your compiler groks them.
 
- - Variables have to be declared at the beginning of the block.
+   There are a few exceptions to this guideline:
+
+   . since early 2012 with e1327023ea, we have been using an enum
+     definition whose last element is followed by a comma.  This, like
+     an array initializer that ends with a trailing comma, can be used
+     to reduce the patch noise when adding a new identifer at the end.
+
+   . since mid 2017 with cbc0f81d, we have been using designated
+     initializers for struct (e.g. "struct t v = { .val = 'a' };").
+
+   . since mid 2017 with 512f41cf, we have been using designated
+     initializers for array (e.g. "int array[10] = { [5] = 2 }").
+
+   These used to be forbidden, but we have not heard any breakage
+   report, and they are assumed to be safe.
+
+ - Variables have to be declared at the beginning of the block, before
+   the first statement (i.e. -Wdeclaration-after-statement).
+
+ - Declaring a variable in the for loop "for (int i = 0; i < 10; i++)"
+   is still not allowed in this codebase.
 
  - NULL pointers shall be written as NULL, not as 0.
 
index 819879d382d7bc74b958c9821ef3cd0c19d0b4ea..76dd8fb578b54ab66044518bad521b0c53a8a61c 100644 (file)
@@ -73,4 +73,75 @@ Fixes since v2.22
  * The list of for-each like macros used by clang-format has been
    updated.
 
-Also contains various documentation updates and code clean-ups.
+ * "git push --atomic" that goes over the transport-helper (namely,
+   the smart http transport) failed to prevent refs to be pushed when
+   it can locally tell that one of the ref update will fail without
+   having to consult the other end, which has been corrected.
+
+ * "git clean" silently skipped a path when it cannot lstat() it; now
+   it gives a warning.
+
+ * A codepath that reads from GPG for signed object verification read
+   past the end of allocated buffer, which has been fixed.
+
+ * "git rm" to resolve a conflicted path leaked an internal message
+   "needs merge" before actually removing the path, which was
+   confusing.  This has been corrected.
+
+ * The "git clone" documentation refers to command line options in its
+   description in the short form; they have been replaced with long
+   forms to make them more recognisable.
+
+ * The configuration variable rebase.rescheduleFailedExec should be
+   effective only while running an interactive rebase and should not
+   affect anything when running a non-interactive one, which was not
+   the case.  This has been corrected.
+
+ * "git submodule foreach" did not protect command line options passed
+   to the command to be run in each submodule correctly, when the
+   "--recursive" option was in use.
+
+ * Use "Erase in Line" CSI sequence that is already used in the editor
+   support to clear cruft in the progress output.
+
+ * The codepath to compute delta islands used to spew progress output
+   without giving the callers any way to squelch it, which has been
+   fixed.
+
+ * The code to parse scaled numbers out of configuration files has
+   been made more robust and also easier to follow.
+
+ * An incorrect list of options was cached after command line
+   completion failed (e.g. trying to complete a command that requires
+   a repository outside one), which has been corrected.
+
+ * "git rebase --abort" used to leave refs/rewritten/ when concluding
+   "git rebase -r", which has been corrected.
+
+ * "git stash show 23" used to work, but no more after getting
+   rewritten in C; this regression has been corrected.
+
+ * "git interpret-trailers" always treated '#' as the comment
+   character, regardless of core.commentChar setting, which has been
+   corrected.
+
+ * Code clean-up to avoid signed integer overlaps during binary search.
+
+ * "git checkout -p" needs to selectively apply a patch in reverse,
+   which did not work well.
+
+ * The commit-graph file is now part of the "files that the runtime
+   may keep open file descriptors on, all of which would need to be
+   closed when done with the object store", and the file descriptor to
+   an existing commit-graph file now is closed before "gc" finalizes a
+   new instance to replace it.
+
+ * Code restructuring during 2.20 period broke fetching tags via
+   "import" based transports.
+
+ * We have been trying out a few language features outside c89; the
+   coding guidelines document did not talk about them and instead had
+   a blanket ban against them.
+
+
+Also contains various documentation updates, code clean-ups and minor fixups.
index 0b14178314251a9b43c62bc5f85a4c1cc445fd1a..f1ca739d574293fd001322a2cc272e2cc0510344 100644 (file)
@@ -1,18 +1,28 @@
 alias.*::
        Command aliases for the linkgit:git[1] command wrapper - e.g.
-       after defining "alias.last = cat-file commit HEAD", the invocation
-       "git last" is equivalent to "git cat-file commit HEAD". To avoid
+       after defining `alias.last = cat-file commit HEAD`, the invocation
+       `git last` is equivalent to `git cat-file commit HEAD`. To avoid
        confusion and troubles with script usage, aliases that
        hide existing Git commands are ignored. Arguments are split by
        spaces, the usual shell quoting and escaping is supported.
        A quote pair or a backslash can be used to quote them.
 +
+Note that the first word of an alias does not necessarily have to be a
+command. It can be a command-line option that will be passed into the
+invocation of `git`. In particular, this is useful when used with `-c`
+to pass in one-time configurations or `-p` to force pagination. For example,
+`loud-rebase = -c commit.verbose=true rebase` can be defined such that
+running `git loud-rebase` would be equivalent to
+`git -c commit.verbose=true rebase`. Also, `ps = -p status` would be a
+helpful alias since `git ps` would paginate the output of `git status`
+where the original command does not.
++
 If the alias expansion is prefixed with an exclamation point,
 it will be treated as a shell command.  For example, defining
-"alias.new = !gitk --all --not ORIG_HEAD", the invocation
-"git new" is equivalent to running the shell command
-"gitk --all --not ORIG_HEAD".  Note that shell commands will be
+`alias.new = !gitk --all --not ORIG_HEAD`, the invocation
+`git new` is equivalent to running the shell command
+`gitk --all --not ORIG_HEAD`.  Note that shell commands will be
 executed from the top-level directory of a repository, which may
 not necessarily be the current directory.
-`GIT_PREFIX` is set as returned by running 'git rev-parse --show-prefix'
+`GIT_PREFIX` is set as returned by running `git rev-parse --show-prefix`
 from the original current directory. See linkgit:git-rev-parse[1].
index f999f8ea491d0238abaca2037535c0374f28bc01..cce2c8924598c240fcebfadea8be5b4b28ce378e 100644 (file)
@@ -2,7 +2,7 @@ gpg.program::
        Use this custom program instead of "`gpg`" found on `$PATH` when
        making or verifying a PGP signature. The program must support the
        same command-line interface as GPG, namely, to verify a detached
-       signature, "`gpg --verify $file - <$signature`" is run, and the
+       signature, "`gpg --verify $signature - <$file`" is run, and the
        program is expected to signal a good signature by exiting with
        code 0, and to generate an ASCII-armored detached signature, the
        standard input of "`gpg -bsau $key`" is fed with the contents to be
index 6ebd512b4f3344c2f166f5bb09f0b5c6eb96ab03..d9325e2145441520c706c8d0a369d559e841eb19 100644 (file)
@@ -8,12 +8,15 @@ git-branch - List, create, or delete branches
 SYNOPSIS
 --------
 [verse]
-'git branch' [--color[=<when>] | --no-color] [-r | -a]
-       [--list] [--show-current] [-v [--abbrev=<length> | --no-abbrev]]
+'git branch' [--color[=<when>] | --no-color]
+       [-v [--abbrev=<length> | --no-abbrev]]
+       [--show-current]
        [--column[=<options>] | --no-column] [--sort=<key>]
        [(--merged | --no-merged) [<commit>]]
        [--contains [<commit]] [--no-contains [<commit>]]
-       [--points-at <object>] [--format=<format>] [<pattern>...]
+       [--points-at <object>] [--format=<format>]
+       [(-r | --remotes) | (-a | --all)]
+       [--list] [<pattern>...]
 'git branch' [--track | --no-track] [-f] <branchname> [<start-point>]
 'git branch' (--set-upstream-to=<upstream> | -u <upstream>) [<branchname>]
 'git branch' --unset-upstream [<branchname>]
@@ -28,11 +31,15 @@ DESCRIPTION
 If `--list` is given, or if there are no non-option arguments, existing
 branches are listed; the current branch will be highlighted with an
 asterisk.  Option `-r` causes the remote-tracking branches to be listed,
-and option `-a` shows both local and remote branches. If a `<pattern>`
+and option `-a` shows both local and remote branches.
+
+If a `<pattern>`
 is given, it is used as a shell wildcard to restrict the output to
 matching branches. If multiple patterns are given, a branch is shown if
-it matches any of the patterns.  Note that when providing a
-`<pattern>`, you must use `--list`; otherwise the command is interpreted
+it matches any of the patterns.
+
+Note that when providing a
+`<pattern>`, you must use `--list`; otherwise the command may be interpreted
 as branch creation.
 
 With `--contains`, shows only the branches that contain the named commit
@@ -153,10 +160,12 @@ This option is only applicable in non-verbose mode.
 -r::
 --remotes::
        List or delete (if used with -d) the remote-tracking branches.
+       Combine with `--list` to match the optional pattern(s).
 
 -a::
 --all::
        List both remote-tracking branches and local branches.
+       Combine with `--list` to match optional pattern(s).
 
 -l::
 --list::
@@ -322,6 +331,18 @@ $ git branch -D test                                    <2>
 <2> Delete the "test" branch even if the "master" branch (or whichever branch
     is currently checked out) does not have all commits from the test branch.
 
+Listing branches from a specific remote::
++
+------------
+$ git branch -r -l '<remote>/<pattern>'                 <1>
+$ git for-each-ref 'refs/remotes/<remote>/<pattern>'    <2>
+------------
++
+<1> Using `-a` would conflate <remote> with any local branches you happen to
+    have been prefixed with the same <remote> pattern.
+<2> `for-each-ref` can take a wide range of options. See linkgit:git-for-each-ref[1]
+
+Patterns will normally need quoting.
 
 NOTES
 -----
index a0f14b51f2654ced2fb7f76078d95050d2730a7d..ca8871c165bffa25e5d7500918ef15415f0c3e41 100644 (file)
@@ -22,7 +22,7 @@ DESCRIPTION
 
 Clones a repository into a newly created directory, creates
 remote-tracking branches for each branch in the cloned repository
-(visible using `git branch -r`), and creates and checks out an
+(visible using `git branch --remotes`), and creates and checks out an
 initial branch that is forked from the cloned repository's
 currently active branch.
 
@@ -40,8 +40,8 @@ configuration variables.
 
 OPTIONS
 -------
---local::
 -l::
+--local::
        When the repository to clone from is on a local machine,
        this flag bypasses the normal "Git aware" transport
        mechanism and clones the repository by making a copy of
@@ -62,8 +62,8 @@ Git transport instead.
        directory instead of using hardlinks. This may be desirable
        if you are trying to make a back-up of your repository.
 
---shared::
 -s::
+--shared::
        When the repository to clone is on the local machine,
        instead of using hard links, automatically setup
        `.git/objects/info/alternates` to share the objects
@@ -80,13 +80,13 @@ which automatically call `git gc --auto`. (See linkgit:git-gc[1].)
 If these objects are removed and were referenced by the cloned repository,
 then the cloned repository will become corrupt.
 +
-Note that running `git repack` without the `-l` option in a repository
-cloned with `-s` will copy objects from the source repository into a pack
-in the cloned repository, removing the disk space savings of `clone -s`.
-It is safe, however, to run `git gc`, which uses the `-l` option by
+Note that running `git repack` without the `--local` option in a repository
+cloned with `--shared` will copy objects from the source repository into a pack
+in the cloned repository, removing the disk space savings of `clone --shared`.
+It is safe, however, to run `git gc`, which uses the `--local` option by
 default.
 +
-If you want to break the dependency of a repository cloned with `-s` on
+If you want to break the dependency of a repository cloned with `--shared` on
 its source repository, you can simply run `git repack -a` to copy all
 objects from the source repository into a pack in the cloned repository.
 
@@ -115,19 +115,19 @@ objects from the source repository into a pack in the cloned repository.
        same repository, and this option can be used to stop the
        borrowing.
 
---quiet::
 -q::
+--quiet::
        Operate quietly.  Progress is not reported to the standard
        error stream.
 
---verbose::
 -v::
+--verbose::
        Run verbosely. Does not affect the reporting of progress status
        to the standard error stream.
 
 --progress::
        Progress status is reported on the standard error stream
-       by default when it is attached to a terminal, unless -q
+       by default when it is attached to a terminal, unless `--quiet`
        is specified. This flag forces progress status even if the
        standard error stream is not directed to a terminal.
 
@@ -139,15 +139,15 @@ objects from the source repository into a pack in the cloned repository.
        When multiple `--server-option=<option>` are given, they are all
        sent to the other side in the order listed on the command line.
 
---no-checkout::
 -n::
+--no-checkout::
        No checkout of HEAD is performed after the clone is complete.
 
 --bare::
        Make a 'bare' Git repository.  That is, instead of
        creating `<directory>` and placing the administrative
        files in `<directory>/.git`, make the `<directory>`
-       itself the `$GIT_DIR`. This obviously implies the `-n`
+       itself the `$GIT_DIR`. This obviously implies the `--no-checkout`
        because there is nowhere to check out the working tree.
        Also the branch heads at the remote are copied directly
        to corresponding local branch heads, without mapping
@@ -163,13 +163,13 @@ objects from the source repository into a pack in the cloned repository.
        that all these refs are overwritten by a `git remote update` in the
        target repository.
 
---origin <name>::
 -o <name>::
+--origin <name>::
        Instead of using the remote name `origin` to keep track
        of the upstream repository, use `<name>`.
 
---branch <name>::
 -b <name>::
+--branch <name>::
        Instead of pointing the newly created HEAD to the branch pointed
        to by the cloned repository's HEAD, point to `<name>` branch
        instead. In a non-bare repository, this is the branch that will
@@ -177,8 +177,8 @@ objects from the source repository into a pack in the cloned repository.
        `--branch` can also take tags and detaches the HEAD at that commit
        in the resulting repository.
 
---upload-pack <upload-pack>::
 -u <upload-pack>::
+--upload-pack <upload-pack>::
        When given, and the repository to clone from is accessed
        via ssh, this specifies a non-default path for the command
        run on the other end.
@@ -187,8 +187,8 @@ objects from the source repository into a pack in the cloned repository.
        Specify the directory from which templates will be used;
        (See the "TEMPLATE DIRECTORY" section of linkgit:git-init[1].)
 
---config <key>=<value>::
 -c <key>=<value>::
+--config <key>=<value>::
        Set a configuration variable in the newly-created repository;
        this takes effect immediately after the repository is
        initialized, but before the remote history is fetched or any
index 6ddc1e2ca6830ac165543dad4a57afd6d4b50c49..81f7ecd52c959f74f5b78b45d34ea0c9b041a966 100644 (file)
@@ -56,7 +56,8 @@ help ...`.
        Run as if git was started in '<path>' instead of the current working
        directory.  When multiple `-C` options are given, each subsequent
        non-absolute `-C <path>` is interpreted relative to the preceding `-C
-       <path>`.
+       <path>`.  If '<path>' is present but empty, e.g. `-C ""`, then the
+       current working directory is left unchanged.
 +
 This option affects options that expect path name like `--git-dir` and
 `--work-tree` in that their interpretations of the path names would be
index c7436098c9596f221fc9553c1af7f8a599c6301c..3cc9b034c435a555f96eb7943932045ec2b9bcc8 100644 (file)
@@ -28,8 +28,7 @@ Gitweb provides a web interface to Git repositories.  Its features include:
   revisions one at a time, viewing the history of the repository.
 * Finding commits which commit messages matches given search term.
 
-See http://git.kernel.org/?p=git/git.git;a=tree;f=gitweb[] or
-http://repo.or.cz/w/git.git/tree/HEAD:/gitweb/[] for gitweb source code,
+See http://repo.or.cz/w/git.git/tree/HEAD:/gitweb/[] for gitweb source code,
 browsed using gitweb itself.
 
 
index 23c3cc7a3727ed88513c1e735a41654a75334363..fd1e6289447050c39dcc993c552196c24c21b6e9 100644 (file)
@@ -668,7 +668,7 @@ completed.)
        "event":"signal",
        ...
        "t_abs":0.001227,  # elapsed time in seconds
-       "signal":13        # SIGTERM, SIGINT, etc.
+       "signo":13         # SIGTERM, SIGINT, etc.
 }
 ------------
 
index 7805b0968c828fda1601fe00c2b5b9d9359b38e2..fb53341d5ee3116361d599b5aca2e5ea3a228589 100644 (file)
@@ -127,23 +127,6 @@ Design Details
   helpful for these clones, anyway. The commit-graph will not be read or
   written when shallow commits are present.
 
-Future Work
------------
-
-- After computing and storing generation numbers, we must make graph
-  walks aware of generation numbers to gain the performance benefits they
-  enable. This will mostly be accomplished by swapping a commit-date-ordered
-  priority queue with one ordered by generation number. The following
-  operations are important candidates:
-
-    - 'log --topo-order'
-    - 'tag --merged'
-
-- A server could provide a commit-graph file as part of the network protocol
-  to avoid extra calculations by clients. This feature is only of benefit if
-  the user is willing to trust the file, because verifying the file is correct
-  is as hard as computing it from scratch.
-
 Related Links
 -------------
 [0] https://bugs.chromium.org/p/git/issues/detail?id=8
index 88f126184c52bfe4859ec189d018872902e02a84..e1d2b82209bb6d9aab0c86ca89d486c48bfc71f5 100644 (file)
--- a/README.md
+++ b/README.md
@@ -19,7 +19,7 @@ including full documentation and Git related tools.
 
 See [Documentation/gittutorial.txt][] to get started, then see
 [Documentation/giteveryday.txt][] for a useful minimum set of commands, and
-Documentation/git-<commandname>.txt for documentation of each command.
+`Documentation/git-<commandname>.txt` for documentation of each command.
 If git has been correctly installed, then the tutorial can also be
 read with `man gittutorial` or `git help tutorial`, and the
 documentation of each command with `man git-<commandname>` or `git help
index 78389d08b631f0281b7f315de0fb67f61c044d0f..252e37ddf08e45eceb98628fe598df3ebcafb1fd 100644 (file)
@@ -1801,7 +1801,7 @@ static void am_run(struct am_state *state, int resume)
         */
        if (!state->rebasing) {
                am_destroy(state);
-               close_all_packs(the_repository->objects);
+               close_object_store(the_repository->objects);
                run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
        }
 }
index d4359b33ac0fb27b6b8be7109a2a862bc9cd2d77..8e243cf8c2fe094ec332150761f7434287d6e96b 100644 (file)
@@ -830,7 +830,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
                strbuf_release(&buf);
        } else if (argc > 0 && argc <= 2) {
                if (filter.kind != FILTER_REFS_BRANCHES)
-                       die(_("-a and -r options to 'git branch' do not make sense with a branch name"));
+                       die(_("The -a, and -r, options to 'git branch' do not take a branch name.\n"
+                                 "Did you mean to use: -a|-r --list <pattern>?"));
 
                if (track == BRANCH_TRACK_OVERRIDE)
                        die(_("the '--set-upstream' option is no longer supported. Please use '--track' or '--set-upstream-to' instead."));
index aaba4af3c26de2150ad655a0b162fc85a69a466d..d5579da716e06f372132f827852b0f49f1e4af1d 100644 (file)
@@ -34,6 +34,7 @@ static const char *msg_would_remove = N_("Would remove %s\n");
 static const char *msg_skip_git_dir = N_("Skipping repository %s\n");
 static const char *msg_would_skip_git_dir = N_("Would skip repository %s\n");
 static const char *msg_warn_remove_failed = N_("failed to remove %s");
+static const char *msg_warn_lstat_failed = N_("could not lstat %s\n");
 
 enum color_clean {
        CLEAN_COLOR_RESET = 0,
@@ -194,7 +195,7 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
                strbuf_setlen(path, len);
                strbuf_addstr(path, e->d_name);
                if (lstat(path->buf, &st))
-                       ; /* fall thru */
+                       warning_errno(_(msg_warn_lstat_failed), path->buf);
                else if (S_ISDIR(st.st_mode)) {
                        if (remove_dirs(path, prefix, force_flag, dry_run, quiet, &gone))
                                ret = 1;
index 356bae5ed72d2e510da08d8089fbdda607ba0e9f..3623f040d4086458b8e36e5e701d892c1d426078 100644 (file)
@@ -1245,7 +1245,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        transport_disconnect(transport);
 
        if (option_dissociate) {
-               close_all_packs(the_repository->objects);
+               close_object_store(the_repository->objects);
                dissociate_from_references();
        }
 
index 537fdfd0f0759e8cce2303af5a197ae77b2b879b..d8efa5bab276a816bef48e9cb3891ccc0cb81aad 100644 (file)
@@ -141,6 +141,8 @@ static int graph_write(int argc, const char **argv)
        struct string_list *pack_indexes = NULL;
        struct string_list *commit_hex = NULL;
        struct string_list lines;
+       int result = 0;
+       unsigned int flags = COMMIT_GRAPH_PROGRESS;
 
        static struct option builtin_commit_graph_write_options[] = {
                OPT_STRING(0, "object-dir", &opts.obj_dir,
@@ -165,13 +167,13 @@ static int graph_write(int argc, const char **argv)
                die(_("use at most one of --reachable, --stdin-commits, or --stdin-packs"));
        if (!opts.obj_dir)
                opts.obj_dir = get_object_directory();
+       if (opts.append)
+               flags |= COMMIT_GRAPH_APPEND;
 
        read_replace_refs = 0;
 
-       if (opts.reachable) {
-               write_commit_graph_reachable(opts.obj_dir, opts.append, 1);
-               return 0;
-       }
+       if (opts.reachable)
+               return write_commit_graph_reachable(opts.obj_dir, flags);
 
        string_list_init(&lines, 0);
        if (opts.stdin_packs || opts.stdin_commits) {
@@ -188,14 +190,14 @@ static int graph_write(int argc, const char **argv)
                UNLEAK(buf);
        }
 
-       write_commit_graph(opts.obj_dir,
-                          pack_indexes,
-                          commit_hex,
-                          opts.append,
-                          1);
+       if (write_commit_graph(opts.obj_dir,
+                              pack_indexes,
+                              commit_hex,
+                              flags))
+               result = 1;
 
        UNLEAK(lines);
-       return 0;
+       return result;
 }
 
 int cmd_commit_graph(int argc, const char **argv, const char *prefix)
index 1c9e8e2228c7ce58375bc247c4ad5850a1bd7d2d..192140111747cc0683c1ae19a785000110808ff3 100644 (file)
@@ -1669,8 +1669,9 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
                      "new_index file. Check that disk is not full and quota is\n"
                      "not exceeded, and then \"git reset HEAD\" to recover."));
 
-       if (git_env_bool(GIT_TEST_COMMIT_GRAPH, 0))
-               write_commit_graph_reachable(get_object_directory(), 0, 0);
+       if (git_env_bool(GIT_TEST_COMMIT_GRAPH, 0) &&
+           write_commit_graph_reachable(get_object_directory(), 0))
+               return 1;
 
        repo_rerere(the_repository, 0);
        run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
index 4ba63d5ac642844832a5c832cea93ddf99507764..c9b92b1e52448bb1537a229b3ed7d1587db28ecf 100644 (file)
@@ -239,6 +239,7 @@ static int will_fetch(struct ref **head, const unsigned char *sha1)
 struct refname_hash_entry {
        struct hashmap_entry ent; /* must be the first member */
        struct object_id oid;
+       int ignore;
        char refname[FLEX_ARRAY];
 };
 
@@ -287,6 +288,11 @@ static int refname_hash_exists(struct hashmap *map, const char *refname)
        return !!hashmap_get_from_hash(map, strhash(refname), refname);
 }
 
+static void clear_item(struct refname_hash_entry *item)
+{
+       item->ignore = 1;
+}
+
 static void find_non_local_tags(const struct ref *refs,
                                struct ref **head,
                                struct ref ***tail)
@@ -319,7 +325,7 @@ static void find_non_local_tags(const struct ref *refs,
                            !will_fetch(head, ref->old_oid.hash) &&
                            !has_object_file_with_flags(&item->oid, OBJECT_INFO_QUICK) &&
                            !will_fetch(head, item->oid.hash))
-                               oidclr(&item->oid);
+                               clear_item(item);
                        item = NULL;
                        continue;
                }
@@ -333,7 +339,7 @@ static void find_non_local_tags(const struct ref *refs,
                if (item &&
                    !has_object_file_with_flags(&item->oid, OBJECT_INFO_QUICK) &&
                    !will_fetch(head, item->oid.hash))
-                       oidclr(&item->oid);
+                       clear_item(item);
 
                item = NULL;
 
@@ -354,7 +360,7 @@ static void find_non_local_tags(const struct ref *refs,
        if (item &&
            !has_object_file_with_flags(&item->oid, OBJECT_INFO_QUICK) &&
            !will_fetch(head, item->oid.hash))
-               oidclr(&item->oid);
+               clear_item(item);
 
        /*
         * For all the tags in the remote_refs_list,
@@ -362,19 +368,21 @@ static void find_non_local_tags(const struct ref *refs,
         */
        for_each_string_list_item(remote_ref_item, &remote_refs_list) {
                const char *refname = remote_ref_item->string;
+               struct ref *rm;
 
                item = hashmap_get_from_hash(&remote_refs, strhash(refname), refname);
                if (!item)
                        BUG("unseen remote ref?");
 
                /* Unless we have already decided to ignore this item... */
-               if (!is_null_oid(&item->oid)) {
-                       struct ref *rm = alloc_ref(item->refname);
-                       rm->peer_ref = alloc_ref(item->refname);
-                       oidcpy(&rm->old_oid, &item->oid);
-                       **tail = rm;
-                       *tail = &rm->next;
-               }
+               if (item->ignore)
+                       continue;
+
+               rm = alloc_ref(item->refname);
+               rm->peer_ref = alloc_ref(item->refname);
+               oidcpy(&rm->old_oid, &item->oid);
+               **tail = rm;
+               *tail = &rm->next;
        }
        hashmap_free(&remote_refs, 1);
        string_list_clear(&remote_refs_list, 0);
@@ -1672,7 +1680,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
 
        string_list_clear(&list, 0);
 
-       close_all_packs(the_repository->objects);
+       close_object_store(the_repository->objects);
 
        argv_array_pushl(&argv_gc_auto, "gc", "--auto", NULL);
        if (verbosity < 0)
index 8943bcc300d4a2ce6786857908e189c374373906..be8e0bfcbe0a428f72c5762ff8a066baaefd2533 100644 (file)
@@ -653,7 +653,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
        gc_before_repack();
 
        if (!repository_format_precious_objects) {
-               close_all_packs(the_repository->objects);
+               close_object_store(the_repository->objects);
                if (run_command_v_opt(repack.argv, RUN_GIT_CMD))
                        die(FAILED_RUN, repack.argv[0]);
 
@@ -681,13 +681,14 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
        report_garbage = report_pack_garbage;
        reprepare_packed_git(the_repository);
        if (pack_garbage.nr > 0) {
-               close_all_packs(the_repository->objects);
+               close_object_store(the_repository->objects);
                clean_pack_garbage();
        }
 
-       if (gc_write_commit_graph)
-               write_commit_graph_reachable(get_object_directory(), 0,
-                                            !quiet && !daemonized);
+       if (gc_write_commit_graph &&
+           write_commit_graph_reachable(get_object_directory(),
+                                        !quiet && !daemonized ? COMMIT_GRAPH_PROGRESS : 0))
+               return 1;
 
        if (auto_gc && too_many_loose_objects())
                warning(_("There are too many unreachable loose objects; "
index 8ae40dec4746571cf80d2f76bbad067c33a972fa..f101d092b883e6554cce193c66626b5707252c3f 100644 (file)
@@ -10,6 +10,7 @@
 #include "parse-options.h"
 #include "string-list.h"
 #include "trailer.h"
+#include "config.h"
 
 static const char * const git_interpret_trailers_usage[] = {
        N_("git interpret-trailers [--in-place] [--trim-empty] [(--trailer <token>[(=|:)<value>])...] [<file>...]"),
@@ -112,6 +113,8 @@ int cmd_interpret_trailers(int argc, const char **argv, const char *prefix)
                OPT_END()
        };
 
+       git_config(git_default_config, NULL);
+
        argc = parse_options(argc, argv, prefix, options,
                             git_interpret_trailers_usage, 0);
 
index 7f83c9a6f26bd92e5e3832a8ea8d6ec44d5a5f5a..670e8fb93c9320686410cabb8938a106be8e8158 100644 (file)
@@ -373,7 +373,7 @@ static void prune_index(struct index_state *istate,
        first = pos;
        last = istate->cache_nr;
        while (last > first) {
-               int next = (last + first) >> 1;
+               int next = first + ((last - first) >> 1);
                const struct cache_entry *ce = istate->cache[next];
                if (!strncmp(ce->name, prefix, prefixlen)) {
                        first = next+1;
index 57c2a24f6d82fc7cedcc86979db6ff1a8fa6bc50..29988e54c5be5ecb3afa2382ba73a965e1d2b511 100644 (file)
@@ -457,7 +457,7 @@ static void finish(struct commit *head_commit,
                         * We ignore errors in 'gc --auto', since the
                         * user should see them.
                         */
-                       close_all_packs(the_repository->objects);
+                       close_object_store(the_repository->objects);
                        run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
                }
        }
index 41d7fc59830c090d8b0422980528e4ea429c9e6b..787ae1028844c9be009feaf487c7bc9ce5f46cee 100644 (file)
@@ -3134,7 +3134,7 @@ static void get_object_list(int ac, const char **av)
                return;
 
        if (use_delta_islands)
-               load_delta_islands(the_repository);
+               load_delta_islands(the_repository, progress);
 
        if (prepare_revision_walk(&revs))
                die(_("revision walk setup failed"));
index db6ca9bd7d4c4e9cdf58eb47fbfce2dda5b58497..2748fa6f2e91d2b788d5b54edfb15de29d8aa833 100644 (file)
@@ -738,20 +738,30 @@ static int finish_rebase(struct rebase_options *opts)
 {
        struct strbuf dir = STRBUF_INIT;
        const char *argv_gc_auto[] = { "gc", "--auto", NULL };
+       int ret = 0;
 
        delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
        apply_autostash(opts);
-       close_all_packs(the_repository->objects);
+       close_object_store(the_repository->objects);
        /*
         * We ignore errors in 'gc --auto', since the
         * user should see them.
         */
        run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
-       strbuf_addstr(&dir, opts->state_dir);
-       remove_dir_recursively(&dir, 0);
-       strbuf_release(&dir);
+       if (opts->type == REBASE_INTERACTIVE) {
+               struct replay_opts replay = REPLAY_OPTS_INIT;
 
-       return 0;
+               replay.action = REPLAY_INTERACTIVE_REBASE;
+               ret = sequencer_remove_state(&replay);
+       } else {
+               strbuf_addstr(&dir, opts->state_dir);
+               if (remove_dir_recursively(&dir, 0))
+                       ret = error(_("could not remove '%s'"),
+                                   opts->state_dir);
+               strbuf_release(&dir);
+       }
+
+       return ret;
 }
 
 static struct commit *peel_committish(const char *name)
@@ -1384,6 +1394,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
        struct string_list strategy_options = STRING_LIST_INIT_NODUP;
        struct object_id squash_onto;
        char *squash_onto_name = NULL;
+       int reschedule_failed_exec = -1;
        struct option builtin_rebase_options[] = {
                OPT_STRING(0, "onto", &options.onto_name,
                           N_("revision"),
@@ -1476,7 +1487,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                OPT_BOOL(0, "root", &options.root,
                         N_("rebase all reachable commits up to the root(s)")),
                OPT_BOOL(0, "reschedule-failed-exec",
-                        &options.reschedule_failed_exec,
+                        &reschedule_failed_exec,
                         N_("automatically re-schedule any `exec` that fails")),
                OPT_END(),
        };
@@ -1626,15 +1637,23 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                        die(_("could not move back to %s"),
                            oid_to_hex(&options.orig_head));
                remove_branch_state(the_repository);
-               ret = finish_rebase(&options);
+               ret = !!finish_rebase(&options);
                goto cleanup;
        }
        case ACTION_QUIT: {
-               strbuf_reset(&buf);
-               strbuf_addstr(&buf, options.state_dir);
-               ret = !!remove_dir_recursively(&buf, 0);
-               if (ret)
-                       die(_("could not remove '%s'"), options.state_dir);
+               if (options.type == REBASE_INTERACTIVE) {
+                       struct replay_opts replay = REPLAY_OPTS_INIT;
+
+                       replay.action = REPLAY_INTERACTIVE_REBASE;
+                       ret = !!sequencer_remove_state(&replay);
+               } else {
+                       strbuf_reset(&buf);
+                       strbuf_addstr(&buf, options.state_dir);
+                       ret = !!remove_dir_recursively(&buf, 0);
+                       if (ret)
+                               error(_("could not remove '%s'"),
+                                      options.state_dir);
+               }
                goto cleanup;
        }
        case ACTION_EDIT_TODO:
@@ -1783,8 +1802,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                break;
        }
 
-       if (options.reschedule_failed_exec && !is_interactive(&options))
-               die(_("%s requires an interactive rebase"), "--reschedule-failed-exec");
+       if (reschedule_failed_exec > 0 && !is_interactive(&options))
+               die(_("--reschedule-failed-exec requires "
+                     "--exec or --interactive"));
+       if (reschedule_failed_exec >= 0)
+               options.reschedule_failed_exec = reschedule_failed_exec;
 
        if (options.git_am_opts.argc) {
                /* all am options except -q are compatible only with --am */
@@ -2146,6 +2168,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
        ret = !!run_specific_rebase(&options, action);
 
 cleanup:
+       strbuf_release(&buf);
        strbuf_release(&revisions);
        free(options.head_name);
        free(options.gpg_sign_opt);
index 29f165d8bd3513a6f85667b070e8f157994364bb..c5f5da940b0dee3fd2df55e11514ee9553926e30 100644 (file)
@@ -2043,7 +2043,7 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
                        proc.git_cmd = 1;
                        proc.argv = argv_gc_auto;
 
-                       close_all_packs(the_repository->objects);
+                       close_object_store(the_repository->objects);
                        if (!start_command(&proc)) {
                                if (use_sideband)
                                        copy_to_sideband(proc.err, -1, NULL);
index caca11392713eb92816d1e503f286bfe0d9be78a..f834b5551b1ffe003b943c18cd35397e9196e730 100644 (file)
@@ -422,7 +422,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
        if (!names.nr && !po_args.quiet)
                printf_ln(_("Nothing new to pack."));
 
-       close_all_packs(the_repository->objects);
+       close_object_store(the_repository->objects);
 
        /*
         * Ok we have prepared all new packfiles.
index 90cbe896c99188130a36a18aa18a57cd080be591..bf4a443e1369c2ef6131e5ac34be30c5a5d5b19f 100644 (file)
@@ -273,7 +273,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
        parse_pathspec(&pathspec, 0,
                       PATHSPEC_PREFER_CWD,
                       prefix, argv);
-       refresh_index(&the_index, REFRESH_QUIET, &pathspec, NULL, NULL);
+       refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, &pathspec, NULL, NULL);
 
        seen = xcalloc(pathspec.nr, 1);
 
index 2a8e6d09b406345519201ef64f572197a409e7a4..fde6397caa16326912087215383506026d00ab79 100644 (file)
@@ -713,11 +713,11 @@ static int git_stash_config(const char *var, const char *value, void *cb)
 static int show_stash(int argc, const char **argv, const char *prefix)
 {
        int i;
-       int opts = 0;
        int ret = 0;
        struct stash_info info;
        struct rev_info rev;
        struct argv_array stash_args = ARGV_ARRAY_INIT;
+       struct argv_array revision_args = ARGV_ARRAY_INIT;
        struct option options[] = {
                OPT_END()
        };
@@ -726,11 +726,12 @@ static int show_stash(int argc, const char **argv, const char *prefix)
        git_config(git_diff_ui_config, NULL);
        init_revisions(&rev, prefix);
 
+       argv_array_push(&revision_args, argv[0]);
        for (i = 1; i < argc; i++) {
                if (argv[i][0] != '-')
                        argv_array_push(&stash_args, argv[i]);
                else
-                       opts++;
+                       argv_array_push(&revision_args, argv[i]);
        }
 
        ret = get_stash_info(&info, stash_args.argc, stash_args.argv);
@@ -742,7 +743,7 @@ static int show_stash(int argc, const char **argv, const char *prefix)
         * The config settings are applied only if there are not passed
         * any options.
         */
-       if (!opts) {
+       if (revision_args.argc == 1) {
                git_config(git_stash_config, NULL);
                if (show_stat)
                        rev.diffopt.output_format = DIFF_FORMAT_DIFFSTAT;
@@ -756,7 +757,7 @@ static int show_stash(int argc, const char **argv, const char *prefix)
                }
        }
 
-       argc = setup_revisions(argc, argv, &rev, NULL);
+       argc = setup_revisions(revision_args.argc, revision_args.argv, &rev, NULL);
        if (argc > 1) {
                free_stash_info(&info);
                usage_with_options(git_stash_show_usage, options);
index 0bf4aa088e0ca4bf6378d121cfc23e0f42ac3af4..afaf0819c981ff896bf6f0b843b594a660d85a97 100644 (file)
@@ -540,6 +540,7 @@ static void runcommand_in_submodule_cb(const struct cache_entry *list_item,
                if (info->quiet)
                        argv_array_push(&cpr.args, "--quiet");
 
+               argv_array_push(&cpr.args, "--");
                argv_array_pushv(&cpr.args, info->argv);
 
                if (run_command(&cpr))
diff --git a/cache.h b/cache.h
index bf20337ef435234acf171f76137e683e66fb22d2..3e7cd0b4bc5528d00f3ec01d46db9e8a16d770e3 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -1759,6 +1759,7 @@ void setup_pager(void);
 int pager_in_use(void);
 extern int pager_use_color;
 int term_columns(void);
+void term_clear_line(void);
 int decimal_width(uintmax_t);
 int check_pager_config(const char *cmd);
 void prepare_pager_args(struct child_process *, const char *pager);
index 7f6acdd803c33bc43200804ecec5d99f6d404224..8cc72503cb768573bc6d284eb57910ba6ca5a5cb 100755 (executable)
@@ -34,7 +34,7 @@ linux-clang|linux-gcc)
        popd
        ;;
 osx-clang|osx-gcc)
-       brew update >/dev/null
+       export HOMEBREW_NO_AUTO_UPDATE=1 HOMEBREW_NO_INSTALL_CLEANUP=1
        # Uncomment this if you want to run perf tests:
        # brew install gnu-time
        test -z "$BREW_INSTALL_PACKAGES" ||
index 288a5b3884ad825c99601ba5dc62ee81c62d7d64..0c7171a17354fd6dba5017e443b3d1b936f73e34 100755 (executable)
--- a/ci/lib.sh
+++ b/ci/lib.sh
@@ -163,8 +163,10 @@ linux-clang|linux-gcc)
        export GIT_TEST_HTTPD=YesPlease
 
        # The Linux build installs the defined dependency versions below.
-       # The OS X build installs the latest available versions. Keep that
-       # in mind when you encounter a broken OS X build!
+       # The OS X build installs much more recent versions, whichever
+       # were recorded in the Homebrew database upon creating the OS X
+       # image.
+       # Keep that in mind when you encounter a broken OS X build!
        export LINUX_P4_VERSION="16.2"
        export LINUX_GIT_LFS_VERSION="1.5.2"
 
index 7c5e54875fdacdf77235a077c9928f7d3bf0d001..1752341098c328da1e9f6bce230e6b04730ffa11 100644 (file)
@@ -361,10 +361,10 @@ int generation_numbers_enabled(struct repository *r)
        return !!first_generation;
 }
 
-void close_commit_graph(struct repository *r)
+void close_commit_graph(struct raw_object_store *o)
 {
-       free_commit_graph(r->objects->commit_graph);
-       r->objects->commit_graph = NULL;
+       free_commit_graph(o->commit_graph);
+       o->commit_graph = NULL;
 }
 
 static int bsearch_graph(struct commit_graph *g, struct object_id *oid, uint32_t *pos)
@@ -525,14 +525,38 @@ struct tree *get_commit_tree_in_graph(struct repository *r, const struct commit
        return get_commit_tree_in_graph_one(r, r->objects->commit_graph, c);
 }
 
+struct packed_commit_list {
+       struct commit **list;
+       int nr;
+       int alloc;
+};
+
+struct packed_oid_list {
+       struct object_id *list;
+       int nr;
+       int alloc;
+};
+
+struct write_commit_graph_context {
+       struct repository *r;
+       const char *obj_dir;
+       char *graph_name;
+       struct packed_oid_list oids;
+       struct packed_commit_list commits;
+       int num_extra_edges;
+       unsigned long approx_nr_objects;
+       struct progress *progress;
+       int progress_done;
+       uint64_t progress_cnt;
+       unsigned append:1,
+                report_progress:1;
+};
+
 static void write_graph_chunk_fanout(struct hashfile *f,
-                                    struct commit **commits,
-                                    int nr_commits,
-                                    struct progress *progress,
-                                    uint64_t *progress_cnt)
+                                    struct write_commit_graph_context *ctx)
 {
        int i, count = 0;
-       struct commit **list = commits;
+       struct commit **list = ctx->commits.list;
 
        /*
         * Write the first-level table (the list is sorted,
@@ -540,10 +564,10 @@ static void write_graph_chunk_fanout(struct hashfile *f,
         * having to do eight extra binary search iterations).
         */
        for (i = 0; i < 256; i++) {
-               while (count < nr_commits) {
+               while (count < ctx->commits.nr) {
                        if ((*list)->object.oid.hash[0] != i)
                                break;
-                       display_progress(progress, ++*progress_cnt);
+                       display_progress(ctx->progress, ++ctx->progress_cnt);
                        count++;
                        list++;
                }
@@ -553,14 +577,12 @@ static void write_graph_chunk_fanout(struct hashfile *f,
 }
 
 static void write_graph_chunk_oids(struct hashfile *f, int hash_len,
-                                  struct commit **commits, int nr_commits,
-                                  struct progress *progress,
-                                  uint64_t *progress_cnt)
+                                  struct write_commit_graph_context *ctx)
 {
-       struct commit **list = commits;
+       struct commit **list = ctx->commits.list;
        int count;
-       for (count = 0; count < nr_commits; count++, list++) {
-               display_progress(progress, ++*progress_cnt);
+       for (count = 0; count < ctx->commits.nr; count++, list++) {
+               display_progress(ctx->progress, ++ctx->progress_cnt);
                hashwrite(f, (*list)->object.oid.hash, (int)hash_len);
        }
 }
@@ -572,19 +594,17 @@ static const unsigned char *commit_to_sha1(size_t index, void *table)
 }
 
 static void write_graph_chunk_data(struct hashfile *f, int hash_len,
-                                  struct commit **commits, int nr_commits,
-                                  struct progress *progress,
-                                  uint64_t *progress_cnt)
+                                  struct write_commit_graph_context *ctx)
 {
-       struct commit **list = commits;
-       struct commit **last = commits + nr_commits;
+       struct commit **list = ctx->commits.list;
+       struct commit **last = ctx->commits.list + ctx->commits.nr;
        uint32_t num_extra_edges = 0;
 
        while (list < last) {
                struct commit_list *parent;
                int edge_value;
                uint32_t packedDate[2];
-               display_progress(progress, ++*progress_cnt);
+               display_progress(ctx->progress, ++ctx->progress_cnt);
 
                parse_commit_no_graph(*list);
                hashwrite(f, get_commit_tree_oid(*list)->hash, hash_len);
@@ -595,8 +615,8 @@ static void write_graph_chunk_data(struct hashfile *f, int hash_len,
                        edge_value = GRAPH_PARENT_NONE;
                else {
                        edge_value = sha1_pos(parent->item->object.oid.hash,
-                                             commits,
-                                             nr_commits,
+                                             ctx->commits.list,
+                                             ctx->commits.nr,
                                              commit_to_sha1);
 
                        if (edge_value < 0)
@@ -616,8 +636,8 @@ static void write_graph_chunk_data(struct hashfile *f, int hash_len,
                        edge_value = GRAPH_EXTRA_EDGES_NEEDED | num_extra_edges;
                else {
                        edge_value = sha1_pos(parent->item->object.oid.hash,
-                                             commits,
-                                             nr_commits,
+                                             ctx->commits.list,
+                                             ctx->commits.nr,
                                              commit_to_sha1);
                        if (edge_value < 0)
                                BUG("missing parent %s for commit %s",
@@ -649,19 +669,16 @@ static void write_graph_chunk_data(struct hashfile *f, int hash_len,
 }
 
 static void write_graph_chunk_extra_edges(struct hashfile *f,
-                                         struct commit **commits,
-                                         int nr_commits,
-                                         struct progress *progress,
-                                         uint64_t *progress_cnt)
+                                         struct write_commit_graph_context *ctx)
 {
-       struct commit **list = commits;
-       struct commit **last = commits + nr_commits;
+       struct commit **list = ctx->commits.list;
+       struct commit **last = ctx->commits.list + ctx->commits.nr;
        struct commit_list *parent;
 
        while (list < last) {
                int num_parents = 0;
 
-               display_progress(progress, ++*progress_cnt);
+               display_progress(ctx->progress, ++ctx->progress_cnt);
 
                for (parent = (*list)->parents; num_parents < 3 && parent;
                     parent = parent->next)
@@ -675,8 +692,8 @@ static void write_graph_chunk_extra_edges(struct hashfile *f,
                /* Since num_parents > 2, this initializer is safe. */
                for (parent = (*list)->parents->next; parent; parent = parent->next) {
                        int edge_value = sha1_pos(parent->item->object.oid.hash,
-                                                 commits,
-                                                 nr_commits,
+                                                 ctx->commits.list,
+                                                 ctx->commits.nr,
                                                  commit_to_sha1);
 
                        if (edge_value < 0)
@@ -700,125 +717,111 @@ static int commit_compare(const void *_a, const void *_b)
        return oidcmp(a, b);
 }
 
-struct packed_commit_list {
-       struct commit **list;
-       int nr;
-       int alloc;
-};
-
-struct packed_oid_list {
-       struct object_id *list;
-       int nr;
-       int alloc;
-       struct progress *progress;
-       int progress_done;
-};
-
 static int add_packed_commits(const struct object_id *oid,
                              struct packed_git *pack,
                              uint32_t pos,
                              void *data)
 {
-       struct packed_oid_list *list = (struct packed_oid_list*)data;
+       struct write_commit_graph_context *ctx = (struct write_commit_graph_context*)data;
        enum object_type type;
        off_t offset = nth_packed_object_offset(pack, pos);
        struct object_info oi = OBJECT_INFO_INIT;
 
-       if (list->progress)
-               display_progress(list->progress, ++list->progress_done);
+       if (ctx->progress)
+               display_progress(ctx->progress, ++ctx->progress_done);
 
        oi.typep = &type;
-       if (packed_object_info(the_repository, pack, offset, &oi) < 0)
+       if (packed_object_info(ctx->r, pack, offset, &oi) < 0)
                die(_("unable to get type of object %s"), oid_to_hex(oid));
 
        if (type != OBJ_COMMIT)
                return 0;
 
-       ALLOC_GROW(list->list, list->nr + 1, list->alloc);
-       oidcpy(&(list->list[list->nr]), oid);
-       list->nr++;
+       ALLOC_GROW(ctx->oids.list, ctx->oids.nr + 1, ctx->oids.alloc);
+       oidcpy(&(ctx->oids.list[ctx->oids.nr]), oid);
+       ctx->oids.nr++;
 
        return 0;
 }
 
-static void add_missing_parents(struct packed_oid_list *oids, struct commit *commit)
+static void add_missing_parents(struct write_commit_graph_context *ctx, struct commit *commit)
 {
        struct commit_list *parent;
        for (parent = commit->parents; parent; parent = parent->next) {
                if (!(parent->item->object.flags & UNINTERESTING)) {
-                       ALLOC_GROW(oids->list, oids->nr + 1, oids->alloc);
-                       oidcpy(&oids->list[oids->nr], &(parent->item->object.oid));
-                       oids->nr++;
+                       ALLOC_GROW(ctx->oids.list, ctx->oids.nr + 1, ctx->oids.alloc);
+                       oidcpy(&ctx->oids.list[ctx->oids.nr], &(parent->item->object.oid));
+                       ctx->oids.nr++;
                        parent->item->object.flags |= UNINTERESTING;
                }
        }
 }
 
-static void close_reachable(struct packed_oid_list *oids, int report_progress)
+static void close_reachable(struct write_commit_graph_context *ctx)
 {
        int i;
        struct commit *commit;
-       struct progress *progress = NULL;
 
-       if (report_progress)
-               progress = start_delayed_progress(
-                       _("Loading known commits in commit graph"), oids->nr);
-       for (i = 0; i < oids->nr; i++) {
-               display_progress(progress, i + 1);
-               commit = lookup_commit(the_repository, &oids->list[i]);
+       if (ctx->report_progress)
+               ctx->progress = start_delayed_progress(
+                                       _("Loading known commits in commit graph"),
+                                       ctx->oids.nr);
+       for (i = 0; i < ctx->oids.nr; i++) {
+               display_progress(ctx->progress, i + 1);
+               commit = lookup_commit(ctx->r, &ctx->oids.list[i]);
                if (commit)
                        commit->object.flags |= UNINTERESTING;
        }
-       stop_progress(&progress);
+       stop_progress(&ctx->progress);
 
        /*
-        * As this loop runs, oids->nr may grow, but not more
+        * As this loop runs, ctx->oids.nr may grow, but not more
         * than the number of missing commits in the reachable
         * closure.
         */
-       if (report_progress)
-               progress = start_delayed_progress(
-                       _("Expanding reachable commits in commit graph"), oids->nr);
-       for (i = 0; i < oids->nr; i++) {
-               display_progress(progress, i + 1);
-               commit = lookup_commit(the_repository, &oids->list[i]);
+       if (ctx->report_progress)
+               ctx->progress = start_delayed_progress(
+                                       _("Expanding reachable commits in commit graph"),
+                                       ctx->oids.nr);
+       for (i = 0; i < ctx->oids.nr; i++) {
+               display_progress(ctx->progress, i + 1);
+               commit = lookup_commit(ctx->r, &ctx->oids.list[i]);
 
                if (commit && !parse_commit_no_graph(commit))
-                       add_missing_parents(oids, commit);
+                       add_missing_parents(ctx, commit);
        }
-       stop_progress(&progress);
+       stop_progress(&ctx->progress);
 
-       if (report_progress)
-               progress = start_delayed_progress(
-                       _("Clearing commit marks in commit graph"), oids->nr);
-       for (i = 0; i < oids->nr; i++) {
-               display_progress(progress, i + 1);
-               commit = lookup_commit(the_repository, &oids->list[i]);
+       if (ctx->report_progress)
+               ctx->progress = start_delayed_progress(
+                                       _("Clearing commit marks in commit graph"),
+                                       ctx->oids.nr);
+       for (i = 0; i < ctx->oids.nr; i++) {
+               display_progress(ctx->progress, i + 1);
+               commit = lookup_commit(ctx->r, &ctx->oids.list[i]);
 
                if (commit)
                        commit->object.flags &= ~UNINTERESTING;
        }
-       stop_progress(&progress);
+       stop_progress(&ctx->progress);
 }
 
-static void compute_generation_numbers(struct packed_commit_list* commits,
-                                      int report_progress)
+static void compute_generation_numbers(struct write_commit_graph_context *ctx)
 {
        int i;
        struct commit_list *list = NULL;
-       struct progress *progress = NULL;
 
-       if (report_progress)
-               progress = start_progress(
-                       _("Computing commit graph generation numbers"),
-                       commits->nr);
-       for (i = 0; i < commits->nr; i++) {
-               display_progress(progress, i + 1);
-               if (commits->list[i]->generation != GENERATION_NUMBER_INFINITY &&
-                   commits->list[i]->generation != GENERATION_NUMBER_ZERO)
+       if (ctx->report_progress)
+               ctx->progress = start_progress(
+                                       _("Computing commit graph generation numbers"),
+                                       ctx->commits.nr);
+       for (i = 0; i < ctx->commits.nr; i++) {
+               display_progress(ctx->progress, i + 1);
+               if (ctx->commits.list[i]->generation != GENERATION_NUMBER_INFINITY &&
+                   ctx->commits.list[i]->generation != GENERATION_NUMBER_ZERO)
                        continue;
 
-               commit_list_insert(commits->list[i], &list);
+               commit_list_insert(ctx->commits.list[i], &list);
                while (list) {
                        struct commit *current = list->item;
                        struct commit_list *parent;
@@ -845,7 +848,7 @@ static void compute_generation_numbers(struct packed_commit_list* commits,
                        }
                }
        }
-       stop_progress(&progress);
+       stop_progress(&ctx->progress);
 }
 
 static int add_ref_to_list(const char *refname,
@@ -858,207 +861,187 @@ static int add_ref_to_list(const char *refname,
        return 0;
 }
 
-void write_commit_graph_reachable(const char *obj_dir, int append,
-                                 int report_progress)
+int write_commit_graph_reachable(const char *obj_dir, unsigned int flags)
 {
        struct string_list list = STRING_LIST_INIT_DUP;
+       int result;
 
        for_each_ref(add_ref_to_list, &list);
-       write_commit_graph(obj_dir, NULL, &list, append, report_progress);
+       result = write_commit_graph(obj_dir, NULL, &list,
+                                   flags);
 
        string_list_clear(&list, 0);
+       return result;
 }
 
-void write_commit_graph(const char *obj_dir,
-                       struct string_list *pack_indexes,
-                       struct string_list *commit_hex,
-                       int append, int report_progress)
+static int fill_oids_from_packs(struct write_commit_graph_context *ctx,
+                               struct string_list *pack_indexes)
 {
-       struct packed_oid_list oids;
-       struct packed_commit_list commits;
-       struct hashfile *f;
-       uint32_t i, count_distinct = 0;
-       char *graph_name;
-       struct lock_file lk = LOCK_INIT;
-       uint32_t chunk_ids[5];
-       uint64_t chunk_offsets[5];
-       int num_chunks;
-       int num_extra_edges;
-       struct commit_list *parent;
-       struct progress *progress = NULL;
-       const unsigned hashsz = the_hash_algo->rawsz;
-       uint64_t progress_cnt = 0;
+       uint32_t i;
        struct strbuf progress_title = STRBUF_INIT;
-       unsigned long approx_nr_objects;
-
-       if (!commit_graph_compatible(the_repository))
-               return;
-
-       oids.nr = 0;
-       approx_nr_objects = approximate_object_count();
-       oids.alloc = approx_nr_objects / 32;
-       oids.progress = NULL;
-       oids.progress_done = 0;
+       struct strbuf packname = STRBUF_INIT;
+       int dirlen;
 
-       if (append) {
-               prepare_commit_graph_one(the_repository, obj_dir);
-               if (the_repository->objects->commit_graph)
-                       oids.alloc += the_repository->objects->commit_graph->num_commits;
+       strbuf_addf(&packname, "%s/pack/", ctx->obj_dir);
+       dirlen = packname.len;
+       if (ctx->report_progress) {
+               strbuf_addf(&progress_title,
+                           Q_("Finding commits for commit graph in %d pack",
+                              "Finding commits for commit graph in %d packs",
+                              pack_indexes->nr),
+                           pack_indexes->nr);
+               ctx->progress = start_delayed_progress(progress_title.buf, 0);
+               ctx->progress_done = 0;
        }
-
-       if (oids.alloc < 1024)
-               oids.alloc = 1024;
-       ALLOC_ARRAY(oids.list, oids.alloc);
-
-       if (append && the_repository->objects->commit_graph) {
-               struct commit_graph *commit_graph =
-                       the_repository->objects->commit_graph;
-               for (i = 0; i < commit_graph->num_commits; i++) {
-                       const unsigned char *hash = commit_graph->chunk_oid_lookup +
-                               commit_graph->hash_len * i;
-                       hashcpy(oids.list[oids.nr++].hash, hash);
+       for (i = 0; i < pack_indexes->nr; i++) {
+               struct packed_git *p;
+               strbuf_setlen(&packname, dirlen);
+               strbuf_addstr(&packname, pack_indexes->items[i].string);
+               p = add_packed_git(packname.buf, packname.len, 1);
+               if (!p) {
+                       error(_("error adding pack %s"), packname.buf);
+                       return -1;
+               }
+               if (open_pack_index(p)) {
+                       error(_("error opening index for %s"), packname.buf);
+                       return -1;
                }
+               for_each_object_in_pack(p, add_packed_commits, ctx,
+                                       FOR_EACH_OBJECT_PACK_ORDER);
+               close_pack(p);
+               free(p);
        }
 
-       if (pack_indexes) {
-               struct strbuf packname = STRBUF_INIT;
-               int dirlen;
-               strbuf_addf(&packname, "%s/pack/", obj_dir);
-               dirlen = packname.len;
-               if (report_progress) {
-                       strbuf_addf(&progress_title,
-                                   Q_("Finding commits for commit graph in %d pack",
-                                      "Finding commits for commit graph in %d packs",
-                                      pack_indexes->nr),
-                                   pack_indexes->nr);
-                       oids.progress = start_delayed_progress(progress_title.buf, 0);
-                       oids.progress_done = 0;
-               }
-               for (i = 0; i < pack_indexes->nr; i++) {
-                       struct packed_git *p;
-                       strbuf_setlen(&packname, dirlen);
-                       strbuf_addstr(&packname, pack_indexes->items[i].string);
-                       p = add_packed_git(packname.buf, packname.len, 1);
-                       if (!p)
-                               die(_("error adding pack %s"), packname.buf);
-                       if (open_pack_index(p))
-                               die(_("error opening index for %s"), packname.buf);
-                       for_each_object_in_pack(p, add_packed_commits, &oids,
-                                               FOR_EACH_OBJECT_PACK_ORDER);
-                       close_pack(p);
-                       free(p);
-               }
-               stop_progress(&oids.progress);
-               strbuf_reset(&progress_title);
-               strbuf_release(&packname);
+       stop_progress(&ctx->progress);
+       strbuf_reset(&progress_title);
+       strbuf_release(&packname);
+
+       return 0;
+}
+
+static void fill_oids_from_commit_hex(struct write_commit_graph_context *ctx,
+                                     struct string_list *commit_hex)
+{
+       uint32_t i;
+       struct strbuf progress_title = STRBUF_INIT;
+
+       if (ctx->report_progress) {
+               strbuf_addf(&progress_title,
+                           Q_("Finding commits for commit graph from %d ref",
+                              "Finding commits for commit graph from %d refs",
+                              commit_hex->nr),
+                           commit_hex->nr);
+               ctx->progress = start_delayed_progress(
+                                       progress_title.buf,
+                                       commit_hex->nr);
        }
+       for (i = 0; i < commit_hex->nr; i++) {
+               const char *end;
+               struct object_id oid;
+               struct commit *result;
+
+               display_progress(ctx->progress, i + 1);
+               if (commit_hex->items[i].string &&
+                   parse_oid_hex(commit_hex->items[i].string, &oid, &end))
+                       continue;
 
-       if (commit_hex) {
-               if (report_progress) {
-                       strbuf_addf(&progress_title,
-                                   Q_("Finding commits for commit graph from %d ref",
-                                      "Finding commits for commit graph from %d refs",
-                                      commit_hex->nr),
-                                   commit_hex->nr);
-                       progress = start_delayed_progress(progress_title.buf,
-                                                         commit_hex->nr);
-               }
-               for (i = 0; i < commit_hex->nr; i++) {
-                       const char *end;
-                       struct object_id oid;
-                       struct commit *result;
-
-                       display_progress(progress, i + 1);
-                       if (commit_hex->items[i].string &&
-                           parse_oid_hex(commit_hex->items[i].string, &oid, &end))
-                               continue;
-
-                       result = lookup_commit_reference_gently(the_repository, &oid, 1);
-
-                       if (result) {
-                               ALLOC_GROW(oids.list, oids.nr + 1, oids.alloc);
-                               oidcpy(&oids.list[oids.nr], &(result->object.oid));
-                               oids.nr++;
-                       }
+               result = lookup_commit_reference_gently(ctx->r, &oid, 1);
+
+               if (result) {
+                       ALLOC_GROW(ctx->oids.list, ctx->oids.nr + 1, ctx->oids.alloc);
+                       oidcpy(&ctx->oids.list[ctx->oids.nr], &(result->object.oid));
+                       ctx->oids.nr++;
                }
-               stop_progress(&progress);
-               strbuf_reset(&progress_title);
        }
+       stop_progress(&ctx->progress);
+       strbuf_release(&progress_title);
+}
 
-       if (!pack_indexes && !commit_hex) {
-               if (report_progress)
-                       oids.progress = start_delayed_progress(
-                               _("Finding commits for commit graph among packed objects"),
-                               approx_nr_objects);
-               for_each_packed_object(add_packed_commits, &oids,
-                                      FOR_EACH_OBJECT_PACK_ORDER);
-               if (oids.progress_done < approx_nr_objects)
-                       display_progress(oids.progress, approx_nr_objects);
-               stop_progress(&oids.progress);
-       }
+static void fill_oids_from_all_packs(struct write_commit_graph_context *ctx)
+{
+       if (ctx->report_progress)
+               ctx->progress = start_delayed_progress(
+                       _("Finding commits for commit graph among packed objects"),
+                       ctx->approx_nr_objects);
+       for_each_packed_object(add_packed_commits, ctx,
+                              FOR_EACH_OBJECT_PACK_ORDER);
+       if (ctx->progress_done < ctx->approx_nr_objects)
+               display_progress(ctx->progress, ctx->approx_nr_objects);
+       stop_progress(&ctx->progress);
+}
 
-       close_reachable(&oids, report_progress);
+static uint32_t count_distinct_commits(struct write_commit_graph_context *ctx)
+{
+       uint32_t i, count_distinct = 1;
 
-       if (report_progress)
-               progress = start_delayed_progress(
+       if (ctx->report_progress)
+               ctx->progress = start_delayed_progress(
                        _("Counting distinct commits in commit graph"),
-                       oids.nr);
-       display_progress(progress, 0); /* TODO: Measure QSORT() progress */
-       QSORT(oids.list, oids.nr, commit_compare);
-       count_distinct = 1;
-       for (i = 1; i < oids.nr; i++) {
-               display_progress(progress, i + 1);
-               if (!oideq(&oids.list[i - 1], &oids.list[i]))
+                       ctx->oids.nr);
+       display_progress(ctx->progress, 0); /* TODO: Measure QSORT() progress */
+       QSORT(ctx->oids.list, ctx->oids.nr, commit_compare);
+
+       for (i = 1; i < ctx->oids.nr; i++) {
+               display_progress(ctx->progress, i + 1);
+               if (!oideq(&ctx->oids.list[i - 1], &ctx->oids.list[i]))
                        count_distinct++;
        }
-       stop_progress(&progress);
+       stop_progress(&ctx->progress);
 
-       if (count_distinct >= GRAPH_EDGE_LAST_MASK)
-               die(_("the commit graph format cannot write %d commits"), count_distinct);
+       return count_distinct;
+}
 
-       commits.nr = 0;
-       commits.alloc = count_distinct;
-       ALLOC_ARRAY(commits.list, commits.alloc);
+static void copy_oids_to_commits(struct write_commit_graph_context *ctx)
+{
+       uint32_t i;
+       struct commit_list *parent;
 
-       num_extra_edges = 0;
-       if (report_progress)
-               progress = start_delayed_progress(
+       ctx->num_extra_edges = 0;
+       if (ctx->report_progress)
+               ctx->progress = start_delayed_progress(
                        _("Finding extra edges in commit graph"),
-                       oids.nr);
-       for (i = 0; i < oids.nr; i++) {
+                       ctx->oids.nr);
+       for (i = 0; i < ctx->oids.nr; i++) {
                int num_parents = 0;
-               display_progress(progress, i + 1);
-               if (i > 0 && oideq(&oids.list[i - 1], &oids.list[i]))
+               display_progress(ctx->progress, i + 1);
+               if (i > 0 && oideq(&ctx->oids.list[i - 1], &ctx->oids.list[i]))
                        continue;
 
-               commits.list[commits.nr] = lookup_commit(the_repository, &oids.list[i]);
-               parse_commit_no_graph(commits.list[commits.nr]);
+               ctx->commits.list[ctx->commits.nr] = lookup_commit(ctx->r, &ctx->oids.list[i]);
+               parse_commit_no_graph(ctx->commits.list[ctx->commits.nr]);
 
-               for (parent = commits.list[commits.nr]->parents;
+               for (parent = ctx->commits.list[ctx->commits.nr]->parents;
                     parent; parent = parent->next)
                        num_parents++;
 
                if (num_parents > 2)
-                       num_extra_edges += num_parents - 1;
+                       ctx->num_extra_edges += num_parents - 1;
 
-               commits.nr++;
+               ctx->commits.nr++;
        }
-       num_chunks = num_extra_edges ? 4 : 3;
-       stop_progress(&progress);
-
-       if (commits.nr >= GRAPH_EDGE_LAST_MASK)
-               die(_("too many commits to write graph"));
-
-       compute_generation_numbers(&commits, report_progress);
+       stop_progress(&ctx->progress);
+}
 
-       graph_name = get_commit_graph_filename(obj_dir);
-       if (safe_create_leading_directories(graph_name)) {
-               UNLEAK(graph_name);
-               die_errno(_("unable to create leading directories of %s"),
-                         graph_name);
+static int write_commit_graph_file(struct write_commit_graph_context *ctx)
+{
+       uint32_t i;
+       struct hashfile *f;
+       struct lock_file lk = LOCK_INIT;
+       uint32_t chunk_ids[5];
+       uint64_t chunk_offsets[5];
+       const unsigned hashsz = the_hash_algo->rawsz;
+       struct strbuf progress_title = STRBUF_INIT;
+       int num_chunks = ctx->num_extra_edges ? 4 : 3;
+
+       ctx->graph_name = get_commit_graph_filename(ctx->obj_dir);
+       if (safe_create_leading_directories(ctx->graph_name)) {
+               UNLEAK(ctx->graph_name);
+               error(_("unable to create leading directories of %s"),
+                       ctx->graph_name);
+               return -1;
        }
 
-       hold_lock_file_for_update(&lk, graph_name, LOCK_DIE_ON_ERROR);
+       hold_lock_file_for_update(&lk, ctx->graph_name, LOCK_DIE_ON_ERROR);
        f = hashfd(lk.tempfile->fd, lk.tempfile->filename.buf);
 
        hashwrite_be32(f, GRAPH_SIGNATURE);
@@ -1071,7 +1054,7 @@ void write_commit_graph(const char *obj_dir,
        chunk_ids[0] = GRAPH_CHUNKID_OIDFANOUT;
        chunk_ids[1] = GRAPH_CHUNKID_OIDLOOKUP;
        chunk_ids[2] = GRAPH_CHUNKID_DATA;
-       if (num_extra_edges)
+       if (ctx->num_extra_edges)
                chunk_ids[3] = GRAPH_CHUNKID_EXTRAEDGES;
        else
                chunk_ids[3] = 0;
@@ -1079,9 +1062,9 @@ void write_commit_graph(const char *obj_dir,
 
        chunk_offsets[0] = 8 + (num_chunks + 1) * GRAPH_CHUNKLOOKUP_WIDTH;
        chunk_offsets[1] = chunk_offsets[0] + GRAPH_FANOUT_SIZE;
-       chunk_offsets[2] = chunk_offsets[1] + hashsz * commits.nr;
-       chunk_offsets[3] = chunk_offsets[2] + (hashsz + 16) * commits.nr;
-       chunk_offsets[4] = chunk_offsets[3] + 4 * num_extra_edges;
+       chunk_offsets[2] = chunk_offsets[1] + hashsz * ctx->commits.nr;
+       chunk_offsets[3] = chunk_offsets[2] + (hashsz + 16) * ctx->commits.nr;
+       chunk_offsets[4] = chunk_offsets[3] + 4 * ctx->num_extra_edges;
 
        for (i = 0; i <= num_chunks; i++) {
                uint32_t chunk_write[3];
@@ -1092,31 +1075,113 @@ void write_commit_graph(const char *obj_dir,
                hashwrite(f, chunk_write, 12);
        }
 
-       if (report_progress) {
+       if (ctx->report_progress) {
                strbuf_addf(&progress_title,
                            Q_("Writing out commit graph in %d pass",
                               "Writing out commit graph in %d passes",
                               num_chunks),
                            num_chunks);
-               progress = start_delayed_progress(
+               ctx->progress = start_delayed_progress(
                        progress_title.buf,
-                       num_chunks * commits.nr);
+                       num_chunks * ctx->commits.nr);
        }
-       write_graph_chunk_fanout(f, commits.list, commits.nr, progress, &progress_cnt);
-       write_graph_chunk_oids(f, hashsz, commits.list, commits.nr, progress, &progress_cnt);
-       write_graph_chunk_data(f, hashsz, commits.list, commits.nr, progress, &progress_cnt);
-       if (num_extra_edges)
-               write_graph_chunk_extra_edges(f, commits.list, commits.nr, progress, &progress_cnt);
-       stop_progress(&progress);
+       write_graph_chunk_fanout(f, ctx);
+       write_graph_chunk_oids(f, hashsz, ctx);
+       write_graph_chunk_data(f, hashsz, ctx);
+       if (ctx->num_extra_edges)
+               write_graph_chunk_extra_edges(f, ctx);
+       stop_progress(&ctx->progress);
        strbuf_release(&progress_title);
 
-       close_commit_graph(the_repository);
+       close_commit_graph(ctx->r->objects);
        finalize_hashfile(f, NULL, CSUM_HASH_IN_STREAM | CSUM_FSYNC);
        commit_lock_file(&lk);
 
-       free(graph_name);
-       free(commits.list);
-       free(oids.list);
+       return 0;
+}
+
+int write_commit_graph(const char *obj_dir,
+                      struct string_list *pack_indexes,
+                      struct string_list *commit_hex,
+                      unsigned int flags)
+{
+       struct write_commit_graph_context *ctx;
+       uint32_t i, count_distinct = 0;
+       int res = 0;
+
+       if (!commit_graph_compatible(the_repository))
+               return 0;
+
+       ctx = xcalloc(1, sizeof(struct write_commit_graph_context));
+       ctx->r = the_repository;
+       ctx->obj_dir = obj_dir;
+       ctx->append = flags & COMMIT_GRAPH_APPEND ? 1 : 0;
+       ctx->report_progress = flags & COMMIT_GRAPH_PROGRESS ? 1 : 0;
+
+       ctx->approx_nr_objects = approximate_object_count();
+       ctx->oids.alloc = ctx->approx_nr_objects / 32;
+
+       if (ctx->append) {
+               prepare_commit_graph_one(ctx->r, ctx->obj_dir);
+               if (ctx->r->objects->commit_graph)
+                       ctx->oids.alloc += ctx->r->objects->commit_graph->num_commits;
+       }
+
+       if (ctx->oids.alloc < 1024)
+               ctx->oids.alloc = 1024;
+       ALLOC_ARRAY(ctx->oids.list, ctx->oids.alloc);
+
+       if (ctx->append && ctx->r->objects->commit_graph) {
+               struct commit_graph *g = ctx->r->objects->commit_graph;
+               for (i = 0; i < g->num_commits; i++) {
+                       const unsigned char *hash = g->chunk_oid_lookup + g->hash_len * i;
+                       hashcpy(ctx->oids.list[ctx->oids.nr++].hash, hash);
+               }
+       }
+
+       if (pack_indexes) {
+               if ((res = fill_oids_from_packs(ctx, pack_indexes)))
+                       goto cleanup;
+       }
+
+       if (commit_hex)
+               fill_oids_from_commit_hex(ctx, commit_hex);
+
+       if (!pack_indexes && !commit_hex)
+               fill_oids_from_all_packs(ctx);
+
+       close_reachable(ctx);
+
+       count_distinct = count_distinct_commits(ctx);
+
+       if (count_distinct >= GRAPH_EDGE_LAST_MASK) {
+               error(_("the commit graph format cannot write %d commits"), count_distinct);
+               res = -1;
+               goto cleanup;
+       }
+
+       ctx->commits.alloc = count_distinct;
+       ALLOC_ARRAY(ctx->commits.list, ctx->commits.alloc);
+
+       copy_oids_to_commits(ctx);
+
+       if (ctx->commits.nr >= GRAPH_EDGE_LAST_MASK) {
+               error(_("too many commits to write graph"));
+               res = -1;
+               goto cleanup;
+       }
+
+       compute_generation_numbers(ctx);
+
+       res = write_commit_graph_file(ctx);
+
+cleanup:
+       free(ctx->graph_name);
+       free(ctx->commits.list);
+       free(ctx->oids.list);
+       free(ctx);
+
+       return res;
 }
 
 #define VERIFY_COMMIT_GRAPH_ERROR_HASH 2
index 7dfb8c896fc35f633c73221ec639ca9c425338ab..390c7f696104fbe772151b91cb04889d0f682401 100644 (file)
@@ -65,16 +65,24 @@ struct commit_graph *parse_commit_graph(void *graph_map, int fd,
  */
 int generation_numbers_enabled(struct repository *r);
 
-void write_commit_graph_reachable(const char *obj_dir, int append,
-                                 int report_progress);
-void write_commit_graph(const char *obj_dir,
-                       struct string_list *pack_indexes,
-                       struct string_list *commit_hex,
-                       int append, int report_progress);
+#define COMMIT_GRAPH_APPEND     (1 << 0)
+#define COMMIT_GRAPH_PROGRESS   (1 << 1)
+
+/*
+ * The write_commit_graph* methods return zero on success
+ * and a negative value on failure. Note that if the repository
+ * is not compatible with the commit-graph feature, then the
+ * methods will return 0 without writing a commit-graph.
+ */
+int write_commit_graph_reachable(const char *obj_dir, unsigned int flags);
+int write_commit_graph(const char *obj_dir,
+                      struct string_list *pack_indexes,
+                      struct string_list *commit_hex,
+                      unsigned int flags);
 
 int verify_commit_graph(struct repository *r, struct commit_graph *g);
 
-void close_commit_graph(struct repository *);
+void close_commit_graph(struct raw_object_store *);
 void free_commit_graph(struct commit_graph *);
 
 #endif
index 8fa1883c61c580578a755cdf2da009203d8d386e..26ce0770f688eb94d5be5975d756752dbc0337a8 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -449,7 +449,7 @@ int parse_commit_buffer(struct repository *r, struct commit *item, const void *b
        item->date = parse_commit_date(bufptr, tail);
 
        if (check_graph)
-               load_commit_graph_info(the_repository, item);
+               load_commit_graph_info(r, item);
 
        return 0;
 }
index 9b6d2400e1107fa2a65f6ecd7d067809115930b9..6d7fc07a487a0ffb023db5b45ba9eaa0b3c36bc9 100644 (file)
@@ -1437,7 +1437,9 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
        si.hStdOutput = winansi_get_osfhandle(fhout);
        si.hStdError = winansi_get_osfhandle(fherr);
 
-       if (xutftowcs_path(wcmd, cmd) < 0)
+       if (*argv && !strcmp(cmd, *argv))
+               wcmd[0] = L'\0';
+       else if (xutftowcs_path(wcmd, cmd) < 0)
                return -1;
        if (dir && xutftowcs_path(wdir, dir) < 0)
                return -1;
@@ -1466,8 +1468,8 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
        wenvblk = make_environment_block(deltaenv);
 
        memset(&pi, 0, sizeof(pi));
-       ret = CreateProcessW(wcmd, wargs, NULL, NULL, TRUE, flags,
-               wenvblk, dir ? wdir : NULL, &si, &pi);
+       ret = CreateProcessW(*wcmd ? wcmd : NULL, wargs, NULL, NULL, TRUE,
+               flags, wenvblk, dir ? wdir : NULL, &si, &pi);
 
        free(wenvblk);
        free(wargs);
index 4459408c7d0ba762b1dd4184a4c970778ba097a5..8b07edb0feca434cce2ff7766c30d7529febf996 100644 (file)
@@ -149,7 +149,7 @@ win32_compute_revents (HANDLE h, int *p_sought)
     case FILE_TYPE_PIPE:
       if (!once_only)
        {
-         NtQueryInformationFile = (PNtQueryInformationFile)
+         NtQueryInformationFile = (PNtQueryInformationFile)(void (*)(void))
            GetProcAddress (GetModuleHandle ("ntdll.dll"),
                            "NtQueryInformationFile");
          once_only = TRUE;
index f4f08237f9ed513e0dd3b3bfd9494f19944e3239..a29d34ef44864f02bd89c8490d38bca0efe2a8e6 100644 (file)
@@ -7,6 +7,7 @@
 #include <wingdi.h>
 #include <winreg.h>
 #include "win32.h"
+#include "win32/lazyload.h"
 
 static int fd_is_interactive[3] = { 0, 0, 0 };
 #define FD_CONSOLE 0x1
@@ -41,26 +42,21 @@ typedef struct _CONSOLE_FONT_INFOEX {
 #endif
 #endif
 
-typedef BOOL (WINAPI *PGETCURRENTCONSOLEFONTEX)(HANDLE, BOOL,
-               PCONSOLE_FONT_INFOEX);
-
 static void warn_if_raster_font(void)
 {
        DWORD fontFamily = 0;
-       PGETCURRENTCONSOLEFONTEX pGetCurrentConsoleFontEx;
+       DECLARE_PROC_ADDR(kernel32.dll, BOOL, GetCurrentConsoleFontEx,
+                       HANDLE, BOOL, PCONSOLE_FONT_INFOEX);
 
        /* don't bother if output was ascii only */
        if (!non_ascii_used)
                return;
 
        /* GetCurrentConsoleFontEx is available since Vista */
-       pGetCurrentConsoleFontEx = (PGETCURRENTCONSOLEFONTEX) GetProcAddress(
-                       GetModuleHandle("kernel32.dll"),
-                       "GetCurrentConsoleFontEx");
-       if (pGetCurrentConsoleFontEx) {
+       if (INIT_PROC_ADDR(GetCurrentConsoleFontEx)) {
                CONSOLE_FONT_INFOEX cfi;
                cfi.cbSize = sizeof(cfi);
-               if (pGetCurrentConsoleFontEx(console, 0, &cfi))
+               if (GetCurrentConsoleFontEx(console, 0, &cfi))
                        fontFamily = cfi.FontFamily;
        } else {
                /* pre-Vista: check default console font in registry */
index 296a6d9cc4110bd7fcef542ac9cc9cfe04d4f4d4..26196bdccfbc268d7ee669546d39a15e14703494 100644 (file)
--- a/config.c
+++ b/config.c
@@ -834,22 +834,16 @@ static int git_parse_source(config_fn_t fn, void *data,
        return error_return;
 }
 
-static int parse_unit_factor(const char *end, uintmax_t *val)
+static uintmax_t get_unit_factor(const char *end)
 {
        if (!*end)
                return 1;
-       else if (!strcasecmp(end, "k")) {
-               *val *= 1024;
-               return 1;
-       }
-       else if (!strcasecmp(end, "m")) {
-               *val *= 1024 * 1024;
-               return 1;
-       }
-       else if (!strcasecmp(end, "g")) {
-               *val *= 1024 * 1024 * 1024;
-               return 1;
-       }
+       else if (!strcasecmp(end, "k"))
+               return 1024;
+       else if (!strcasecmp(end, "m"))
+               return 1024 * 1024;
+       else if (!strcasecmp(end, "g"))
+               return 1024 * 1024 * 1024;
        return 0;
 }
 
@@ -859,19 +853,20 @@ static int git_parse_signed(const char *value, intmax_t *ret, intmax_t max)
                char *end;
                intmax_t val;
                uintmax_t uval;
-               uintmax_t factor = 1;
+               uintmax_t factor;
 
                errno = 0;
                val = strtoimax(value, &end, 0);
                if (errno == ERANGE)
                        return 0;
-               if (!parse_unit_factor(end, &factor)) {
+               factor = get_unit_factor(end);
+               if (!factor) {
                        errno = EINVAL;
                        return 0;
                }
-               uval = labs(val);
-               uval *= factor;
-               if (uval > max || labs(val) > uval) {
+               uval = val < 0 ? -val : val;
+               if (unsigned_mult_overflows(factor, uval) ||
+                   factor * uval > max) {
                        errno = ERANGE;
                        return 0;
                }
@@ -888,21 +883,23 @@ static int git_parse_unsigned(const char *value, uintmax_t *ret, uintmax_t max)
        if (value && *value) {
                char *end;
                uintmax_t val;
-               uintmax_t oldval;
+               uintmax_t factor;
 
                errno = 0;
                val = strtoumax(value, &end, 0);
                if (errno == ERANGE)
                        return 0;
-               oldval = val;
-               if (!parse_unit_factor(end, &val)) {
+               factor = get_unit_factor(end);
+               if (!factor) {
                        errno = EINVAL;
                        return 0;
                }
-               if (val > max || oldval > val) {
+               if (unsigned_mult_overflows(factor, val) ||
+                   factor * val > max) {
                        errno = ERANGE;
                        return 0;
                }
+               val *= factor;
                *ret = val;
                return 1;
        }
index 01586821dc7c15be2a94c30bfbf4f3b4c94ac5f3..46b8d2ee11151b97e4e0cfd56afcf059247d1c54 100644 (file)
@@ -1,29 +1,60 @@
 @@
-type T;
-T *dst;
-T *src;
-expression n;
+expression dst, src, n, E;
 @@
-- memcpy(dst, src, (n) * sizeof(*dst));
-+ COPY_ARRAY(dst, src, n);
+  memcpy(dst, src, n * sizeof(
+- E[...]
++ *(E)
+  ))
 
 @@
 type T;
-T *dst;
-T *src;
-expression n;
+T *ptr;
+T[] arr;
+expression E, n;
 @@
-- memcpy(dst, src, (n) * sizeof(*src));
-+ COPY_ARRAY(dst, src, n);
+(
+  memcpy(ptr, E,
+- n * sizeof(*(ptr))
++ n * sizeof(T)
+  )
+|
+  memcpy(arr, E,
+- n * sizeof(*(arr))
++ n * sizeof(T)
+  )
+|
+  memcpy(E, ptr,
+- n * sizeof(*(ptr))
++ n * sizeof(T)
+  )
+|
+  memcpy(E, arr,
+- n * sizeof(*(arr))
++ n * sizeof(T)
+  )
+)
 
 @@
 type T;
-T *dst;
-T *src;
+T *dst_ptr;
+T *src_ptr;
+T[] dst_arr;
+T[] src_arr;
 expression n;
 @@
-- memcpy(dst, src, (n) * sizeof(T));
-+ COPY_ARRAY(dst, src, n);
+(
+- memcpy(dst_ptr, src_ptr, (n) * sizeof(T))
++ COPY_ARRAY(dst_ptr, src_ptr, n)
+|
+- memcpy(dst_ptr, src_arr, (n) * sizeof(T))
++ COPY_ARRAY(dst_ptr, src_arr, n)
+|
+- memcpy(dst_arr, src_ptr, (n) * sizeof(T))
++ COPY_ARRAY(dst_arr, src_ptr, n)
+|
+- memcpy(dst_arr, src_arr, (n) * sizeof(T))
++ COPY_ARRAY(dst_arr, src_arr, n)
+)
 
 @@
 type T;
index 9f71bcde967bc50915b90eaef72061f8c6a56315..8c6b610a2447cd1ec7c2dff0f9dbd09f6a024f57 100644 (file)
@@ -400,7 +400,8 @@ __gitcomp_builtin ()
        if [ -z "$options" ]; then
                # leading and trailing spaces are significant to make
                # option removal work correctly.
-               options=" $incl $(__git ${cmd/_/ } --git-completion-helper) "
+               options=" $incl $(__git ${cmd/_/ } --git-completion-helper) " || return
+
                for i in $excl; do
                        options="${options/ $i / }"
                done
index 2186bd0738ed2fcbe216cf24a6c99ed4dc4ccd9b..b959f6c380a6f42849db570cf0a51fed23f2b2a8 100644 (file)
@@ -454,7 +454,7 @@ static void deduplicate_islands(struct repository *r)
        free(list);
 }
 
-void load_delta_islands(struct repository *r)
+void load_delta_islands(struct repository *r, int progress)
 {
        island_marks = kh_init_sha1();
        remote_islands = kh_init_str();
@@ -463,7 +463,8 @@ void load_delta_islands(struct repository *r)
        for_each_ref(find_island_for_ref, NULL);
        deduplicate_islands(r);
 
-       fprintf(stderr, _("Marked %d islands, done.\n"), island_counter);
+       if (progress)
+               fprintf(stderr, _("Marked %d islands, done.\n"), island_counter);
 }
 
 void propagate_island_marks(struct commit *commit)
index 3ac8045d8c528be81ac1cbefe9942c534e3c498a..eb0f952629fc0a6cdcc113e19a04ddaa54bb32dd 100644 (file)
@@ -11,7 +11,7 @@ int in_same_island(const struct object_id *, const struct object_id *);
 void resolve_tree_islands(struct repository *r,
                          int progress,
                          struct packing_data *to_pack);
-void load_delta_islands(struct repository *r);
+void load_delta_islands(struct repository *r, int progress);
 void propagate_island_marks(struct commit *commit);
 int compute_pack_layers(struct packing_data *to_pack);
 
diff --git a/diff.c b/diff.c
index a654d46f6a93de96d85706c7373ef0d617081a21..1ccd96bbfde23a0f6d42104e81f78e0953c1bc93 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -4206,6 +4206,8 @@ static void run_external_diff(const char *pgm,
        argv_array_pushf(&env, "GIT_DIFF_PATH_COUNTER=%d", ++o->diff_path_counter);
        argv_array_pushf(&env, "GIT_DIFF_PATH_TOTAL=%d", q->nr);
 
+       diff_free_filespec_data(one);
+       diff_free_filespec_data(two);
        if (run_command_v_opt_cd_env(argv.argv, RUN_USING_SHELL, NULL, env.argv))
                die(_("external diff died, stopping at %s"), name);
 
index 07bd34b63145e1dc179afebcc7af351c34c2ab07..6af92d5eba6584416d331c2f6e63bd7d403ca773 100644 (file)
@@ -23,7 +23,7 @@ static int find_rename_dst(struct diff_filespec *two)
        first = 0;
        last = rename_dst_nr;
        while (last > first) {
-               int next = (last + first) >> 1;
+               int next = first + ((last - first) >> 1);
                struct diff_rename_dst *dst = &(rename_dst[next]);
                int cmp = strcmp(two->path, dst->two->path);
                if (!cmp)
@@ -83,7 +83,7 @@ static struct diff_rename_src *register_rename_src(struct diff_filepair *p)
        first = 0;
        last = rename_src_nr;
        while (last > first) {
-               int next = (last + first) >> 1;
+               int next = first + ((last - first) >> 1);
                struct diff_rename_src *src = &(rename_src[next]);
                int cmp = strcmp(one->path, src->p->one->path);
                if (!cmp)
diff --git a/dir.c b/dir.c
index ba4a51c296efcad9861ebfb4b318fbd5cb3025a4..d021c908e5d162cfd4d961ab11d16b1b9eb7124a 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -701,7 +701,7 @@ static struct untracked_cache_dir *lookup_untracked(struct untracked_cache *uc,
        first = 0;
        last = dir->dirs_nr;
        while (last > first) {
-               int cmp, next = (last + first) >> 1;
+               int cmp, next = first + ((last - first) >> 1);
                d = dir->dirs[next];
                cmp = strncmp(name, d->name, len);
                if (!cmp && strlen(d->name) > len)
index 71547674ab4e885bd06560a547e78fddb0849158..f079abbf1102686fdb8e64258f456877f976dd03 100644 (file)
--- a/editor.c
+++ b/editor.c
@@ -96,10 +96,10 @@ static int launch_specified_editor(const char *editor, const char *path,
 
                if (print_waiting_for_editor && !is_terminal_dumb())
                        /*
-                        * Go back to the beginning and erase the entire line to
-                        * avoid wasting the vertical space.
+                        * Erase the entire line to avoid wasting the
+                        * vertical space.
                         */
-                       fputs("\r\033[K", stderr);
+                       term_clear_line();
        }
 
        if (!buffer)
index f38d04fa58510bb7ab35caf4c43d5b2d954cc292..606d44278d88b3ae23dac934dc1e77eb0b138c31 100644 (file)
@@ -644,7 +644,7 @@ static struct tree_content *grow_tree_content(
        struct tree_content *r = new_tree_content(t->entry_count + amt);
        r->entry_count = t->entry_count;
        r->delta_depth = t->delta_depth;
-       memcpy(r->entries,t->entries,t->entry_count*sizeof(t->entries[0]));
+       COPY_ARRAY(r->entries, t->entries, t->entry_count);
        release_tree_content(t);
        return r;
 }
index 1dee0aded1c433c7cfb90195328f270559c9b421..231e83a94db58e4efb2f4d7a1dbe90220bd2f317 100644 (file)
@@ -56,7 +56,7 @@ int read_fsmonitor_extension(struct index_state *istate, const void *data,
 
 void fill_fsmonitor_bitmap(struct index_state *istate)
 {
-       int i;
+       unsigned int i;
        istate->fsmonitor_dirty = ewah_new();
        for (i = 0; i < istate->cache_nr; i++)
                if (!(istate->cache[i]->ce_flags & CE_FSMONITOR_VALID))
@@ -134,7 +134,7 @@ void refresh_fsmonitor(struct index_state *istate)
        size_t bol; /* beginning of line */
        uint64_t last_update;
        char *buf;
-       int i;
+       unsigned int i;
 
        if (!core_fsmonitor || istate->fsmonitor_has_run_once)
                return;
@@ -192,7 +192,7 @@ void refresh_fsmonitor(struct index_state *istate)
 
 void add_fsmonitor(struct index_state *istate)
 {
-       int i;
+       unsigned int i;
 
        if (!istate->fsmonitor_last_update) {
                trace_printf_key(&trace_fsmonitor, "add fsmonitor");
@@ -225,7 +225,7 @@ void remove_fsmonitor(struct index_state *istate)
 
 void tweak_fsmonitor(struct index_state *istate)
 {
-       int i;
+       unsigned int i;
        int fsmonitor_enabled = git_config_get_fsmonitor();
 
        if (istate->fsmonitor_dirty) {
index 20eb81cc92f947d872b31a179d43d97772ff25e4..da5b4ec4bc50e4e3adaba0f25820262b6a7436a0 100755 (executable)
@@ -972,7 +972,11 @@ sub coalesce_overlapping_hunks {
                        next;
                }
                if ($ofs_delta) {
-                       $n_ofs += $ofs_delta;
+                       if ($patch_mode_flavour{IS_REVERSE}) {
+                               $o_ofs -= $ofs_delta;
+                       } else {
+                               $n_ofs += $ofs_delta;
+                       }
                        $_->{TEXT}->[0] = format_hunk_header($o_ofs, $o_cnt,
                                                             $n_ofs, $n_cnt);
                }
index 8ed274533f87198a74a3660809c25932fa43124a..d60115ca404edfedc492c55e5bc0f22fe0eaa5a2 100644 (file)
@@ -116,6 +116,9 @@ static void parse_gpg_output(struct signature_check *sigc)
        for (line = buf; *line; line = strchrnul(line+1, '\n')) {
                while (*line == '\n')
                        line++;
+               if (!*line)
+                       break;
+
                /* Skip lines that don't start with GNUPG status */
                if (!skip_prefix(line, "[GNUPG:] ", &line))
                        continue;
diff --git a/kwset.c b/kwset.c
index 4fb6455acaf1293c4f47f27eb6e47be4d39633e2..fc439e0667f137f3449635a37a32f8418d5041f0 100644 (file)
--- a/kwset.c
+++ b/kwset.c
 #include "compat/obstack.h"
 
 #define NCHAR (UCHAR_MAX + 1)
-#define obstack_chunk_alloc xmalloc
+/* adapter for `xmalloc()`, which takes `size_t`, not `long` */
+static void *obstack_chunk_alloc(long size)
+{
+       if (size < 0)
+               BUG("Cannot allocate a negative amount: %ld", size);
+       return xmalloc(size);
+}
 #define obstack_chunk_free free
 
 #define U(c) ((unsigned char) (c))
@@ -475,7 +481,7 @@ kwsprep (kwset_t kws)
        for (i = 0; i < NCHAR; ++i)
          kwset->next[i] = next[U(trans[i])];
       else
-       memcpy(kwset->next, next, NCHAR * sizeof(struct trie *));
+       COPY_ARRAY(kwset->next, next, NCHAR);
     }
 
   /* Fix things up for any translation table. */
index b4861bc7b02a93bd5f3659098764f535bb1f51c1..695908609f40f9cbb3960162a311af5d452610e2 100644 (file)
@@ -345,8 +345,9 @@ static int handle_range_dir(
        else {
                int begin = k_start;
                int end = k_end;
+               assert(begin >= 0);
                while (begin < end) {
-                       int mid = (begin + end) >> 1;
+                       int mid = begin + ((end - begin) >> 1);
                        int cmp = strncmp(istate->cache[mid]->name, prefix->buf, prefix->len);
                        if (cmp == 0) /* mid has same prefix; look in second part */
                                begin = mid + 1;
index e81d47a79cd6eb42fae7c104be0e9ac1e5173e79..cf1a2b708612a0e335893fadee0952e620400c5f 100644 (file)
--- a/object.c
+++ b/object.c
@@ -517,7 +517,7 @@ void raw_object_store_clear(struct raw_object_store *o)
        o->loaded_alternates = 0;
 
        INIT_LIST_HEAD(&o->packed_git_mru);
-       close_all_packs(o);
+       close_object_store(o);
        o->packed_git = NULL;
 }
 
index d786ec731202e5d623a7558bec2168194db11514..c0d83fdfed973de8574224e46fb0afd4be0a98c9 100644 (file)
@@ -16,6 +16,7 @@
 #include "tree.h"
 #include "object-store.h"
 #include "midx.h"
+#include "commit-graph.h"
 
 char *odb_pack_name(struct strbuf *buf,
                    const unsigned char *sha1,
@@ -336,7 +337,7 @@ void close_pack(struct packed_git *p)
        close_pack_index(p);
 }
 
-void close_all_packs(struct raw_object_store *o)
+void close_object_store(struct raw_object_store *o)
 {
        struct packed_git *p;
 
@@ -350,6 +351,8 @@ void close_all_packs(struct raw_object_store *o)
                close_midx(o->multi_pack_index);
                o->multi_pack_index = NULL;
        }
+
+       close_commit_graph(o);
 }
 
 /*
@@ -1269,7 +1272,7 @@ static enum object_type packed_to_object_type(struct repository *r,
                if (poi_stack_nr >= poi_stack_alloc && poi_stack == small_poi_stack) {
                        poi_stack_alloc = alloc_nr(poi_stack_nr);
                        ALLOC_ARRAY(poi_stack, poi_stack_alloc);
-                       memcpy(poi_stack, small_poi_stack, sizeof(off_t)*poi_stack_nr);
+                       COPY_ARRAY(poi_stack, small_poi_stack, poi_stack_nr);
                } else {
                        ALLOC_GROW(poi_stack, poi_stack_nr+1, poi_stack_alloc);
                }
@@ -1679,8 +1682,8 @@ void *unpack_entry(struct repository *r, struct packed_git *p, off_t obj_offset,
                    && delta_stack == small_delta_stack) {
                        delta_stack_alloc = alloc_nr(delta_stack_nr);
                        ALLOC_ARRAY(delta_stack, delta_stack_alloc);
-                       memcpy(delta_stack, small_delta_stack,
-                              sizeof(*delta_stack)*delta_stack_nr);
+                       COPY_ARRAY(delta_stack, small_delta_stack,
+                                  delta_stack_nr);
                } else {
                        ALLOC_GROW(delta_stack, delta_stack_nr+1, delta_stack_alloc);
                }
index b678d35c0b6df11623f2d29f7b7ea7dfe0e1bea1..81e868d55a9b1f1aeaafa4925a72ae5c53af86e9 100644 (file)
@@ -90,7 +90,7 @@ uint32_t get_pack_fanout(struct packed_git *p, uint32_t value);
 unsigned char *use_pack(struct packed_git *, struct pack_window **, off_t, unsigned long *);
 void close_pack_windows(struct packed_git *);
 void close_pack(struct packed_git *);
-void close_all_packs(struct raw_object_store *o);
+void close_object_store(struct raw_object_store *o);
 void unuse_pack(struct pack_window **);
 void clear_delta_base_cache(void);
 struct packed_git *add_packed_git(const char *path, size_t path_len, int local);
diff --git a/pager.c b/pager.c
index 4168460ae92ceb18b4a31c4d0524b411231cf137..41446d4f0543df18fb2430188d886ed1975a55d9 100644 (file)
--- a/pager.c
+++ b/pager.c
@@ -177,6 +177,26 @@ int term_columns(void)
        return term_columns_at_startup;
 }
 
+/*
+ * Clear the entire line, leave cursor in first column.
+ */
+void term_clear_line(void)
+{
+       if (is_terminal_dumb())
+               /*
+                * Fall back to print a terminal width worth of space
+                * characters (hoping that the terminal is still as wide
+                * as it was upon the first call to term_columns()).
+                */
+               fprintf(stderr, "\r%*s\r", term_columns(), "");
+       else
+               /*
+                * On non-dumb terminals use an escape sequence to clear
+                * the whole line, no matter how wide the terminal.
+                */
+               fputs("\r\033[K", stderr);
+}
+
 /*
  * How many columns do we need to show this number in decimal?
  */
index ced0485257d3190f0c3ad8b89f15fa47c8660fad..e4ed14effe1aabdef50155b932a96e6648ff4707 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -106,8 +106,8 @@ static void setup_commit_formats(void)
        commit_formats_len = ARRAY_SIZE(builtin_formats);
        builtin_formats_len = commit_formats_len;
        ALLOC_GROW(commit_formats, commit_formats_len, commit_formats_alloc);
-       memcpy(commit_formats, builtin_formats,
-              sizeof(*builtin_formats)*ARRAY_SIZE(builtin_formats));
+       COPY_ARRAY(commit_formats, builtin_formats,
+                  ARRAY_SIZE(builtin_formats));
 
        git_config(git_pretty_formats_config, NULL);
 }
index a2e8cf64a8d1aa2f94c745bbb5098b9c771f3a05..095dcd0ddff2132a4dcfd59486ee4a5ddfa4684f 100644 (file)
@@ -88,7 +88,6 @@ static void display(struct progress *progress, uint64_t n, const char *done)
        const char *tp;
        struct strbuf *counters_sb = &progress->counters_sb;
        int show_update = 0;
-       int last_count_len = counters_sb->len;
 
        if (progress->delay && (!progress_update || --progress->delay))
                return;
@@ -116,26 +115,21 @@ static void display(struct progress *progress, uint64_t n, const char *done)
        if (show_update) {
                if (is_foreground_fd(fileno(stderr)) || done) {
                        const char *eol = done ? done : "\r";
-                       size_t clear_len = counters_sb->len < last_count_len ?
-                                       last_count_len - counters_sb->len + 1 :
-                                       0;
-                       size_t progress_line_len = progress->title_len +
-                                               counters_sb->len + 2;
-                       int cols = term_columns();
 
+                       term_clear_line();
                        if (progress->split) {
-                               fprintf(stderr, "  %s%*s", counters_sb->buf,
-                                       (int) clear_len, eol);
-                       } else if (!done && cols < progress_line_len) {
-                               clear_len = progress->title_len + 1 < cols ?
-                                           cols - progress->title_len - 1 : 0;
-                               fprintf(stderr, "%s:%*s\n  %s%s",
-                                       progress->title, (int) clear_len, "",
-                                       counters_sb->buf, eol);
+                               fprintf(stderr, "  %s%s", counters_sb->buf,
+                                       eol);
+                       } else if (!done &&
+                                  /* The "+ 2" accounts for the ": ". */
+                                  term_columns() < progress->title_len +
+                                                   counters_sb->len + 2) {
+                               fprintf(stderr, "%s:\n  %s%s",
+                                       progress->title, counters_sb->buf, eol);
                                progress->split = 1;
                        } else {
-                               fprintf(stderr, "%s: %s%*s", progress->title,
-                                       counters_sb->buf, (int) clear_len, eol);
+                               fprintf(stderr, "%s: %s%s", progress->title,
+                                       counters_sb->buf, eol);
                        }
                        fflush(stderr);
                }
index 32816763bd6dbe3d5e1784b3a39bb8c431ec82f9..93a897f240696c82d45e4189266fe187c58f0b1b 100644 (file)
@@ -549,7 +549,7 @@ static int index_name_stage_pos(const struct index_state *istate, const char *na
        first = 0;
        last = istate->cache_nr;
        while (last > first) {
-               int next = (last + first) >> 1;
+               int next = first + ((last - first) >> 1);
                struct cache_entry *ce = istate->cache[next];
                int cmp = cache_name_stage_compare(name, namelen, stage, ce->name, ce_namelen(ce), ce_stage(ce));
                if (!cmp)
@@ -2140,7 +2140,7 @@ int do_read_index(struct index_state *istate, const char *path, int must_exist)
        if (mmap_size < sizeof(struct cache_header) + the_hash_algo->rawsz)
                die(_("%s: index file smaller than expected"), path);
 
-       mmap = xmmap(NULL, mmap_size, PROT_READ, MAP_PRIVATE, fd, 0);
+       mmap = xmmap_gently(NULL, mmap_size, PROT_READ, MAP_PRIVATE, fd, 0);
        if (mmap == MAP_FAILED)
                die_errno(_("%s: unable to map index file"), path);
        close(fd);
index f88a97fb10a322c21062ec8c102482677cfd0feb..1d206fd22456cce12f7496321df8a47fcb94db9e 100644 (file)
@@ -279,7 +279,7 @@ static const char *gpg_sign_opt_quoted(struct replay_opts *opts)
 int sequencer_remove_state(struct replay_opts *opts)
 {
        struct strbuf buf = STRBUF_INIT;
-       int i;
+       int i, ret = 0;
 
        if (is_rebase_i(opts) &&
            strbuf_read_file(&buf, rebase_path_refs_to_delete(), 0) > 0) {
@@ -288,8 +288,10 @@ int sequencer_remove_state(struct replay_opts *opts)
                        char *eol = strchr(p, '\n');
                        if (eol)
                                *eol = '\0';
-                       if (delete_ref("(rebase -i) cleanup", p, NULL, 0) < 0)
+                       if (delete_ref("(rebase -i) cleanup", p, NULL, 0) < 0) {
                                warning(_("could not delete '%s'"), p);
+                               ret = -1;
+                       }
                        if (!eol)
                                break;
                        p = eol + 1;
@@ -305,10 +307,11 @@ int sequencer_remove_state(struct replay_opts *opts)
 
        strbuf_reset(&buf);
        strbuf_addstr(&buf, get_dir(opts));
-       remove_dir_recursively(&buf, 0);
+       if (remove_dir_recursively(&buf, 0))
+               ret = error(_("could not remove '%s'"), buf.buf);
        strbuf_release(&buf);
 
-       return 0;
+       return ret;
 }
 
 static const char *action_name(const struct replay_opts *opts)
@@ -3731,8 +3734,11 @@ static int pick_commits(struct repository *r,
                        unlink(git_path_merge_head(the_repository));
                        delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
 
-                       if (item->command == TODO_BREAK)
+                       if (item->command == TODO_BREAK) {
+                               if (!opts->verbose)
+                                       term_clear_line();
                                return stopped_at_head(r);
+                       }
                }
                if (item->command <= TODO_SQUASH) {
                        if (is_rebase_i(opts))
@@ -3754,11 +3760,14 @@ static int pick_commits(struct repository *r,
                        }
                        if (item->command == TODO_EDIT) {
                                struct commit *commit = item->commit;
-                               if (!res)
+                               if (!res) {
+                                       if (!opts->verbose)
+                                               term_clear_line();
                                        fprintf(stderr,
                                                _("Stopped at %s...  %.*s\n"),
                                                short_commit_name(commit),
                                                item->arg_len, arg);
+                               }
                                return error_with_patch(r, commit,
                                        arg, item->arg_len, opts, res, !res);
                        }
@@ -3796,6 +3805,8 @@ static int pick_commits(struct repository *r,
                        int saved = *end_of_arg;
                        struct stat st;
 
+                       if (!opts->verbose)
+                               term_clear_line();
                        *end_of_arg = '\0';
                        res = do_exec(r, arg);
                        *end_of_arg = saved;
@@ -3954,10 +3965,13 @@ static int pick_commits(struct repository *r,
                }
                apply_autostash(opts);
 
-               if (!opts->quiet)
+               if (!opts->quiet) {
+                       if (!opts->verbose)
+                               term_clear_line();
                        fprintf(stderr,
                                "Successfully rebased and updated %s.\n",
                                head_ref.buf);
+               }
 
                strbuf_release(&buf);
                strbuf_release(&head_ref);
index cecfdd36c7e696dce6188aea3398fbe8857d798b..e7430b9aa8ecf136a7d625ee059822e1e1a74b26 100644 (file)
@@ -249,7 +249,7 @@ sorted_string_list_member (const string_list_ty *slp, const char *s)
        {
          /* Here we know that if s is in the list, it is at an index j
             with j1 <= j < j2.  */
-         size_t j = (j1 + j2) >> 1;
+         size_t j = j1 + ((j2 - j1) >> 1);
          int result = strcmp (slp->item[j], s);
 
          if (result > 0)
index 0276d14a0ba66d4af6ad7af14c6b7e98305a1b84..77c5ed6a18e7849c4b823f9056dfb34cca951e15 100755 (executable)
@@ -311,8 +311,8 @@ test_expect_success 'init prefers command line to GIT_DIR' '
 test_expect_success 'init with separate gitdir' '
        rm -rf newdir &&
        git init --separate-git-dir realgitdir newdir &&
-       echo "gitdir: $(pwd)/realgitdir" >expected &&
-       test_cmp expected newdir/.git &&
+       newdir_git="$(cat newdir/.git)" &&
+       test_cmp_fspath "$(pwd)/realgitdir" "${newdir_git#gitdir: }" &&
        test_path_is_dir realgitdir/refs
 '
 
@@ -361,12 +361,9 @@ test_expect_success 're-init on .git file' '
 '
 
 test_expect_success 're-init to update git link' '
-       (
-       cd newdir &&
-       git init --separate-git-dir ../surrealgitdir
-       ) &&
-       echo "gitdir: $(pwd)/surrealgitdir" >expected &&
-       test_cmp expected newdir/.git &&
+       git -C newdir init --separate-git-dir ../surrealgitdir &&
+       newdir_git="$(cat newdir/.git)" &&
+       test_cmp_fspath "$(pwd)/surrealgitdir" "${newdir_git#gitdir: }" &&
        test_path_is_dir surrealgitdir/refs &&
        test_path_is_missing realgitdir/refs
 '
@@ -374,12 +371,9 @@ test_expect_success 're-init to update git link' '
 test_expect_success 're-init to move gitdir' '
        rm -rf newdir realgitdir surrealgitdir &&
        git init newdir &&
-       (
-       cd newdir &&
-       git init --separate-git-dir ../realgitdir
-       ) &&
-       echo "gitdir: $(pwd)/realgitdir" >expected &&
-       test_cmp expected newdir/.git &&
+       git -C newdir init --separate-git-dir ../realgitdir &&
+       newdir_git="$(cat newdir/.git)" &&
+       test_cmp_fspath "$(pwd)/realgitdir" "${newdir_git#gitdir: }" &&
        test_path_is_dir realgitdir/refs
 '
 
index ebc49561acc08e916b2ab78cd84e6729c65f5803..015fac8b5d077165c7f9ac8090fd4e2ac12a7cb3 100755 (executable)
@@ -210,4 +210,10 @@ test_expect_success MINGW 'verify curlies are quoted properly' '
        test_cmp expect actual
 '
 
+test_expect_success MINGW 'can spawn with argv[0] containing spaces' '
+       cp "$GIT_BUILD_DIR/t/helper/test-fake-ssh$X" ./ &&
+       test_must_fail "$PWD/test-fake-ssh$X" 2>err &&
+       grep TRASH_DIRECTORY err
+'
+
 test_done
index 1723e1a858585d9e83d7662326a6f337db7e1fa6..461dd539ffd4803c62d54e22e8921fbefa2c0786 100755 (executable)
@@ -75,11 +75,10 @@ test_expect_success 'rebase --keep-empty' '
        test_line_count = 6 actual
 '
 
-cat > expect <<EOF
-error: nothing to do
-EOF
-
 test_expect_success 'rebase -i with empty HEAD' '
+       cat >expect <<-\EOF &&
+       error: nothing to do
+       EOF
        set_fake_editor &&
        test_must_fail env FAKE_LINES="1 exec_true" git rebase -i HEAD^ >actual 2>&1 &&
        test_i18ncmp expect actual
@@ -237,25 +236,23 @@ test_expect_success 'exchange two commits' '
        test G = $(git cat-file commit HEAD | sed -ne \$p)
 '
 
-cat > expect << EOF
-diff --git a/file1 b/file1
-index f70f10e..fd79235 100644
---- a/file1
-+++ b/file1
-@@ -1 +1 @@
--A
-+G
-EOF
-
-cat > expect2 << EOF
-<<<<<<< HEAD
-D
-=======
-G
->>>>>>> 5d18e54... G
-EOF
-
 test_expect_success 'stop on conflicting pick' '
+       cat >expect <<-\EOF &&
+       diff --git a/file1 b/file1
+       index f70f10e..fd79235 100644
+       --- a/file1
+       +++ b/file1
+       @@ -1 +1 @@
+       -A
+       +G
+       EOF
+       cat >expect2 <<-\EOF &&
+       <<<<<<< HEAD
+       D
+       =======
+       G
+       >>>>>>> 5d18e54... G
+       EOF
        git tag new-branch1 &&
        set_fake_editor &&
        test_must_fail git rebase -i master &&
@@ -495,15 +492,14 @@ test_expect_success 'commit message retained after conflict' '
        git branch -D conflict-squash
 '
 
-cat > expect-squash-fixup << EOF
-B
-
-D
+test_expect_success C_LOCALE_OUTPUT 'squash and fixup generate correct log messages' '
+       cat >expect-squash-fixup <<-\EOF &&
+       B
 
-ONCE
-EOF
+       D
 
-test_expect_success C_LOCALE_OUTPUT 'squash and fixup generate correct log messages' '
+       ONCE
+       EOF
        git checkout -b squash-fixup E &&
        base=$(git rev-parse HEAD~4) &&
        set_fake_editor &&
@@ -799,13 +795,12 @@ test_expect_success 'rebase -i can copy notes' '
        test "a note" = "$(git notes show HEAD)"
 '
 
-cat >expect <<EOF
-an earlier note
-
-a note
-EOF
-
 test_expect_success 'rebase -i can copy notes over a fixup' '
+       cat >expect <<-\EOF &&
+       an earlier note
+
+       a note
+       EOF
        git reset --hard n3 &&
        git notes add -m"an earlier note" n2 &&
        set_fake_editor &&
@@ -1031,7 +1026,7 @@ test_expect_success 'rebase -i --root reword root commit' '
        test -z "$(git show -s --format=%p HEAD^)"
 '
 
-test_expect_success 'rebase -i --root when root has untracked file confilct' '
+test_expect_success 'rebase -i --root when root has untracked file conflict' '
        test_when_finished "reset_rebase" &&
        git checkout -b failing-root-pick A &&
        echo x >file2 &&
@@ -1304,52 +1299,37 @@ test_expect_success 'rebase -i respects rebase.missingCommitsCheck = ignore' '
                actual
 '
 
-cat >expect <<EOF
-Warning: some commits may have been dropped accidentally.
-Dropped commits (newer to older):
- - $(git rev-list --pretty=oneline --abbrev-commit -1 master)
-To avoid this message, use "drop" to explicitly remove a commit.
-
-Use 'git config rebase.missingCommitsCheck' to change the level of warnings.
-The possible behaviours are: ignore, warn, error.
-
-Rebasing (1/4)
-Rebasing (2/4)
-Rebasing (3/4)
-Rebasing (4/4)
-Successfully rebased and updated refs/heads/missing-commit.
-EOF
-
-cr_to_nl () {
-       tr '\015' '\012'
-}
-
 test_expect_success 'rebase -i respects rebase.missingCommitsCheck = warn' '
+       cat >expect <<-EOF &&
+       Warning: some commits may have been dropped accidentally.
+       Dropped commits (newer to older):
+        - $(git rev-list --pretty=oneline --abbrev-commit -1 master)
+       To avoid this message, use "drop" to explicitly remove a commit.
+       EOF
        test_config rebase.missingCommitsCheck warn &&
        rebase_setup_and_clean missing-commit &&
        set_fake_editor &&
        FAKE_LINES="1 2 3 4" \
                git rebase -i --root 2>actual.2 &&
-       cr_to_nl <actual.2 >actual &&
+       head -n4 actual.2 >actual &&
        test_i18ncmp expect actual &&
        test D = $(git cat-file commit HEAD | sed -ne \$p)
 '
 
-cat >expect <<EOF
-Warning: some commits may have been dropped accidentally.
-Dropped commits (newer to older):
- - $(git rev-list --pretty=oneline --abbrev-commit -1 master)
- - $(git rev-list --pretty=oneline --abbrev-commit -1 master~2)
-To avoid this message, use "drop" to explicitly remove a commit.
-
-Use 'git config rebase.missingCommitsCheck' to change the level of warnings.
-The possible behaviours are: ignore, warn, error.
-
-You can fix this with 'git rebase --edit-todo' and then run 'git rebase --continue'.
-Or you can abort the rebase with 'git rebase --abort'.
-EOF
-
 test_expect_success 'rebase -i respects rebase.missingCommitsCheck = error' '
+       cat >expect <<-EOF &&
+       Warning: some commits may have been dropped accidentally.
+       Dropped commits (newer to older):
+        - $(git rev-list --pretty=oneline --abbrev-commit -1 master)
+        - $(git rev-list --pretty=oneline --abbrev-commit -1 master~2)
+       To avoid this message, use "drop" to explicitly remove a commit.
+
+       Use '\''git config rebase.missingCommitsCheck'\'' to change the level of warnings.
+       The possible behaviours are: ignore, warn, error.
+
+       You can fix this with '\''git rebase --edit-todo'\'' and then run '\''git rebase --continue'\''.
+       Or you can abort the rebase with '\''git rebase --abort'\''.
+       EOF
        test_config rebase.missingCommitsCheck error &&
        rebase_setup_and_clean missing-commit &&
        set_fake_editor &&
index bdaa511bb0ae1082568e83781252f667b39cda8a..4eff14dae53223fb432ff5d9147543850a2c9ad5 100755 (executable)
@@ -265,4 +265,12 @@ test_expect_success '--reschedule-failed-exec' '
        test_i18ngrep "has been rescheduled" err
 '
 
+test_expect_success 'rebase.reschedulefailedexec only affects `rebase -i`' '
+       test_config rebase.reschedulefailedexec true &&
+       test_must_fail git rebase -x false HEAD^ &&
+       grep "^exec false" .git/rebase-merge/git-rebase-todo &&
+       git rebase --abort &&
+       git rebase HEAD^
+'
+
 test_done
index 2d1094e4831a88205133558d2ca6ac5ac07209d1..9186e90127712feaf92fe94e5bdfce02528fcf5f 100755 (executable)
@@ -49,7 +49,7 @@ create_expected_success_interactive () {
        $(grep "^Created autostash: [0-9a-f][0-9a-f]*\$" actual)
        HEAD is now at $(git rev-parse --short feature-branch) third commit
        Rebasing (1/2)QRebasing (2/2)QApplied autostash.
-       Successfully rebased and updated refs/heads/rebased-feature-branch.
+       Q                                                                                QSuccessfully rebased and updated refs/heads/rebased-feature-branch.
        EOF
 }
 
@@ -73,7 +73,7 @@ create_expected_failure_interactive () {
        Rebasing (1/2)QRebasing (2/2)QApplying autostash resulted in conflicts.
        Your changes are safe in the stash.
        You can run "git stash pop" or "git stash drop" at any time.
-       Successfully rebased and updated refs/heads/rebased-feature-branch.
+       Q                                                                                QSuccessfully rebased and updated refs/heads/rebased-feature-branch.
        EOF
 }
 
index 42ba5b9f0981b2b7ce98e1c5c55012ef61757bab..f0814d5280c6755ad1f9f8378a6a32d32ffaee1d 100755 (executable)
@@ -224,8 +224,24 @@ test_expect_success 'refs/rewritten/* is worktree-local' '
        test_cmp_rev HEAD "$(cat wt/b)"
 '
 
+test_expect_success '--abort cleans up refs/rewritten' '
+       git checkout -b abort-cleans-refs-rewritten H &&
+       GIT_SEQUENCE_EDITOR="echo break >>" git rebase -ir @^ &&
+       git rev-parse --verify refs/rewritten/onto &&
+       git rebase --abort &&
+       test_must_fail git rev-parse --verify refs/rewritten/onto
+'
+
+test_expect_success '--quit cleans up refs/rewritten' '
+       git checkout -b quit-cleans-refs-rewritten H &&
+       GIT_SEQUENCE_EDITOR="echo break >>" git rebase -ir @^ &&
+       git rev-parse --verify refs/rewritten/onto &&
+       git rebase --quit &&
+       test_must_fail git rev-parse --verify refs/rewritten/onto
+'
+
 test_expect_success 'post-rewrite hook and fixups work for merges' '
-       git checkout -b post-rewrite &&
+       git checkout -b post-rewrite &&
        test_commit same1 &&
        git reset --hard HEAD^ &&
        test_commit same2 &&
index 85ae7dc1e4618275413a8b498f43d43f3e0ead92..66282a720ee853e7442334c889e199978aac9260 100755 (executable)
@@ -252,6 +252,19 @@ test_expect_success 'choking "git rm" should not let it die with cruft' '
        test_path_is_missing .git/index.lock
 '
 
+test_expect_success 'Resolving by removal is not a warning-worthy event' '
+       git reset -q --hard &&
+       test_when_finished "rm -f .git/index.lock msg && git reset -q --hard" &&
+       blob=$(echo blob | git hash-object -w --stdin) &&
+       for stage in 1 2 3
+       do
+               echo "100644 $blob $stage       blob"
+       done | git update-index --index-info &&
+       git rm blob >msg 2>&1 &&
+       test_i18ngrep ! "needs merge" msg &&
+       test_must_fail git ls-files -s --error-unmatch blob
+'
+
 test_expect_success 'rm removes subdirectories recursively' '
        mkdir -p dir/subdir/subsubdir &&
        echo content >dir/subdir/subsubdir/file &&
index 65dfbc033a027df1a590cbfaf75ae47f4ed9c547..69991a3168f354b8bfefcd396a968e653bb352bd 100755 (executable)
@@ -639,4 +639,12 @@ test_expect_success 'add -p patch editing works with pathological context lines'
        test_cmp expected-2 actual
 '
 
+test_expect_success 'checkout -p works with pathological context lines' '
+       test_write_lines a a a a a a >a &&
+       git add a &&
+       test_write_lines a b a b a b a b a b a > a&&
+       test_write_lines s n n y q | git checkout -p &&
+       test_write_lines a b a b a a b a b a >expect &&
+       test_cmp expect a
+'
 test_done
index ea30d5f6a0f228971d29e257a89fca6ea594cecb..b22e671608119d37dd73bf6b0df41ce62e1a26f9 100755 (executable)
@@ -708,6 +708,24 @@ test_expect_success 'invalid ref of the form "n", n >= N' '
        git stash drop
 '
 
+test_expect_success 'valid ref of the form "n", n < N' '
+       git stash clear &&
+       echo bar5 >file &&
+       echo bar6 >file2 &&
+       git add file2 &&
+       git stash &&
+       git stash show 0 &&
+       git stash branch tmp 0 &&
+       git checkout master &&
+       git stash &&
+       git stash apply 0 &&
+       git reset --hard &&
+       git stash pop 0 &&
+       git stash &&
+       git stash drop 0 &&
+       test_must_fail git stash drop
+'
+
 test_expect_success 'branch: do not drop the stash if the branch exists' '
        git stash clear &&
        echo foo >file &&
index ab4670d23653eca46292b13ef1559584aae5c3a6..6b087df3dcbd01df9c7e533f9e039518beaa773a 100755 (executable)
@@ -2008,4 +2008,26 @@ test_expect_success 'compare mixed whitespace delta across moved blocks' '
        test_cmp expected actual
 '
 
+# Note that the "6" in the expected hunk header below is funny, since we only
+# show 5 lines (the missing one was blank and thus ignored). This is how
+# --ignore-blank-lines behaves even without --function-context, and this test
+# is just checking the interaction of the two features. Don't take it as an
+# endorsement of that output.
+test_expect_success 'combine --ignore-blank-lines with --function-context' '
+       test_write_lines 1 "" 2 3 4 5 >a &&
+       test_write_lines 1    2 3 4   >b &&
+       test_must_fail git diff --no-index \
+               --ignore-blank-lines --function-context a b >actual.raw &&
+       sed -n "/@@/,\$p" <actual.raw >actual &&
+       cat <<-\EOF >expect &&
+       @@ -1,6 +1,4 @@
+        1
+        2
+        3
+        4
+       -5
+       EOF
+       test_cmp expect actual
+'
+
 test_done
index 840ad4d8accbfef59e2c9ade82105cc717f6866c..5267c4be20e709bb1632c5b9786ffdb418bcb5c9 100755 (executable)
@@ -23,6 +23,14 @@ test_expect_success 'write graph with no packs' '
        test_path_is_file info/commit-graph
 '
 
+test_expect_success 'close with correct error on bad input' '
+       cd "$TRASH_DIRECTORY/full" &&
+       echo doesnotexist >in &&
+       { git commit-graph write --stdin-packs <in 2>stderr; ret=$?; } &&
+       test "$ret" = 1 &&
+       test_i18ngrep "error adding pack" stderr
+'
+
 test_expect_success 'create commits and repack' '
        cd "$TRASH_DIRECTORY/full" &&
        for i in $(test_seq 3)
index 8ef8763e063cf30da8af4a80079458cf8e18c56d..b86ddb60f2ea6ec1c177b8be5d056d064a1d7697 100755 (executable)
@@ -177,6 +177,55 @@ test_expect_success 'push (chunked)' '
         test $HEAD = $(git rev-parse --verify HEAD))
 '
 
+test_expect_success 'push --atomic also prevents branch creation, reports collateral' '
+       # Setup upstream repo - empty for now
+       d=$HTTPD_DOCUMENT_ROOT_PATH/atomic-branches.git &&
+       git init --bare "$d" &&
+       test_config -C "$d" http.receivepack true &&
+       up="$HTTPD_URL"/smart/atomic-branches.git &&
+
+       # Tell "$up" about two branches for now
+       test_commit atomic1 &&
+       test_commit atomic2 &&
+       git branch collateral &&
+       git push "$up" master collateral &&
+
+       # collateral is a valid push, but should be failed by atomic push
+       git checkout collateral &&
+       test_commit collateral1 &&
+
+       # Make master incompatible with upstream to provoke atomic
+       git checkout master &&
+       git reset --hard HEAD^ &&
+
+       # Add a new branch which should be failed by atomic push. This is a
+       # regression case.
+       git branch atomic &&
+
+       # --atomic should cause entire push to be rejected
+       test_must_fail git push --atomic "$up" master atomic collateral 2>output &&
+
+       # the new branch should not have been created upstream
+       test_must_fail git -C "$d" show-ref --verify refs/heads/atomic &&
+
+       # upstream should still reflect atomic2, the last thing we pushed
+       # successfully
+       git rev-parse atomic2 >expected &&
+       # on master...
+       git -C "$d" rev-parse refs/heads/master >actual &&
+       test_cmp expected actual &&
+       # ...and collateral.
+       git -C "$d" rev-parse refs/heads/collateral >actual &&
+       test_cmp expected actual &&
+
+       # the failed refs should be indicated to the user
+       grep "^ ! .*rejected.* master -> master" output &&
+
+       # the collateral failure refs should be indicated to the user
+       grep "^ ! .*rejected.* atomic -> atomic .*atomic push failed" output &&
+       grep "^ ! .*rejected.* collateral -> collateral .*atomic push failed" output
+'
+
 test_expect_success 'push --all can push to empty repo' '
        d=$HTTPD_DOCUMENT_ROOT_PATH/empty-all.git &&
        git init --bare "$d" &&
@@ -213,7 +262,7 @@ test_expect_success TTY 'push shows progress when stderr is a tty' '
        cd "$ROOT_PATH"/test_repo_clone &&
        test_commit noisy &&
        test_terminal git push >output 2>&1 &&
-       test_i18ngrep "^Writing objects" output
+       test_i18ngrep "Writing objects" output
 '
 
 test_expect_success TTY 'push --quiet silences status and progress' '
@@ -228,7 +277,7 @@ test_expect_success TTY 'push --no-progress silences progress but not status' '
        test_commit no-progress &&
        test_terminal git push --no-progress >output 2>&1 &&
        test_i18ngrep "^To http" output &&
-       test_i18ngrep ! "^Writing objects" output
+       test_i18ngrep ! "Writing objects" output
 '
 
 test_expect_success 'push --progress shows progress to non-tty' '
@@ -236,7 +285,7 @@ test_expect_success 'push --progress shows progress to non-tty' '
        test_commit progress &&
        git push --progress >output 2>&1 &&
        test_i18ngrep "^To http" output &&
-       test_i18ngrep "^Writing objects" output
+       test_i18ngrep "Writing objects" output
 '
 
 test_expect_success 'http push gives sane defaults to reflog' '
index ac74626a7b667c3723569cfbca9c77bc264c6728..e38e54386795a0e05003e803dc79301ed1cd52d9 100755 (executable)
@@ -199,7 +199,7 @@ test_expect_success 'GIT_SMART_HTTP can disable smart http' '
 
 test_expect_success 'invalid Content-Type rejected' '
        test_must_fail git clone $HTTPD_URL/broken_smart/repo.git 2>actual &&
-       grep "not valid:" actual
+       test_i18ngrep "not valid:" actual
 '
 
 test_expect_success 'create namespaced refs' '
@@ -301,11 +301,10 @@ test_expect_success CMDLINE_LIMIT \
        )
 '
 
-test_expect_success 'large fetch-pack requests can be split across POSTs' '
+test_expect_success 'large fetch-pack requests can be sent using chunked encoding' '
        GIT_TRACE_CURL=true git -c http.postbuffer=65536 \
                clone --bare "$HTTPD_URL/smart/repo.git" split.git 2>err &&
-       grep "^=> Send header: POST" err >posts &&
-       test_line_count = 2 posts
+       grep "^=> Send header: Transfer-Encoding: chunked" err
 '
 
 test_expect_success 'test allowreachablesha1inwant' '
@@ -466,7 +465,7 @@ test_expect_success 'GIT_TRACE_CURL_NO_DATA prevents data from being traced' '
 
 test_expect_success 'server-side error detected' '
        test_must_fail git clone $HTTPD_URL/error_smart/repo.git 2>actual &&
-       grep "server-side error" actual
+       test_i18ngrep "server-side error" actual
 '
 
 test_done
index d04f8007e0e34fa938bf4fccb91eff384743e616..2d6c4a281edb3f0678a600fd3e780825536da437 100755 (executable)
@@ -126,7 +126,7 @@ test_expect_success 'forced push' '
 '
 
 test_expect_success 'cloning without refspec' '
-       GIT_REMOTE_TESTGIT_REFSPEC="" \
+       GIT_REMOTE_TESTGIT_NOREFSPEC=1 \
        git clone "testgit::${PWD}/server" local2 2>error &&
        test_i18ngrep "this remote helper should implement refspec capability" error &&
        compare_refs local2 HEAD server HEAD
@@ -135,7 +135,7 @@ test_expect_success 'cloning without refspec' '
 test_expect_success 'pulling without refspecs' '
        (cd local2 &&
        git reset --hard &&
-       GIT_REMOTE_TESTGIT_REFSPEC="" git pull 2>../error) &&
+       GIT_REMOTE_TESTGIT_NOREFSPEC=1 git pull 2>../error) &&
        test_i18ngrep "this remote helper should implement refspec capability" error &&
        compare_refs local2 HEAD server HEAD
 '
@@ -145,8 +145,8 @@ test_expect_success 'pushing without refspecs' '
        (cd local2 &&
        echo content >>file &&
        git commit -a -m ten &&
-       GIT_REMOTE_TESTGIT_REFSPEC="" &&
-       export GIT_REMOTE_TESTGIT_REFSPEC &&
+       GIT_REMOTE_TESTGIT_NOREFSPEC=1 &&
+       export GIT_REMOTE_TESTGIT_NOREFSPEC &&
        test_must_fail git push 2>../error) &&
        test_i18ngrep "remote-helper doesn.t support push; refspec needed" error
 '
@@ -303,4 +303,14 @@ test_expect_success 'fetch url' '
        compare_refs server HEAD local FETCH_HEAD
 '
 
+test_expect_success 'fetch tag' '
+       (cd server &&
+        git tag v1.0
+       ) &&
+       (cd local &&
+        git fetch
+       ) &&
+       compare_refs local v1.0 server v1.0
+'
+
 test_done
index 752c763eb666e197304efbc7ea006325a36ff870..6b9f0b5dc79cf0239daf4f6a210baaccf8612d74 100755 (executable)
@@ -11,13 +11,15 @@ fi
 url=$2
 
 dir="$GIT_DIR/testgit/$alias"
-prefix="refs/testgit/$alias"
 
-default_refspec="refs/heads/*:${prefix}/heads/*"
+h_refspec="refs/heads/*:refs/testgit/$alias/heads/*"
+t_refspec="refs/tags/*:refs/testgit/$alias/tags/*"
 
-refspec="${GIT_REMOTE_TESTGIT_REFSPEC-$default_refspec}"
-
-test -z "$refspec" && prefix="refs"
+if test -n "$GIT_REMOTE_TESTGIT_NOREFSPEC"
+then
+       h_refspec=""
+       t_refspec=""
+fi
 
 GIT_DIR="$url/.git"
 export GIT_DIR
@@ -40,7 +42,8 @@ do
        capabilities)
                echo 'import'
                echo 'export'
-               test -n "$refspec" && echo "refspec $refspec"
+               test -n "$h_refspec" && echo "refspec $h_refspec"
+               test -n "$t_refspec" && echo "refspec $t_refspec"
                if test -n "$gitmarks"
                then
                        echo "*import-marks $gitmarks"
@@ -52,7 +55,7 @@ do
                echo
                ;;
        list)
-               git for-each-ref --format='? %(refname)' 'refs/heads/'
+               git for-each-ref --format='? %(refname)' 'refs/heads/' 'refs/tags/'
                head=$(git symbolic-ref HEAD)
                echo "@$head HEAD"
                echo
@@ -81,10 +84,11 @@ do
 
                echo "feature done"
                git fast-export \
+                       ${h_refspec:+"--refspec=$h_refspec"} \
+                       ${t_refspec:+"--refspec=$t_refspec"} \
                        ${testgitmarks:+"--import-marks=$testgitmarks"} \
                        ${testgitmarks:+"--export-marks=$testgitmarks"} \
-                       $refs |
-               sed -e "s#refs/heads/#${prefix}/heads/#g"
+                       $refs
                echo "done"
                ;;
        export)
index 7b36954d63d70e79149cf1558a93586e1b39c867..a2c45d1902ac5df8c67b16eb8a9a8dd99839eac1 100755 (executable)
@@ -669,4 +669,16 @@ test_expect_success 'git clean -d skips untracked dirs containing ignored files'
        test_path_is_missing foo/b/bb
 '
 
+test_expect_success MINGW 'handle clean & core.longpaths = false nicely' '
+       test_config core.longpaths false &&
+       a50=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa &&
+       mkdir -p $a50$a50/$a50$a50/$a50$a50 &&
+       : >"$a50$a50/test.txt" 2>"$a50$a50/$a50$a50/$a50$a50/test.txt" &&
+       # create a temporary outside the working tree to hide from "git clean"
+       test_must_fail git clean -xdf 2>.git/err &&
+       # grepping for a strerror string is unportable but it is OK here with
+       # MINGW prereq
+       test_i18ngrep "too long" .git/err
+'
+
 test_done
index 706ae762e0ec2fd0103f7f6788eff13ec86ebd67..6b2aa917e11871eb403c8c79725b1e1944f8d29f 100755 (executable)
@@ -421,4 +421,11 @@ test_expect_success 'option-like arguments passed to foreach commands are not lo
        test_cmp expected actual
 '
 
+test_expect_success 'option-like arguments passed to foreach recurse correctly' '
+       git -C clone2 submodule foreach --recursive "echo be --an-option" >expect &&
+       git -C clone2 submodule foreach --recursive echo be --an-option >actual &&
+       grep -e "--an-option" expect &&
+       test_cmp expect actual
+'
+
 test_done
index c44186133147838d7f17c4d42f8cb96a5df73b28..f19202b509899481d4a098de049f1e2c9cae9915 100755 (executable)
@@ -538,33 +538,50 @@ test_expect_success 'with 2 files arguments' '
        test_cmp expected actual
 '
 
-test_expect_success 'with message that has comments' '
-       cat basic_message >message_with_comments &&
-       sed -e "s/ Z\$/ /" >>message_with_comments <<-\EOF &&
-               # comment
-
-               # other comment
-               Cc: Z
-               # yet another comment
-               Reviewed-by: Johan
-               Reviewed-by: Z
-               # last comment
-
-       EOF
-       cat basic_patch >>message_with_comments &&
-       cat basic_message >expected &&
-       cat >>expected <<-\EOF &&
-               # comment
-
-               Reviewed-by: Johan
-               Cc: Peff
-               # last comment
-
-       EOF
-       cat basic_patch >>expected &&
-       git interpret-trailers --trim-empty --trailer "Cc: Peff" message_with_comments >actual &&
-       test_cmp expected actual
-'
+# Cover multiple comment characters with the same test input.
+for char in "#" ";"
+do
+       case "$char" in
+       "#")
+               # This is the default, so let's explicitly _not_
+               # set any config to make sure it behaves as we expect.
+               ;;
+       *)
+               config="-c core.commentChar=$char"
+               ;;
+       esac
+
+       test_expect_success "with message that has comments ($char)" '
+               cat basic_message >message_with_comments &&
+               sed -e "s/ Z\$/ /" \
+                   -e "s/#/$char/g" >>message_with_comments <<-EOF &&
+                       # comment
+
+                       # other comment
+                       Cc: Z
+                       # yet another comment
+                       Reviewed-by: Johan
+                       Reviewed-by: Z
+                       # last comment
+
+               EOF
+               cat basic_patch >>message_with_comments &&
+               cat basic_message >expected &&
+               sed -e "s/#/$char/g" >>expected <<-\EOF &&
+                       # comment
+
+                       Reviewed-by: Johan
+                       Cc: Peff
+                       # last comment
+
+               EOF
+               cat basic_patch >>expected &&
+               git $config interpret-trailers \
+                       --trim-empty --trailer "Cc: Peff" \
+                       message_with_comments >actual &&
+               test_cmp expected actual
+       '
+done
 
 test_expect_success 'with message that has an old style conflict block' '
        cat basic_message >message_with_comments &&
index 8270de74beafb931f09f296557406c0d158d48de..f233522f43ab39728e3c454258920283f5d9cab3 100644 (file)
@@ -888,6 +888,21 @@ test_cmp_rev () {
        fi
 }
 
+# Compare paths respecting core.ignoreCase
+test_cmp_fspath () {
+       if test "x$1" = "x$2"
+       then
+               return 0
+       fi
+
+       if test true != "$(git config --get --type=bool core.ignorecase)"
+       then
+               return 1
+       fi
+
+       test "x$(echo "$1" | tr A-Z a-z)" =  "x$(echo "$2" | tr A-Z a-z)"
+}
+
 # Print a sequence of integers in increasing order, either with
 # two arguments (start and end):
 #
index c7e17ec9cb61e6782bc255e6b90381054255c269..6b05a88faf59ee74840b0f9fa340a5d9397a0f5d 100644 (file)
@@ -853,6 +853,7 @@ static int push_refs_with_push(struct transport *transport,
 {
        int force_all = flags & TRANSPORT_PUSH_FORCE;
        int mirror = flags & TRANSPORT_PUSH_MIRROR;
+       int atomic = flags & TRANSPORT_PUSH_ATOMIC;
        struct helper_data *data = transport->data;
        struct strbuf buf = STRBUF_INIT;
        struct ref *ref;
@@ -872,6 +873,11 @@ static int push_refs_with_push(struct transport *transport,
                case REF_STATUS_REJECT_NONFASTFORWARD:
                case REF_STATUS_REJECT_STALE:
                case REF_STATUS_REJECT_ALREADY_EXISTS:
+                       if (atomic) {
+                               string_list_clear(&cas_options, 0);
+                               return 0;
+                       } else
+                               continue;
                case REF_STATUS_UPTODATE:
                        continue;
                default:
index f1fcd2c4b006dc2ece2019ac91f73a2f42bbf6bd..453de8f7041b966a9bd0eaee20ef8f5758cec2f1 100644 (file)
@@ -1226,6 +1226,20 @@ int transport_push(struct repository *r,
                err = push_had_errors(remote_refs);
                ret = push_ret | err;
 
+               if ((flags & TRANSPORT_PUSH_ATOMIC) && err) {
+                       struct ref *it;
+                       for (it = remote_refs; it; it = it->next)
+                               switch (it->status) {
+                               case REF_STATUS_NONE:
+                               case REF_STATUS_UPTODATE:
+                               case REF_STATUS_OK:
+                                       it->status = REF_STATUS_ATOMIC_PUSH_FAILED;
+                                       break;
+                               default:
+                                       break;
+                               }
+               }
+
                if (!quiet || err)
                        transport_print_push_status(transport->url, remote_refs,
                                        verbose | porcelain, porcelain,
index 4d2129e7fc134cdbc67e08ce9fb4f805023059b5..b2a9f368ecd6ab159cae99deac0b9a9b5ee08f6a 100644 (file)
@@ -722,7 +722,7 @@ static void deepen_by_rev_list(struct packet_writer *writer, int ac,
 {
        struct commit_list *result;
 
-       close_commit_graph(the_repository);
+       close_commit_graph(the_repository->objects);
        result = get_shallow_commits_by_rev_list(ac, av, SHALLOW, NOT_SHALLOW);
        send_shallow(writer, result);
        free_commit_list(result);
index ea3cf64d4c399ae84c156b850002459a8ffde72c..1e45ab7b92749b653484f522701f5d86521b03c7 100644 (file)
--- a/wrapper.c
+++ b/wrapper.c
@@ -502,7 +502,7 @@ int git_mkstemps_mode(char *pattern, int suffix_len, int mode)
         * Try TMP_MAX different filenames.
         */
        gettimeofday(&tv, NULL);
-       value = ((size_t)(tv.tv_usec << 16)) ^ tv.tv_sec ^ getpid();
+       value = ((uint64_t)tv.tv_usec << 16) ^ tv.tv_sec ^ getpid();
        filename_template = &pattern[len - 6 - suffix_len];
        for (count = 0; count < TMP_MAX; ++count) {
                uint64_t v = value;
index 7778dc2b190f84fe2ab1e1539756a7e1f4254c3b..30713ae9a98ecfc9f764120d3a1d18b5a8ee11bf 100644 (file)
@@ -210,7 +210,7 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
                        if (fs1 < 0)
                                fs1 = 0;
                        if (fs1 < s1) {
-                               s2 -= s1 - fs1;
+                               s2 = XDL_MAX(s2 - (s1 - fs1), 0);
                                s1 = fs1;
                        }
                }
@@ -232,7 +232,7 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
                        if (fe1 < 0)
                                fe1 = xe->xdf1.nrec;
                        if (fe1 > e1) {
-                               e2 += fe1 - e1;
+                               e2 = XDL_MIN(e2 + (fe1 - e1), xe->xdf2.nrec);
                                e1 = fe1;
                        }