summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sha1_file.c47
-rwxr-xr-xt/t5700-clone-reference.sh5
2 files changed, 45 insertions, 7 deletions
diff --git a/sha1_file.c b/sha1_file.c
index 1cee438422..3400b8be4c 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -404,13 +404,46 @@ void read_info_alternates(const char * relative_base, int depth)
void add_to_alternates_file(const char *reference)
{
struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
- int fd = hold_lock_file_for_append(lock, git_path("objects/info/alternates"), LOCK_DIE_ON_ERROR);
- const char *alt = mkpath("%s\n", reference);
- write_or_die(fd, alt, strlen(alt));
- if (commit_lock_file(lock))
- die("could not close alternates file");
- if (alt_odb_tail)
- link_alt_odb_entries(alt, strlen(alt), '\n', NULL, 0);
+ char *alts = git_pathdup("objects/info/alternates");
+ FILE *in, *out;
+
+ hold_lock_file_for_update(lock, alts, LOCK_DIE_ON_ERROR);
+ out = fdopen_lock_file(lock, "w");
+ if (!out)
+ die_errno("unable to fdopen alternates lockfile");
+
+ in = fopen(alts, "r");
+ if (in) {
+ struct strbuf line = STRBUF_INIT;
+ int found = 0;
+
+ while (strbuf_getline(&line, in, '\n') != EOF) {
+ if (!strcmp(reference, line.buf)) {
+ found = 1;
+ break;
+ }
+ fprintf_or_die(out, "%s\n", line.buf);
+ }
+
+ strbuf_release(&line);
+ fclose(in);
+
+ if (found) {
+ rollback_lock_file(lock);
+ lock = NULL;
+ }
+ }
+ else if (errno != ENOENT)
+ die_errno("unable to read alternates file");
+
+ if (lock) {
+ fprintf_or_die(out, "%s\n", reference);
+ if (commit_lock_file(lock))
+ die_errno("unable to move new alternates file into place");
+ if (alt_odb_tail)
+ link_alt_odb_entries(reference, strlen(reference), '\n', NULL, 0);
+ }
+ free(alts);
}
int foreach_alt_odb(alt_odb_fn fn, void *cb)
diff --git a/t/t5700-clone-reference.sh b/t/t5700-clone-reference.sh
index 51d131a97a..ef1779f5ca 100755
--- a/t/t5700-clone-reference.sh
+++ b/t/t5700-clone-reference.sh
@@ -120,6 +120,11 @@ test_expect_success 'cloning with reference being subset of source (-l -s)' '
git clone -l -s --reference A B E
'
+test_expect_success 'cloning with multiple references drops duplicates' '
+ git clone -s --reference B --reference A --reference B A dups &&
+ test_line_count = 2 dups/.git/objects/info/alternates
+'
+
test_expect_success 'clone with reference from a tagged repository' '
(
cd A && git tag -a -m tagged HEAD