Commit Diff


commit - f270548637fc35b5b9576bd91ac1bdcbffb1f039
commit + 3cd22b214454d9973c61426c8a4bd7cf2f8de6ae
blob - a11bcd4d9ca6b55a8f744cf2afe8a8c6820e9cc7
blob + 516a404d1faaff893383511507cf22857a894374
--- regress/cmdline/cherrypick.sh
+++ regress/cmdline/cherrypick.sh
@@ -857,7 +857,342 @@ test_cherrypick_conflict_no_eol2() {
 	if [ "$ret" != "0" ]; then
 		#diff -u $testroot/stdout.expected $testroot/stdout
 		ret="xfail $(head -n 1 $testroot/stderr)"
+	fi
+	test_done "$testroot" "$ret"
+}
+
+test_cherrypick_unrelated_changes() {
+	local testroot=`test_init cherrypick_unrelated_changes`
+
+	# Sorry about the large HERE document but I have not found
+	# a smaller reproduction recipe yet...
+	cat > $testroot/repo/reference.c <<EOF
+const struct got_error *
+got_ref_alloc(struct got_reference **ref, const char *name,
+    struct got_object_id *id)
+{
+        if (!is_valid_ref_name(name))
+                return got_error_path(name, GOT_ERR_BAD_REF_NAME);
+
+        return alloc_ref(ref, name, id, 0);
+}
+
+static const struct got_error *
+parse_packed_ref_line(struct got_reference **ref, const char *abs_refname,
+    const char *line)
+{
+        struct got_object_id id;
+        const char *name;
+
+        *ref = NULL;
+
+        if (line[0] == '#' || line[0] == '^')
+                return NULL;
+
+        if (!got_parse_sha1_digest(id.sha1, line))
+                return got_error(GOT_ERR_BAD_REF_DATA);
+
+        if (abs_refname) {
+                if (strcmp(line + SHA1_DIGEST_STRING_LENGTH, abs_refname) != 0)
+                        return NULL;
+                name = abs_refname;
+        } else
+                name = line + SHA1_DIGEST_STRING_LENGTH;
+
+        return alloc_ref(ref, name, &id, GOT_REF_IS_PACKED);
+}
+
+static const struct got_error *
+open_packed_ref(struct got_reference **ref, FILE *f, const char **subdirs,
+    int nsubdirs, const char *refname)
+{
+        const struct got_error *err = NULL;
+        char *abs_refname;
+        char *line = NULL;
+        size_t linesize = 0;
+        ssize_t linelen;
+        int i, ref_is_absolute = (strncmp(refname, "refs/", 5) == 0);
+
+        *ref = NULL;
+
+        if (ref_is_absolute)
+                abs_refname = (char *)refname;
+        do {
+                linelen = getline(&line, &linesize, f);
+                if (linelen == -1) {
+                        if (feof(f))
+                                break;
+                        err = got_ferror(f, GOT_ERR_BAD_REF_DATA);
+                        break;
+                }
+                if (linelen > 0 && line[linelen - 1] == '\n')
+                        line[linelen - 1] = '\0';
+                for (i = 0; i < nsubdirs; i++) {
+                        if (!ref_is_absolute &&
+                            asprintf(&abs_refname, "refs/%s/%s", subdirs[i],
+                            refname) == -1)
+                                return got_error_from_errno("asprintf");
+                        err = parse_packed_ref_line(ref, abs_refname, line);
+                        if (!ref_is_absolute)
+                                free(abs_refname);
+                        if (err || *ref != NULL)
+                                break;
+                }
+                if (err)
+                        break;
+        } while (*ref == NULL);
+        free(line);
+
+        return err;
+}
+
+static const struct got_error *
+open_ref(struct got_reference **ref, const char *path_refs, const char *subdir,
+    const char *name, int lock)
+{
+        const struct got_error *err = NULL;
+        char *path = NULL;
+        char *absname = NULL;
+        int ref_is_absolute = (strncmp(name, "refs/", 5) == 0);
+        int ref_is_well_known = (subdir[0] == '\0' && is_well_known_ref(name));
+
+        *ref = NULL;
+
+        if (ref_is_absolute || ref_is_well_known) {
+                if (asprintf(&path, "%s/%s", path_refs, name) == -1)
+                        return got_error_from_errno("asprintf");
+                absname = (char *)name;
+        } else {
+                if (asprintf(&path, "%s/%s%s%s", path_refs, subdir,
+                    subdir[0] ? "/" : "", name) == -1)
+                        return got_error_from_errno("asprintf");
+
+                if (asprintf(&absname, "refs/%s%s%s",
+                    subdir, subdir[0] ? "/" : "", name) == -1) {
+                        err = got_error_from_errno("asprintf");
+                        goto done;
+                }
+        }
+
+        err = parse_ref_file(ref, name, absname, path, lock);
+done:
+        if (!ref_is_absolute && !ref_is_well_known)
+                free(absname);
+        free(path);
+        return err;
+}
+
+const struct got_error *
+got_ref_open(struct got_reference **ref, struct got_repository *repo,
+   const char *refname, int lock)
+{
+        const struct got_error *err = NULL;
+        char *path_refs = NULL;
+        const char *subdirs[] = {
+            GOT_REF_HEADS, GOT_REF_TAGS, GOT_REF_REMOTES
+        };
+        size_t i;
+        int well_known = is_well_known_ref(refname);
+        struct got_lockfile *lf = NULL;
+
+        *ref = NULL;
+
+        path_refs = get_refs_dir_path(repo, refname);
+        if (path_refs == NULL) {
+                err = got_error_from_errno2("get_refs_dir_path", refname);
+                goto done;
+        }
+
+        if (well_known) {
+                err = open_ref(ref, path_refs, "", refname, lock);
+        } else {
+                char *packed_refs_path;
+                FILE *f;
+
+                /* Search on-disk refs before packed refs! */
+                for (i = 0; i < nitems(subdirs); i++) {
+                        err = open_ref(ref, path_refs, subdirs[i], refname,
+                            lock);
+                        if ((err && err->code != GOT_ERR_NOT_REF) || *ref)
+                                goto done;
+                }
+
+                packed_refs_path = got_repo_get_path_packed_refs(repo);
+                if (packed_refs_path == NULL) {
+                        err = got_error_from_errno(
+                            "got_repo_get_path_packed_refs");
+                        goto done;
+                }
+
+                if (lock) {
+                        err = got_lockfile_lock(&lf, packed_refs_path);
+                        if (err)
+                                goto done;
+                }
+                f = fopen(packed_refs_path, "rb");
+                free(packed_refs_path);
+                if (f != NULL) {
+                        err = open_packed_ref(ref, f, subdirs, nitems(subdirs),
+                            refname);
+                        if (!err) {
+                                if (fclose(f) == EOF) {
+                                        err = got_error_from_errno("fclose");
+                                        got_ref_close(*ref);
+                                        *ref = NULL;
+                                } else if (*ref)
+                                        (*ref)->lf = lf;
+                        }
+                }
+        }
+done:
+        if (!err && *ref == NULL)
+                err = got_error_not_ref(refname);
+        if (err && lf)
+                got_lockfile_unlock(lf);
+        free(path_refs);
+        return err;
+}
+
+struct got_reference *
+got_ref_dup(struct got_reference *ref)
+{
+        struct got_reference *ret;
+
+        ret = calloc(1, sizeof(*ret));
+        if (ret == NULL)
+                return NULL;
+
+        ret->flags = ref->flags;
+        if (ref->flags & GOT_REF_IS_SYMBOLIC) {
+                ret->ref.symref.name = strdup(ref->ref.symref.name);
+                if (ret->ref.symref.name == NULL) {
+                        free(ret);
+                        return NULL;
+                }
+                ret->ref.symref.ref = strdup(ref->ref.symref.ref);
+                if (ret->ref.symref.ref == NULL) {
+                        free(ret->ref.symref.name);
+                        free(ret);
+                        return NULL;
+                }
+        } else {
+                ret->ref.ref.name = strdup(ref->ref.ref.name);
+                if (ret->ref.ref.name == NULL) {
+                        free(ret);
+                        return NULL;
+                }
+                memcpy(ret->ref.ref.sha1, ref->ref.ref.sha1,
+                    sizeof(ret->ref.ref.sha1));
+        }
+
+        return ret;
+}
+
+const struct got_error *
+got_reflist_entry_dup(struct got_reflist_entry **newp,
+    struct got_reflist_entry *re)
+{
+        const struct got_error *err = NULL;
+        struct got_reflist_entry *new;
+
+        *newp = NULL;
+
+        new = malloc(sizeof(*new));
+        if (new == NULL)
+                return got_error_from_errno("malloc");
+
+        new->ref = got_ref_dup(re->ref);
+        if (new->ref == NULL) {
+                err = got_error_from_errno("got_ref_dup");
+                free(new);
+                return err;
+        }
+
+        *newp = new;
+        return NULL;
+}
+
+void
+got_ref_list_free(struct got_reflist_head *refs)
+{
+        struct got_reflist_entry *re;
+
+        while ((re = TAILQ_FIRST(refs))) {
+                TAILQ_REMOVE(refs, re, entry);
+                free(re);
+        }
+
+}
+EOF
+	(cd $testroot/repo && git add reference.c)
+	git_commit $testroot/repo -m "added reference.c file"
+	local base_commit=`git_show_head $testroot/repo`
+
+	got checkout $testroot/repo $testroot/wt > /dev/null
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		test_done "$testroot" "$ret"
+		return 1
 	fi
+
+	(cd $testroot/repo && git checkout -q -b newbranch)
+	ed -s $testroot/repo/reference.c <<EOF
+91a
+        if (!is_valid_ref_name(name))
+                return got_error_path(name, GOT_ERR_BAD_REF_NAME);
+
+.
+w
+q
+EOF
+	git_commit $testroot/repo -m "added lines on newbranch"
+	local branch_rev1=`git_show_head $testroot/repo`
+
+	ed -s $testroot/repo/reference.c <<EOF
+255a
+                got_ref_close(re->ref);
+.
+w
+q
+EOF
+	git_commit $testroot/repo -m "more lines on newbranch"
+
+	local branch_rev2=`git_show_head $testroot/repo`
+
+	(cd $testroot/wt && got cherrypick $branch_rev2 > $testroot/stdout)
+
+	echo "G  reference.c" > $testroot/stdout.expected
+	echo "Merged commit $branch_rev2" >> $testroot/stdout.expected
+
+	cmp -s $testroot/stdout.expected $testroot/stdout
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		diff -u $testroot/stdout.expected $testroot/stdout
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+
+	cat > $testroot/diff.expected <<EOF
+--- reference.c
++++ reference.c
+@@ -250,6 +250,7 @@ got_ref_list_free(struct got_reflist_head *refs)
+ 
+         while ((re = TAILQ_FIRST(refs))) {
+                 TAILQ_REMOVE(refs, re, entry);
++                got_ref_close(re->ref);
+                 free(re);
+         }
+ 
+EOF
+	(cd $testroot/wt && got diff |
+		egrep -v '^(diff|blob|file)' > $testroot/diff)
+	cmp -s $testroot/diff.expected $testroot/diff
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		#diff -u $testroot/diff.expected $testroot/diff
+		ret="xfail cherrypick results in unexpected diff"
+	fi
+
 	test_done "$testroot" "$ret"
 }
 
@@ -873,3 +1208,4 @@ run_test test_cherrypick_symlink_conflicts
 run_test test_cherrypick_with_path_prefix_and_empty_tree
 run_test test_cherrypick_conflict_no_eol
 run_test test_cherrypick_conflict_no_eol2
+run_test test_cherrypick_unrelated_changes