Merge branch 'jk/ident-loosen-getpwuid' into maint
authorJunio C Hamano <gitster@pobox.com>
Mon, 4 Jan 2016 22:02:57 +0000 (14:02 -0800)
committerJunio C Hamano <gitster@pobox.com>
Mon, 4 Jan 2016 22:02:57 +0000 (14:02 -0800)
When getpwuid() on the system returned NULL (e.g. the user is not
in the /etc/passwd file or other uid-to-name mappings), the
codepath to find who the user is to record it in the reflog barfed
and died. Loosen the check in this codepath, which already accepts
questionable ident string (e.g. host part of the e-mail address is
obviously bogus), and in general when we operate fmt_ident() function
in non-strict mode.

* jk/ident-loosen-getpwuid:
ident: loosen getpwuid error in non-strict mode
ident: keep a flag for bogus default_email
ident: make xgetpwuid_self() a static local helper

33 files changed:
Documentation/RelNotes/2.6.5.txt [new file with mode: 0644]
Documentation/git-check-ref-format.txt
Documentation/git-p4.txt
Documentation/git-submodule.txt
RelNotes
builtin/fmt-merge-msg.c
builtin/merge.c
builtin/reflog.c
builtin/rev-parse.c
builtin/show-branch.c
commit.c
config.c
contrib/completion/git-completion.bash
contrib/subtree/git-subtree.sh
contrib/subtree/t/t7900-subtree.sh
credential-store.c
fast-import.c
git-p4.py
git-send-email.perl
ident.c
pack-check.c
refs.c
remote.c
revision.c
sha1_file.c
shallow.c
t/t3404-rebase-interactive.sh
t/t5801-remote-helpers.sh
t/t7610-mergetool.sh
t/t7800-difftool.sh
t/t9826-git-p4-keep-empty-commits.sh [new file with mode: 0755]
t/test-lib-functions.sh
upload-pack.c
diff --git a/Documentation/RelNotes/2.6.5.txt b/Documentation/RelNotes/2.6.5.txt
new file mode 100644 (file)
index 0000000..3e6331d
--- /dev/null
@@ -0,0 +1,39 @@
+Git v2.6.5 Release Notes
+========================
+
+Fixes since v2.6.4
+------------------
+
+ * Because "test_when_finished" in our test framework queues the
+   clean-up tasks to be done in a shell variable, it should not be
+   used inside a subshell.  Add a mechanism to allow 'bash' to catch
+   such uses, and fix the ones that were found.
+
+ * Update "git subtree" (in contrib/) so that it can take whitespaces
+   in the pathnames, not only in the in-tree pathname but the name of
+   the directory that the repository is in.
+
+ * Cosmetic improvement to lock-file error messages.
+
+ * mark_tree_uninteresting() has code to handle the case where it gets
+   passed a NULL pointer in its 'tree' parameter, but the function had
+   'object = &tree->object' assignment before checking if tree is
+   NULL.  This gives a compiler an excuse to declare that tree will
+   never be NULL and apply a wrong optimization.  Avoid it.
+
+ * The helper used to iterate over loose object directories to prune
+   stale objects did not closedir() immediately when it is done with a
+   directory--a callback such as the one used for "git prune" may want
+   to do rmdir(), but it would fail on open directory on platforms
+   such as WinXP.
+
+ * "git p4" used to import Perforce CLs that touch only paths outside
+   the client spec as empty commits.  It has been corrected to ignore
+   them instead, with a new configuration git-p4.keepEmptyCommits as a
+   backward compatibility knob.
+
+ * The exit code of git-fsck did not reflect some types of errors
+   found in packed objects, which has been corrected.
+
+Also contains typofixes, documentation updates and trivial code
+clean-ups.
index 9044dfaadae10047281dfdfd06a75e634e1309fa..91a3622ee4e270a11c699e310fb4d544e37845c9 100644 (file)
@@ -60,7 +60,7 @@ Git imposes the following rules on how references are named:
 
 These rules make it easy for shell script based tools to parse
 reference names, pathname expansion by the shell when a reference name is used
-unquoted (by mistake), and also avoids ambiguities in certain
+unquoted (by mistake), and also avoid ambiguities in certain
 reference name expressions (see linkgit:gitrevisions[7]):
 
 . A double-dot `..` is often used as in `ref1..ref2`, and in some
index 82aa5d60736ccc75510c19ddec2da9eb4c5611a8..b3e768ee81084acf447072b448e738671b6401f5 100644 (file)
@@ -510,6 +510,10 @@ git-p4.useClientSpec::
        option '--use-client-spec'.  See the "CLIENT SPEC" section above.
        This variable is a boolean, not the name of a p4 client.
 
+git-p4.keepEmptyCommits::
+       A changelist that contains only excluded files will be imported
+       as an empty commit if this boolean option is set to true.
+
 Submit variables
 ~~~~~~~~~~~~~~~~
 git-p4.detectRenames::
index f17687e09d322f909ae4236c297e769d2e949b90..1572f058f59ad1f75c4b5ed6e34fae9110414bad 100644 (file)
@@ -237,6 +237,9 @@ sync::
 +
 "git submodule sync" synchronizes all submodules while
 "git submodule sync \-- A" synchronizes submodule "A" only.
++
+If `--recursive` is specified, this command will recurse into the
+registered submodules, and sync any nested submodules within.
 
 OPTIONS
 -------
@@ -364,7 +367,7 @@ the submodule itself.
 for linkgit:git-clone[1]'s `--reference` and `--shared` options carefully.
 
 --recursive::
-       This option is only valid for foreach, update and status commands.
+       This option is only valid for foreach, update, status and sync commands.
        Traverse submodules recursively. The operation is performed not
        only in the submodules of the current repo, but also
        in any nested submodules inside those submodules (and so on).
index 1a01ab88d226976d33a2b8e361958b0fc040a8b3..4570bdf87638d1a26ed9c1c9fe39b5013456bd8f 120000 (symlink)
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/2.6.4.txt
\ No newline at end of file
+Documentation/RelNotes/2.6.5.txt
\ No newline at end of file
index 4ba7f282a5f6cd2e0af643f865a34c3048f2f591..846004b83358cd8adfd5f7de96b2d551700037a1 100644 (file)
@@ -537,7 +537,7 @@ static void fmt_merge_msg_sigs(struct strbuf *out)
 static void find_merge_parents(struct merge_parents *result,
                               struct strbuf *in, unsigned char *head)
 {
-       struct commit_list *parents, *next;
+       struct commit_list *parents;
        struct commit *head_commit;
        int pos = 0, i, j;
 
@@ -576,13 +576,10 @@ static void find_merge_parents(struct merge_parents *result,
        parents = reduce_heads(parents);
 
        while (parents) {
+               struct commit *cmit = pop_commit(&parents);
                for (i = 0; i < result->nr; i++)
-                       if (!hashcmp(result->item[i].commit,
-                                    parents->item->object.sha1))
+                       if (!hashcmp(result->item[i].commit, cmit->object.sha1))
                                result->item[i].used = 1;
-               next = parents->next;
-               free(parents);
-               parents = next;
        }
 
        for (i = j = 0; i < result->nr; i++) {
index e6741f3380d30adaf7b5a16688a4ccb3e6d6dd99..3ec97a83455ea2f14fba0f7530e8bf476692c30c 100644 (file)
@@ -1019,7 +1019,7 @@ static struct commit_list *reduce_parents(struct commit *head_commit,
                                          int *head_subsumed,
                                          struct commit_list *remoteheads)
 {
-       struct commit_list *parents, *next, **remotes = &remoteheads;
+       struct commit_list *parents, **remotes;
 
        /*
         * Is the current HEAD reachable from another commit being
@@ -1033,16 +1033,14 @@ static struct commit_list *reduce_parents(struct commit *head_commit,
        /* Find what parents to record by checking independent ones. */
        parents = reduce_heads(remoteheads);
 
-       for (remoteheads = NULL, remotes = &remoteheads;
-            parents;
-            parents = next) {
-               struct commit *commit = parents->item;
-               next = parents->next;
+       remoteheads = NULL;
+       remotes = &remoteheads;
+       while (parents) {
+               struct commit *commit = pop_commit(&parents);
                if (commit == head_commit)
                        *head_subsumed = 0;
                else
                        remotes = &commit_list_insert(commit, remotes)->next;
-               free(parents);
        }
        return remoteheads;
 }
index f96ca2a27dcb9ac6daa3aa110ff657981296502a..cf1145e635c559206a6b0d61e41321fa914e5e42 100644 (file)
@@ -218,7 +218,6 @@ static int keep_entry(struct commit **it, unsigned char *sha1)
  */
 static void mark_reachable(struct expire_reflog_policy_cb *cb)
 {
-       struct commit *commit;
        struct commit_list *pending;
        unsigned long expire_limit = cb->mark_limit;
        struct commit_list *leftover = NULL;
@@ -228,11 +227,8 @@ static void mark_reachable(struct expire_reflog_policy_cb *cb)
 
        pending = cb->mark_list;
        while (pending) {
-               struct commit_list *entry = pending;
                struct commit_list *parent;
-               pending = entry->next;
-               commit = entry->item;
-               free(entry);
+               struct commit *commit = pop_commit(&pending);
                if (commit->object.flags & REACHABLE)
                        continue;
                if (parse_commit(commit))
index 02d747dcb1a318af98ec06a7ab2f4e489e7c2625..e92a782f7729f0e5522969238920be2b7e607366 100644 (file)
@@ -281,11 +281,8 @@ static int try_difference(const char *arg)
                        b = lookup_commit_reference(end);
                        exclude = get_merge_bases(a, b);
                        while (exclude) {
-                               struct commit_list *n = exclude->next;
-                               show_rev(REVERSED,
-                                        exclude->item->object.sha1,NULL);
-                               free(exclude);
-                               exclude = n;
+                               struct commit *commit = pop_commit(&exclude);
+                               show_rev(REVERSED, commit->object.sha1, NULL);
                        }
                }
                *dotdot = '.';
index 408ce7030731f765228de93066a815bd4db59e30..717a8e7665ee59d9d72165881c962d0d1943dd77 100644 (file)
@@ -3,6 +3,7 @@
 #include "refs.h"
 #include "builtin.h"
 #include "color.h"
+#include "argv-array.h"
 #include "parse-options.h"
 
 static const char* show_branch_usage[] = {
@@ -16,9 +17,7 @@ static const char* show_branch_usage[] = {
 
 static int showbranch_use_color = -1;
 
-static int default_num;
-static int default_alloc;
-static const char **default_arg;
+static struct argv_array default_args = ARGV_ARRAY_INIT;
 
 #define UNINTERESTING  01
 
@@ -53,17 +52,6 @@ static struct commit *interesting(struct commit_list *list)
        return NULL;
 }
 
-static struct commit *pop_one_commit(struct commit_list **list_p)
-{
-       struct commit *commit;
-       struct commit_list *list;
-       list = *list_p;
-       commit = list->item;
-       *list_p = list->next;
-       free(list);
-       return commit;
-}
-
 struct commit_name {
        const char *head_name; /* which head's ancestor? */
        int generation; /* how many parents away from head_name */
@@ -213,7 +201,7 @@ static void join_revs(struct commit_list **list_p,
        while (*list_p) {
                struct commit_list *parents;
                int still_interesting = !!interesting(*list_p);
-               struct commit *commit = pop_one_commit(list_p);
+               struct commit *commit = pop_commit(list_p);
                int flags = commit->object.flags & all_mask;
 
                if (!still_interesting && extra <= 0)
@@ -504,7 +492,7 @@ static int show_merge_base(struct commit_list *seen, int num_rev)
        int exit_status = 1;
 
        while (seen) {
-               struct commit *commit = pop_one_commit(&seen);
+               struct commit *commit = pop_commit(&seen);
                int flags = commit->object.flags & all_mask;
                if (!(flags & UNINTERESTING) &&
                    ((flags & all_revs) == all_revs)) {
@@ -567,16 +555,9 @@ static int git_show_branch_config(const char *var, const char *value, void *cb)
                 * default_arg is now passed to parse_options(), so we need to
                 * mimic the real argv a bit better.
                 */
-               if (!default_num) {
-                       default_alloc = 20;
-                       default_arg = xcalloc(default_alloc, sizeof(*default_arg));
-                       default_arg[default_num++] = "show-branch";
-               } else if (default_alloc <= default_num + 1) {
-                       default_alloc = default_alloc * 3 / 2 + 20;
-                       REALLOC_ARRAY(default_arg, default_alloc);
-               }
-               default_arg[default_num++] = xstrdup(value);
-               default_arg[default_num] = NULL;
+               if (!default_args.argc)
+                       argv_array_push(&default_args, "show-branch");
+               argv_array_push(&default_args, value);
                return 0;
        }
 
@@ -696,9 +677,9 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
        git_config(git_show_branch_config, NULL);
 
        /* If nothing is specified, try the default first */
-       if (ac == 1 && default_num) {
-               ac = default_num;
-               av = default_arg;
+       if (ac == 1 && default_args.argc) {
+               ac = default_args.argc;
+               av = default_args.argv;
        }
 
        ac = parse_options(ac, av, prefix, builtin_show_branch_options,
@@ -927,7 +908,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
        all_revs = all_mask & ~((1u << REV_SHIFT) - 1);
 
        while (seen) {
-               struct commit *commit = pop_one_commit(&seen);
+               struct commit *commit = pop_commit(&seen);
                int this_flag = commit->object.flags;
                int is_merge_point = ((this_flag & all_revs) == all_revs);
 
index 494615d6ff15af5eb95e78053b2799e4c55577fc..d1810c940b3cb354437e86eee38e2c2499375e80 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -455,11 +455,8 @@ struct commit_list *copy_commit_list(struct commit_list *list)
 
 void free_commit_list(struct commit_list *list)
 {
-       while (list) {
-               struct commit_list *temp = list;
-               list = temp->next;
-               free(temp);
-       }
+       while (list)
+               pop_commit(&list);
 }
 
 struct commit_list * commit_list_insert_by_date(struct commit *item, struct commit_list **list)
@@ -505,12 +502,8 @@ void commit_list_sort_by_date(struct commit_list **list)
 struct commit *pop_most_recent_commit(struct commit_list **list,
                                      unsigned int mark)
 {
-       struct commit *ret = (*list)->item;
+       struct commit *ret = pop_commit(list);
        struct commit_list *parents = ret->parents;
-       struct commit_list *old = *list;
-
-       *list = (*list)->next;
-       free(old);
 
        while (parents) {
                struct commit *commit = parents->item;
@@ -861,11 +854,9 @@ static struct commit_list *merge_bases_many(struct commit *one, int n, struct co
        list = paint_down_to_common(one, n, twos);
 
        while (list) {
-               struct commit_list *next = list->next;
-               if (!(list->item->object.flags & STALE))
-                       commit_list_insert_by_date(list->item, &result);
-               free(list);
-               list = next;
+               struct commit *commit = pop_commit(&list);
+               if (!(commit->object.flags & STALE))
+                       commit_list_insert_by_date(commit, &result);
        }
        return result;
 }
@@ -1546,13 +1537,9 @@ int commit_tree_extended(const char *msg, size_t msg_len,
         * if everything else stays the same.
         */
        while (parents) {
-               struct commit_list *next = parents->next;
-               struct commit *parent = parents->item;
-
+               struct commit *parent = pop_commit(&parents);
                strbuf_addf(&buffer, "parent %s\n",
                            sha1_to_hex(parent->object.sha1));
-               free(parents);
-               parents = next;
        }
 
        /* Person/date information */
index 248a21ab94116fabba95e01a8571a458efa99f76..86a5eb2571fb282a1d7bad9e6d097b2374b550dc 100644 (file)
--- a/config.c
+++ b/config.c
@@ -2144,7 +2144,8 @@ int git_config_set_multivar_in_file(const char *config_filename,
        }
 
        if (commit_lock_file(lock) < 0) {
-               error("could not commit config file %s", config_filename);
+               error("could not write config file %s: %s", config_filename,
+                     strerror(errno));
                ret = CONFIG_NO_WRITE;
                lock = NULL;
                goto out_free;
@@ -2330,7 +2331,8 @@ int git_config_rename_section_in_file(const char *config_filename,
        fclose(config_file);
 unlock_and_out:
        if (commit_lock_file(lock) < 0)
-               ret = error("could not commit config file %s", config_filename);
+               ret = error("could not write config file %s: %s",
+                           config_filename, strerror(errno));
 out:
        free(filename_buf);
        return ret;
index 482ca84b451ba7049b702cac7f737ecf4a65fa05..b383ed05c3483d3ac42f404a62505f0fa9e7c2a2 100644 (file)
@@ -663,6 +663,7 @@ __git_list_porcelain_commands ()
                check-mailmap)    : plumbing;;
                check-ref-format) : plumbing;;
                checkout-index)   : plumbing;;
+               column)           : internal helper;;
                commit-tree)      : plumbing;;
                count-objects)    : infrequent;;
                credential)       : credentials;;
index 9f065718513c5c1e82d355aa9dab40ee421b765e..308b777b0aa43a7466fb3402cc67c1f023f57a7a 100755 (executable)
@@ -648,7 +648,7 @@ cmd_split()
                debug "Merging split branch into HEAD..."
                latest_old=$(cache_get latest_old)
                git merge -s ours \
-                       -m "$(rejoin_msg $dir $latest_old $latest_new)" \
+                       -m "$(rejoin_msg "$dir" $latest_old $latest_new)" \
                        $latest_new >&2 || exit $?
        fi
        if [ -n "$branch" ]; then
@@ -735,7 +735,7 @@ cmd_push()
            refspec=$2
            echo "git push using: " $repository $refspec
            localrev=$(git subtree split --prefix="$prefix") || die
-           git push $repository $localrev:refs/heads/$refspec
+           git push "$repository" $localrev:refs/heads/$refspec
        else
            die "'$dir' must already exist. Try 'git subtree add'."
        fi
index 90519823be381390b7f89a1eb2f6007f1e8c86f4..dfbe443deaf1739e47d3f0f791331a3ee6773174 100755 (executable)
@@ -1,6 +1,7 @@
 #!/bin/sh
 #
 # Copyright (c) 2012 Avery Pennaraum
+# Copyright (c) 2015 Alexey Shumkin
 #
 test_description='Basic porcelain support for subtrees
 
@@ -32,25 +33,6 @@ check_equal()
        fi
 }
 
-fixnl()
-{
-       t=""
-       while read x; do
-               t="$t$x "
-       done
-       echo $t
-}
-
-multiline()
-{
-       while read x; do
-               set -- $x
-               for d in "$@"; do
-                       echo "$d"
-               done
-       done
-}
-
 undo()
 {
        git reset --hard HEAD~
@@ -62,11 +44,11 @@ last_commit_message()
 }
 
 test_expect_success 'init subproj' '
-       test_create_repo subproj
+       test_create_repo "sub proj"
 '
 
 # To the subproject!
-cd subproj
+cd ./"sub proj"
 
 test_expect_success 'add sub1' '
        create sub1 &&
@@ -106,39 +88,39 @@ test_expect_success 'add main4' '
 '
 
 test_expect_success 'fetch subproj history' '
-       git fetch ./subproj sub1 &&
+       git fetch ./"sub proj" sub1 &&
        git branch sub1 FETCH_HEAD
 '
 
 test_expect_success 'no subtree exists in main tree' '
-       test_must_fail git subtree merge --prefix=subdir sub1
+       test_must_fail git subtree merge --prefix="sub dir" sub1
 '
 
 test_expect_success 'no pull from non-existant subtree' '
-       test_must_fail git subtree pull --prefix=subdir ./subproj sub1
+       test_must_fail git subtree pull --prefix="sub dir" ./"sub proj" sub1
 '
 
 test_expect_success 'check if --message works for add' '
-       git subtree add --prefix=subdir --message="Added subproject" sub1 &&
+       git subtree add --prefix="sub dir" --message="Added subproject" sub1 &&
        check_equal ''"$(last_commit_message)"'' "Added subproject" &&
        undo
 '
 
 test_expect_success 'check if --message works as -m and --prefix as -P' '
-       git subtree add -P subdir -m "Added subproject using git subtree" sub1 &&
+       git subtree add -P "sub dir" -m "Added subproject using git subtree" sub1 &&
        check_equal ''"$(last_commit_message)"'' "Added subproject using git subtree" &&
        undo
 '
 
 test_expect_success 'check if --message works with squash too' '
-       git subtree add -P subdir -m "Added subproject with squash" --squash sub1 &&
+       git subtree add -P "sub dir" -m "Added subproject with squash" --squash sub1 &&
        check_equal ''"$(last_commit_message)"'' "Added subproject with squash" &&
        undo
 '
 
 test_expect_success 'add subproj to mainline' '
-       git subtree add --prefix=subdir/ FETCH_HEAD &&
-       check_equal ''"$(last_commit_message)"'' "Add '"'subdir/'"' from commit '"'"'''"$(git rev-parse sub1)"'''"'"'"
+       git subtree add --prefix="sub dir"/ FETCH_HEAD &&
+       check_equal ''"$(last_commit_message)"'' "Add '"'sub dir/'"' from commit '"'"'''"$(git rev-parse sub1)"'''"'"'"
 '
 
 # this shouldn't actually do anything, since FETCH_HEAD is already a parent
@@ -147,7 +129,7 @@ test_expect_success 'merge fetched subproj' '
 '
 
 test_expect_success 'add main-sub5' '
-       create subdir/main-sub5 &&
+       create "sub dir/main-sub5" &&
        git commit -m "main-sub5"
 '
 
@@ -157,29 +139,29 @@ test_expect_success 'add main6' '
 '
 
 test_expect_success 'add main-sub7' '
-       create subdir/main-sub7 &&
+       create "sub dir/main-sub7" &&
        git commit -m "main-sub7"
 '
 
 test_expect_success 'fetch new subproj history' '
-       git fetch ./subproj sub2 &&
+       git fetch ./"sub proj" sub2 &&
        git branch sub2 FETCH_HEAD
 '
 
 test_expect_success 'check if --message works for merge' '
-       git subtree merge --prefix=subdir -m "Merged changes from subproject" sub2 &&
+       git subtree merge --prefix="sub dir" -m "Merged changes from subproject" sub2 &&
        check_equal ''"$(last_commit_message)"'' "Merged changes from subproject" &&
        undo
 '
 
 test_expect_success 'check if --message for merge works with squash too' '
-       git subtree merge --prefix subdir -m "Merged changes from subproject using squash" --squash sub2 &&
+       git subtree merge --prefix "sub dir" -m "Merged changes from subproject using squash" --squash sub2 &&
        check_equal ''"$(last_commit_message)"'' "Merged changes from subproject using squash" &&
        undo
 '
 
 test_expect_success 'merge new subproj history into subdir' '
-       git subtree merge --prefix=subdir FETCH_HEAD &&
+       git subtree merge --prefix="sub dir" FETCH_HEAD &&
        git branch pre-split &&
        check_equal ''"$(last_commit_message)"'' "Merge commit '"'"'"$(git rev-parse sub2)"'"'"' into mainline" &&
        undo
@@ -208,53 +190,53 @@ test_expect_success 'Check that the <prefix> exists for a split' '
 '
 
 test_expect_success 'check if --message works for split+rejoin' '
-       spl1=''"$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin)"'' &&
+       spl1=''"$(git subtree split --annotate='"'*'"' --prefix "sub dir" --onto FETCH_HEAD --message "Split & rejoin" --rejoin)"'' &&
        git branch spl1 "$spl1" &&
        check_equal ''"$(last_commit_message)"'' "Split & rejoin" &&
        undo
 '
 
 test_expect_success 'check split with --branch' '
-       spl1=$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin) &&
+       spl1=$(git subtree split --annotate='"'*'"' --prefix "sub dir" --onto FETCH_HEAD --message "Split & rejoin" --rejoin) &&
        undo &&
-       git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --branch splitbr1 &&
+       git subtree split --annotate='"'*'"' --prefix "sub dir" --onto FETCH_HEAD --branch splitbr1 &&
        check_equal ''"$(git rev-parse splitbr1)"'' "$spl1"
 '
 
 test_expect_success 'check hash of split' '
-       spl1=$(git subtree split --prefix subdir) &&
-       git subtree split --prefix subdir --branch splitbr1test &&
+       spl1=$(git subtree split --prefix "sub dir") &&
+       git subtree split --prefix "sub dir" --branch splitbr1test &&
        check_equal ''"$(git rev-parse splitbr1test)"'' "$spl1" &&
        new_hash=$(git rev-parse splitbr1test~2) &&
        check_equal ''"$new_hash"'' "$subdir_hash"
 '
 
 test_expect_success 'check split with --branch for an existing branch' '
-       spl1=''"$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin)"'' &&
+       spl1=''"$(git subtree split --annotate='"'*'"' --prefix "sub dir" --onto FETCH_HEAD --message "Split & rejoin" --rejoin)"'' &&
        undo &&
        git branch splitbr2 sub1 &&
-       git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --branch splitbr2 &&
+       git subtree split --annotate='"'*'"' --prefix "sub dir" --onto FETCH_HEAD --branch splitbr2 &&
        check_equal ''"$(git rev-parse splitbr2)"'' "$spl1"
 '
 
 test_expect_success 'check split with --branch for an incompatible branch' '
-       test_must_fail git subtree split --prefix subdir --onto FETCH_HEAD --branch subdir
+       test_must_fail git subtree split --prefix "sub dir" --onto FETCH_HEAD --branch subdir
 '
 
 test_expect_success 'check split+rejoin' '
-       spl1=''"$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin)"'' &&
+       spl1=''"$(git subtree split --annotate='"'*'"' --prefix "sub dir" --onto FETCH_HEAD --message "Split & rejoin" --rejoin)"'' &&
        undo &&
-       git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --rejoin &&
-       check_equal ''"$(last_commit_message)"'' "Split '"'"'subdir/'"'"' into commit '"'"'"$spl1"'"'"'"
+       git subtree split --annotate='"'*'"' --prefix "sub dir" --onto FETCH_HEAD --rejoin &&
+       check_equal ''"$(last_commit_message)"'' "Split '"'"'sub dir/'"'"' into commit '"'"'"$spl1"'"'"'"
 '
 
 test_expect_success 'add main-sub8' '
-       create subdir/main-sub8 &&
+       create "sub dir/main-sub8" &&
        git commit -m "main-sub8"
 '
 
 # To the subproject!
-cd ./subproj
+cd ./"sub proj"
 
 test_expect_success 'merge split into subproj' '
        git fetch .. spl1 &&
@@ -271,22 +253,22 @@ test_expect_success 'add sub9' '
 cd ..
 
 test_expect_success 'split for sub8' '
-       split2=''"$(git subtree split --annotate='"'*'"' --prefix subdir/ --rejoin)"'' &&
+       split2=''"$(git subtree split --annotate='"'*'"' --prefix "sub dir/" --rejoin)"'' &&
        git branch split2 "$split2"
 '
 
 test_expect_success 'add main-sub10' '
-       create subdir/main-sub10 &&
+       create "sub dir/main-sub10" &&
        git commit -m "main-sub10"
 '
 
 test_expect_success 'split for sub10' '
-       spl3=''"$(git subtree split --annotate='"'*'"' --prefix subdir --rejoin)"'' &&
+       spl3=''"$(git subtree split --annotate='"'*'"' --prefix "sub dir" --rejoin)"'' &&
        git branch spl3 "$spl3"
 '
 
 # To the subproject!
-cd ./subproj
+cd ./"sub proj"
 
 test_expect_success 'merge split into subproj' '
        git fetch .. spl3 &&
@@ -295,42 +277,64 @@ test_expect_success 'merge split into subproj' '
        git branch subproj-merge-spl3
 '
 
-chkm="main4 main6"
-chkms="main-sub10 main-sub5 main-sub7 main-sub8"
-chkms_sub=$(echo $chkms | multiline | sed 's,^,subdir/,' | fixnl)
-chks="sub1 sub2 sub3 sub9"
-chks_sub=$(echo $chks | multiline | sed 's,^,subdir/,' | fixnl)
+chkm="main4
+main6"
+chkms="main-sub10
+main-sub5
+main-sub7
+main-sub8"
+chkms_sub=$(cat <<TXT | sed 's,^,sub dir/,'
+$chkms
+TXT
+)
+chks="sub1
+sub2
+sub3
+sub9"
+chks_sub=$(cat <<TXT | sed 's,^,sub dir/,'
+$chks
+TXT
+)
 
 test_expect_success 'make sure exactly the right set of files ends up in the subproj' '
-       subfiles=''"$(git ls-files | fixnl)"'' &&
-       check_equal "$subfiles" "$chkms $chks"
+       subfiles="$(git ls-files)" &&
+       check_equal "$subfiles" "$chkms
+$chks"
 '
-
 test_expect_success 'make sure the subproj history *only* contains commits that affect the subdir' '
-       allchanges=''"$(git log --name-only --pretty=format:'"''"' | sort | fixnl)"'' &&
-       check_equal "$allchanges" "$chkms $chks"
+       allchanges=''"$(git log --name-only --pretty=format:'"''"' | sort | sed "/^$/d")"'' &&
+       check_equal "$allchanges" "$chkms
+$chks"
 '
 
 # Back to mainline
 cd ..
 
 test_expect_success 'pull from subproj' '
-       git fetch ./subproj subproj-merge-spl3 &&
+       git fetch ./"sub proj" subproj-merge-spl3 &&
        git branch subproj-merge-spl3 FETCH_HEAD &&
-       git subtree pull --prefix=subdir ./subproj subproj-merge-spl3
+       git subtree pull --prefix="sub dir" ./"sub proj" subproj-merge-spl3
 '
 
 test_expect_success 'make sure exactly the right set of files ends up in the mainline' '
-       mainfiles=''"$(git ls-files | fixnl)"'' &&
-       check_equal "$mainfiles" "$chkm $chkms_sub $chks_sub"
+       mainfiles=$(git ls-files) &&
+       check_equal "$mainfiles" "$chkm
+$chkms_sub
+$chks_sub"
 '
 
 test_expect_success 'make sure each filename changed exactly once in the entire history' '
        # main-sub?? and /subdir/main-sub?? both change, because those are the
        # changes that were split into their own history.  And subdir/sub?? never
        # change, since they were *only* changed in the subtree branch.
-       allchanges=''"$(git log --name-only --pretty=format:'"''"' | sort | fixnl)"'' &&
-       check_equal "$allchanges" ''"$(echo $chkms $chkm $chks $chkms_sub | multiline | sort | fixnl)"''
+       allchanges=''"$(git log --name-only --pretty=format:'"''"' | sort | sed "/^$/d")"'' &&
+       check_equal "$allchanges" ''"$(cat <<TXT | sort
+$chkms
+$chkm
+$chks
+$chkms_sub
+TXT
+)"''
 '
 
 test_expect_success 'make sure the --rejoin commits never make it into subproj' '
@@ -377,7 +381,7 @@ cd ../main
 test_expect_success 'add sub as subdir in main' '
        git fetch ../sub master &&
        git branch sub2 FETCH_HEAD &&
-       git subtree add --prefix subdir sub2
+       git subtree add --prefix "sub dir" sub2
 '
 
 cd ../sub
@@ -392,16 +396,16 @@ cd ../main
 test_expect_success 'merge from sub' '
        git fetch ../sub master &&
        git branch sub3 FETCH_HEAD &&
-       git subtree merge --prefix subdir sub3
+       git subtree merge --prefix "sub dir" sub3
 '
 
 test_expect_success 'add main-sub4' '
-       create subdir/main-sub4 &&
+       create "sub dir/main-sub4" &&
        git commit -m "main-sub4"
 '
 
 test_expect_success 'split for main-sub4 without --onto' '
-       git subtree split --prefix subdir --branch mainsub4
+       git subtree split --prefix "sub dir" --branch mainsub4
 '
 
 # at this point, the new commit parent should be sub3 if it is not,
@@ -468,4 +472,50 @@ test_expect_success 'verify one file change per commit' '
        ))
 '
 
+# test push
+
+cd ../..
+
+mkdir test-push
+
+cd test-push
+
+test_expect_success 'init main' '
+       test_create_repo main
+'
+
+test_expect_success 'init sub' '
+       test_create_repo "sub project"
+'
+
+cd ./"sub project"
+
+test_expect_success 'add subproject' '
+       create "sub project" &&
+       git commit -m "Sub project: 1" &&
+       git branch sub-branch-1
+'
+
+cd ../main
+
+test_expect_success 'make first commit and add subproject' '
+       create "main-1" &&
+       git commit -m "main: 1" &&
+       git subtree add "../sub project" --prefix "sub dir" --message "Added subproject" sub-branch-1 &&
+       check_equal "$(last_commit_message)" "Added subproject"
+'
+
+test_expect_success 'make second commit to a subproject file and push it into a sub project' '
+       create "sub dir/sub1" &&
+       git commit -m "Sub project: 2" &&
+       git subtree push "../sub project" --prefix "sub dir" sub-branch-1
+'
+
+cd ../"sub project"
+
+test_expect_success 'Test second commit is pushed' '
+       git checkout sub-branch-1 &&
+       check_equal "$(last_commit_message)" "Sub project: 2"
+'
+
 test_done
index 00aea3aa304f2731da070c447f694591b3021bc9..54c4e047373757598f6de7bf994251f0169b2b38 100644 (file)
@@ -64,7 +64,7 @@ static void rewrite_credential_file(const char *fn, struct credential *c,
                print_line(extra);
        parse_credential_file(fn, c, NULL, print_line);
        if (commit_lock_file(&credential_lock) < 0)
-               die_errno("unable to commit credential store");
+               die_errno("unable to write credential store");
 }
 
 static void store_credential_file(const char *fn, struct credential *c)
index 6c7c3c9b669eb272f4da530aef459c9c39c4d294..b141535e11b0740e8382e23a3c749c252550b493 100644 (file)
@@ -1821,7 +1821,7 @@ static void dump_marks(void)
 
        dump_marks_helper(f, 0, marks);
        if (commit_lock_file(&mark_lock)) {
-               failure |= error("Unable to commit marks file %s: %s",
+               failure |= error("Unable to write file %s: %s",
                        export_marks_file, strerror(errno));
                return;
        }
index a79b6d82ab5ac8def38e7a213f0b990f4688379a..a7ec1187066b90675f55e44103ba54e03d132caa 100755 (executable)
--- a/git-p4.py
+++ b/git-p4.py
@@ -2307,12 +2307,6 @@ def streamP4Files(self, files):
         filesToDelete = []
 
         for f in files:
-            # if using a client spec, only add the files that have
-            # a path in the client
-            if self.clientSpecDirs:
-                if self.clientSpecDirs.map_in_client(f['path']) == "":
-                    continue
-
             filesForCommit.append(f)
             if f['action'] in self.delete_actions:
                 filesToDelete.append(f)
@@ -2383,25 +2377,41 @@ def streamTag(self, gitStream, labelName, labelDetails, commit, epoch):
         gitStream.write(description)
         gitStream.write("\n")
 
+    def inClientSpec(self, path):
+        if not self.clientSpecDirs:
+            return True
+        inClientSpec = self.clientSpecDirs.map_in_client(path)
+        if not inClientSpec and self.verbose:
+            print('Ignoring file outside of client spec: {0}'.format(path))
+        return inClientSpec
+
+    def hasBranchPrefix(self, path):
+        if not self.branchPrefixes:
+            return True
+        hasPrefix = [p for p in self.branchPrefixes
+                        if p4PathStartsWith(path, p)]
+        if hasPrefix and self.verbose:
+            print('Ignoring file outside of prefix: {0}'.format(path))
+        return hasPrefix
+
     def commit(self, details, files, branch, parent = ""):
         epoch = details["time"]
         author = details["user"]
 
         if self.verbose:
-            print "commit into %s" % branch
-
-        # start with reading files; if that fails, we should not
-        # create a commit.
-        new_files = []
-        for f in files:
-            if [p for p in self.branchPrefixes if p4PathStartsWith(f['path'], p)]:
-                new_files.append (f)
-            else:
-                sys.stderr.write("Ignoring file outside of prefix: %s\n" % f['path'])
+            print('commit into {0}'.format(branch))
 
         if self.clientSpecDirs:
             self.clientSpecDirs.update_client_spec_path_cache(files)
 
+        files = [f for f in files
+            if self.inClientSpec(f['path']) and self.hasBranchPrefix(f['path'])]
+
+        if not files and not gitConfigBool('git-p4.keepEmptyCommits'):
+            print('Ignoring revision {0} as it would produce an empty commit.'
+                .format(details['change']))
+            return
+
         self.gitStream.write("commit %s\n" % branch)
         self.gitStream.write("mark :%s\n" % details["change"])
         self.committedChanges.add(int(details["change"]))
@@ -2425,7 +2435,7 @@ def commit(self, details, files, branch, parent = ""):
                 print "parent %s" % parent
             self.gitStream.write("from %s\n" % parent)
 
-        self.streamP4Files(new_files)
+        self.streamP4Files(files)
         self.gitStream.write("\n")
 
         change = int(details["change"])
index e907e0eacf31c21dcb75fa102eb980d6a51f96c3..72508bed93147078ff26b61174f7240b92b21320 100755 (executable)
@@ -1318,6 +1318,13 @@ sub send_message {
                        require Net::SMTP::SSL;
                        $smtp_domain ||= maildomain();
                        require IO::Socket::SSL;
+
+                       # Suppress "variable accessed once" warning.
+                       {
+                               no warnings 'once';
+                               $IO::Socket::SSL::DEBUG = 1;
+                       }
+
                        # Net::SMTP::SSL->new() does not forward any SSL options
                        IO::Socket::SSL::set_client_defaults(
                                ssl_verify_params());
diff --git a/ident.c b/ident.c
index bb1b1743526446bac21abe7da28b15837c8185dc..daf7e1ea8370e5b6a60c5a33a1591ef939655664 100644 (file)
--- a/ident.c
+++ b/ident.c
@@ -91,10 +91,35 @@ static int add_mailname_host(struct strbuf *buf)
        return 0;
 }
 
+static int canonical_name(const char *host, struct strbuf *out)
+{
+       int status = -1;
+
+#ifndef NO_IPV6
+       struct addrinfo hints, *ai;
+       memset (&hints, '\0', sizeof (hints));
+       hints.ai_flags = AI_CANONNAME;
+       if (!getaddrinfo(host, NULL, &hints, &ai)) {
+               if (ai && strchr(ai->ai_canonname, '.')) {
+                       strbuf_addstr(out, ai->ai_canonname);
+                       status = 0;
+               }
+               freeaddrinfo(ai);
+       }
+#else
+       struct hostent *he = gethostbyname(host);
+       if (he && strchr(he->h_name, '.')) {
+               strbuf_addstr(out, he->h_name);
+               status = 0;
+       }
+#endif /* NO_IPV6 */
+
+       return status;
+}
+
 static void add_domainname(struct strbuf *out, int *is_bogus)
 {
        char buf[1024];
-       struct hostent *he;
 
        if (gethostname(buf, sizeof(buf))) {
                warning("cannot get host name: %s", strerror(errno));
@@ -104,9 +129,7 @@ static void add_domainname(struct strbuf *out, int *is_bogus)
        }
        if (strchr(buf, '.'))
                strbuf_addstr(out, buf);
-       else if ((he = gethostbyname(buf)) && strchr(he->h_name, '.'))
-               strbuf_addstr(out, he->h_name);
-       else {
+       else if (canonical_name(buf, out) < 0) {
                strbuf_addf(out, "%s.(none)", buf);
                *is_bogus = 1;
        }
index 63a595c45c961fce54cca95ab6e09e3336f0bb8e..433bd86ccd5c38ff5a4993be4e1463946aead59d 100644 (file)
@@ -126,7 +126,7 @@ static int verify_packfile(struct packed_git *p,
                                    sha1_to_hex(entries[i].sha1), p->pack_name);
                else if (fn) {
                        int eaten = 0;
-                       fn(entries[i].sha1, type, size, data, &eaten);
+                       err |= fn(entries[i].sha1, type, size, data, &eaten);
                        if (eaten)
                                data = NULL;
                }
diff --git a/refs.c b/refs.c
index 4e15f60d98ea8affdef226bce199935fa694b195..9ac9a674a5aa59ea329aae3262615452e40763a6 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -4643,7 +4643,7 @@ int reflog_expire(const char *refname, const unsigned char *sha1,
                                        get_lock_file_path(lock->lk));
                        rollback_lock_file(&reflog_lock);
                } else if (commit_lock_file(&reflog_lock)) {
-                       status |= error("unable to commit reflog '%s' (%s)",
+                       status |= error("unable to write reflog %s: %s",
                                        log_file, strerror(errno));
                } else if (update && commit_ref(lock)) {
                        status |= error("couldn't set %s", lock->ref_name);
index 26504b744786c65ea4d6e1e0abbf5c6409af5358..ee6edfa33548ef4a1a6c5b66bac61423b1d0cef1 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -1975,10 +1975,8 @@ int resolve_remote_symref(struct ref *ref, struct ref *list)
 static void unmark_and_free(struct commit_list *list, unsigned int mark)
 {
        while (list) {
-               struct commit_list *temp = list;
-               temp->item->object.flags &= ~mark;
-               list = temp->next;
-               free(temp);
+               struct commit *commit = pop_commit(&list);
+               commit->object.flags &= ~mark;
        }
 }
 
index af2a18ed7485ea83170409ce55f81473ee606221..f6caef067220e9ecae7cc1b21b7b01380544c066 100644 (file)
@@ -135,10 +135,12 @@ static void mark_tree_contents_uninteresting(struct tree *tree)
 
 void mark_tree_uninteresting(struct tree *tree)
 {
-       struct object *obj = &tree->object;
+       struct object *obj;
 
        if (!tree)
                return;
+
+       obj = &tree->object;
        if (obj->flags & UNINTERESTING)
                return;
        obj->flags |= UNINTERESTING;
@@ -153,10 +155,7 @@ void mark_parents_uninteresting(struct commit *commit)
                commit_list_insert(l->item, &parents);
 
        while (parents) {
-               struct commit *commit = parents->item;
-               l = parents;
-               parents = parents->next;
-               free(l);
+               struct commit *commit = pop_commit(&parents);
 
                while (commit) {
                        /*
@@ -1102,14 +1101,10 @@ static int limit_list(struct rev_info *revs)
        }
 
        while (list) {
-               struct commit_list *entry = list;
-               struct commit *commit = list->item;
+               struct commit *commit = pop_commit(&list);
                struct object *obj = &commit->object;
                show_early_output_fn_t show;
 
-               list = list->next;
-               free(entry);
-
                if (commit == interesting_cache)
                        interesting_cache = NULL;
 
@@ -2733,10 +2728,7 @@ static void simplify_merges(struct rev_info *revs)
                yet_to_do = NULL;
                tail = &yet_to_do;
                while (list) {
-                       commit = list->item;
-                       next = list->next;
-                       free(list);
-                       list = next;
+                       commit = pop_commit(&list);
                        tail = simplify_one(revs, commit, tail);
                }
        }
@@ -2748,10 +2740,7 @@ static void simplify_merges(struct rev_info *revs)
        while (list) {
                struct merge_simplify_state *st;
 
-               commit = list->item;
-               next = list->next;
-               free(list);
-               list = next;
+               commit = pop_commit(&list);
                st = locate_simplify_state(revs, commit);
                if (st->simplified == commit)
                        tail = &commit_list_insert(commit, tail)->next;
@@ -3125,11 +3114,7 @@ static struct commit *get_revision_1(struct rev_info *revs)
                return NULL;
 
        do {
-               struct commit_list *entry = revs->commits;
-               struct commit *commit = entry->item;
-
-               revs->commits = entry->next;
-               free(entry);
+               struct commit *commit = pop_commit(&revs->commits);
 
                if (revs->reflog_info) {
                        save_parents(revs, commit);
index 4160e6882de40b2e032727616084a08c94a1e565..72289696d9f50d823cf20c5cfaf1847eba3b2708 100644 (file)
@@ -3507,12 +3507,12 @@ static int for_each_file_in_obj_subdir(int subdir_nr,
                                break;
                }
        }
-       strbuf_setlen(path, baselen);
+       closedir(dir);
 
+       strbuf_setlen(path, baselen);
        if (!r && subdir_cb)
                r = subdir_cb(subdir_nr, path->buf, data);
 
-       closedir(dir);
        return r;
 }
 
index d49a3d6e9f02e292a04981e34f0a2d0b1ffa00d0..4dcb454d181caf5983f0182eb6ce40fd1d7ebaec 100644 (file)
--- a/shallow.c
+++ b/shallow.c
@@ -401,13 +401,9 @@ static void paint_down(struct paint_info *info, const unsigned char *sha1,
        commit_list_insert(c, &head);
        while (head) {
                struct commit_list *p;
-               struct commit *c = head->item;
+               struct commit *c = pop_commit(&head);
                uint32_t **refs = ref_bitmap_at(&info->ref_bitmap, c);
 
-               p = head;
-               head = head->next;
-               free(p);
-
                /* XXX check "UNINTERESTING" from pack bitmaps if available */
                if (c->object.flags & (SEEN | UNINTERESTING))
                        continue;
index 98eb49ac236e9e9d08d5da5167f7e257db173c41..9067e0206f12d472d6940842635759b4447010f1 100755 (executable)
@@ -1234,7 +1234,7 @@ test_expect_success 'tabs and spaces are accepted in the todolist' '
                # Turn single spaces into space/tab mix
                sed "1s/ /      /g; 2s/ /  /g; 3s/ /    /g" "$1"
                printf "\n\t# comment\n #more\n\t # comment\n"
-       ) >$1.new
+       ) >"$1.new"
        mv "$1.new" "$1"
        EOF
        test_set_editor "$(pwd)/add-indent.sh" &&
index c9d3ed14c3a3238208b44ce815e37123298dea77..362b1581e092826400d78515cdc8d5cb0f7cfc29 100755 (executable)
@@ -242,13 +242,6 @@ clean_mark () {
        sort >$(basename "$1")
 }
 
-cmp_marks () {
-       test_when_finished "rm -rf git.marks testgit.marks" &&
-       clean_mark ".git/testgit/$1/git.marks" &&
-       clean_mark ".git/testgit/$1/testgit.marks" &&
-       test_cmp git.marks testgit.marks
-}
-
 test_expect_success 'proper failure checks for fetching' '
        (cd local &&
        test_must_fail env GIT_REMOTE_TESTGIT_FAILURE=1 git fetch 2>error &&
@@ -258,12 +251,15 @@ test_expect_success 'proper failure checks for fetching' '
 '
 
 test_expect_success 'proper failure checks for pushing' '
+       test_when_finished "rm -rf local/git.marks local/testgit.marks" &&
        (cd local &&
        git checkout -b crash master &&
        echo crash >>file &&
        git commit -a -m crash &&
        test_must_fail env GIT_REMOTE_TESTGIT_FAILURE=1 git push --all &&
-       cmp_marks origin
+       clean_mark ".git/testgit/origin/git.marks" &&
+       clean_mark ".git/testgit/origin/testgit.marks" &&
+       test_cmp git.marks testgit.marks
        )
 '
 
index 7eeb207b32b48041037488f42e645b8b12742690..6f12b235b3a9330497e814d5e8f564aec94d0c44 100755 (executable)
@@ -174,9 +174,9 @@ test_expect_success 'mergetool skips autoresolved' '
 '
 
 test_expect_success 'mergetool merges all from subdir' '
+       test_config rerere.enabled false &&
        (
                cd subdir &&
-               test_config rerere.enabled false &&
                test_must_fail git merge master &&
                ( yes "r" | git mergetool ../submod ) &&
                ( yes "d" "d" | git mergetool --no-prompt ) &&
index a771cf77f5b4f52aa14965a78f9dce2df6a65eec..ec8bc8c7656fd7f0fd03226b5491f5bc9d532dd7 100755 (executable)
@@ -492,12 +492,12 @@ test_expect_success PERL 'difftool --no-symlinks detects conflict ' '
 
 test_expect_success PERL 'difftool properly honors gitlink and core.worktree' '
        git submodule add ./. submod/ule &&
+       test_config -C submod/ule diff.tool checktrees &&
+       test_config -C submod/ule difftool.checktrees.cmd '\''
+               test -d "$LOCAL" && test -d "$REMOTE" && echo good
+               '\'' &&
        (
                cd submod/ule &&
-               test_config diff.tool checktrees &&
-               test_config difftool.checktrees.cmd '\''
-                       test -d "$LOCAL" && test -d "$REMOTE" && echo good
-               '\'' &&
                echo good >expect &&
                git difftool --tool=checktrees --dir-diff HEAD~ >actual &&
                test_cmp expect actual
diff --git a/t/t9826-git-p4-keep-empty-commits.sh b/t/t9826-git-p4-keep-empty-commits.sh
new file mode 100755 (executable)
index 0000000..be12960
--- /dev/null
@@ -0,0 +1,134 @@
+#!/bin/sh
+
+test_description='Clone repositories and keep empty commits'
+
+. ./lib-git-p4.sh
+
+test_expect_success 'start p4d' '
+       start_p4d
+'
+
+test_expect_success 'Create a repo' '
+       client_view "//depot/... //client/..." &&
+       (
+               cd "$cli" &&
+
+               mkdir -p subdir &&
+
+               >subdir/file1.txt &&
+               p4 add subdir/file1.txt &&
+               p4 submit -d "Add file 1" &&
+
+               >file2.txt &&
+               p4 add file2.txt &&
+               p4 submit -d "Add file 2" &&
+
+               >subdir/file3.txt &&
+               p4 add subdir/file3.txt &&
+               p4 submit -d "Add file 3" &&
+
+               >file4.txt &&
+               p4 add file4.txt &&
+               p4 submit -d "Add file 4" &&
+
+               p4 delete subdir/file3.txt &&
+               p4 submit -d "Remove file 3" &&
+
+               p4 delete file4.txt &&
+               p4 submit -d "Remove file 4"
+       )
+'
+
+test_expect_success 'Clone repo root path with all history' '
+       client_view "//depot/... //client/..." &&
+       test_when_finished cleanup_git &&
+       (
+               cd "$git" &&
+               git init . &&
+               git p4 clone --use-client-spec --destination="$git" //depot@all &&
+               cat >expect <<-\EOF &&
+Remove file 4
+[git-p4: depot-paths = "//depot/": change = 6]
+
+Remove file 3
+[git-p4: depot-paths = "//depot/": change = 5]
+
+Add file 4
+[git-p4: depot-paths = "//depot/": change = 4]
+
+Add file 3
+[git-p4: depot-paths = "//depot/": change = 3]
+
+Add file 2
+[git-p4: depot-paths = "//depot/": change = 2]
+
+Add file 1
+[git-p4: depot-paths = "//depot/": change = 1]
+
+               EOF
+               git log --format=%B >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'Clone repo subdir with all history but keep empty commits' '
+       client_view "//depot/subdir/... //client/subdir/..." &&
+       test_when_finished cleanup_git &&
+       (
+               cd "$git" &&
+               git init . &&
+               git config git-p4.keepEmptyCommits true &&
+               git p4 clone --use-client-spec --destination="$git" //depot@all &&
+               cat >expect <<-\EOF &&
+Remove file 4
+[git-p4: depot-paths = "//depot/": change = 6]
+
+Remove file 3
+[git-p4: depot-paths = "//depot/": change = 5]
+
+Add file 4
+[git-p4: depot-paths = "//depot/": change = 4]
+
+Add file 3
+[git-p4: depot-paths = "//depot/": change = 3]
+
+Add file 2
+[git-p4: depot-paths = "//depot/": change = 2]
+
+Add file 1
+[git-p4: depot-paths = "//depot/": change = 1]
+
+               EOF
+               git log --format=%B >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'Clone repo subdir with all history' '
+       client_view "//depot/subdir/... //client/subdir/..." &&
+       test_when_finished cleanup_git &&
+       (
+               cd "$git" &&
+               git init . &&
+               git p4 clone --use-client-spec --destination="$git" --verbose //depot@all &&
+               cat >expect <<-\EOF &&
+Remove file 3
+[git-p4: depot-paths = "//depot/": change = 5]
+
+Add file 3
+[git-p4: depot-paths = "//depot/": change = 3]
+
+Add file 1
+[git-p4: depot-paths = "//depot/": change = 1]
+
+               EOF
+               git log --format=%B >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'kill p4d' '
+       kill_p4d
+'
+
+test_done
index e8d3c0fdbc76d93ea18f6da1c869fc963e7ca81d..6dffb8bcde83b82fa40b05dd8ee815f4e6a3cdd3 100644 (file)
@@ -201,7 +201,14 @@ test_chmod () {
 
 # Unset a configuration variable, but don't fail if it doesn't exist.
 test_unconfig () {
-       git config --unset-all "$@"
+       config_dir=
+       if test "$1" = -C
+       then
+               shift
+               config_dir=$1
+               shift
+       fi
+       git ${config_dir:+-C "$config_dir"} config --unset-all "$@"
        config_status=$?
        case "$config_status" in
        5) # ok, nothing to unset
@@ -213,8 +220,15 @@ test_unconfig () {
 
 # Set git config, automatically unsetting it after the test is over.
 test_config () {
-       test_when_finished "test_unconfig '$1'" &&
-       git config "$@"
+       config_dir=
+       if test "$1" = -C
+       then
+               shift
+               config_dir=$1
+               shift
+       fi
+       test_when_finished "test_unconfig ${config_dir:+-C '$config_dir'} '$1'" &&
+       git ${config_dir:+-C "$config_dir"} config "$@"
 }
 
 test_config_global () {
@@ -722,6 +736,11 @@ test_seq () {
 # what went wrong.
 
 test_when_finished () {
+       # We cannot detect when we are in a subshell in general, but by
+       # doing so on Bash is better than nothing (the test will
+       # silently pass on other shells).
+       test "${BASH_SUBSHELL-0}" = 0 ||
+       error "bug in test script: test_when_finished does nothing in a subshell"
        test_cleanup="{ $*
                } && (exit \"\$eval_ret\"); eval_ret=\$?; $test_cleanup"
 }
index 89e832b64a0548ec79802dfc6911eff9f5c353be..d0bc3ca07ab1b005884c80901ea8dee04452b71a 100644 (file)
@@ -316,10 +316,8 @@ static int reachable(struct commit *want)
 
        commit_list_insert_by_date(want, &work);
        while (work) {
-               struct commit_list *list = work->next;
-               struct commit *commit = work->item;
-               free(work);
-               work = list;
+               struct commit_list *list;
+               struct commit *commit = pop_commit(&work);
 
                if (commit->object.flags & THEY_HAVE) {
                        want->object.flags |= COMMON_KNOWN;