git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH 04/17] add_to_alternates_file: don't add duplicate entries

From
Jeff King <peff@peff.net>
Date
Aug 10, 2015, 09:34 UTC
Message-ID
<20150810093446.GD30981@sigill.intra.peff.net>
In-Reply-To
<20150810092731.GA9027@sigill.intra.peff.net>

The add_to_alternates_file function blindly uses hold_lock_file_for_append to copy the existing contents, and then adds the new line to it. This has two minor problems:

  1. We might add duplicate entries, which are ugly and
     inefficient.
  2. We do not check that the file ends with a newline, in
     which case we would bogusly append to the final line.
     This is quite unlikely in practice, though, as we call
     this function only from git-clone, so presumably we are
     the only writers of the file (and we always add a
     newline).

Instead of using hold_lock_file_for_append, let's copy the file line by line, which ensures all records are properly terminated. If we see an extra line, we can simply abort the update (there is no point in even copying the rest, as we know that it would be identical to the original).

As a bonus, we also get rid of some calls to the static-buffer mkpath and git_path functions.

Signed-off-by: Jeff King <peff@peff.net>
---
This is a polishing of the thread at:
  http://thread.gmane.org/gmane.comp.version-control.git/270341
 sha1_file.c                | 47 +++++++++++++++++++++++++++++++++++++++-------
 t/t5700-clone-reference.sh |  5 +++++
 2 files changed, 45 insertions(+), 7 deletions(-)
diff --git a/sha1_file.c b/sha1_file.c
index 1cee438..3400b8b 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 51d131a..ef1779f 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
-- 
2.5.0.414.g670f2a4
Previous: Jeff KingNext: Michael Haggerty
Message 5 of 34 in “removing questionable uses of git_path”
  1. 0/17 removing questionable uses of git_pathJeff King, Aug 10, 2015
  2. 01/17 cache.h: clarify documentation for git_path, et alJeff King, Aug 10, 2015
  3. 02/17 cache.h: complete set of git_path_submodule helpersJeff King, Aug 10, 2015
  4. 03/17 t5700: modernize styleJeff King, Aug 10, 2015
  5. 04/17 add_to_alternates_file: don't add duplicate entriesJeff King, Aug 10, 2015
  6. Michael HaggertyAug 11, 2015
  7. Jeff KingAug 11, 2015
  8. 05/17 remove hold_lock_file_for_appendJeff King, Aug 10, 2015
  9. Junio C HamanoAug 10, 2015
  10. Jeff KingAug 11, 2015
  11. 06/17 prefer git_pathdup to git_path in some possibly-dangerous casesJeff King, Aug 10, 2015
  12. 07/17 prefer mkpathdup to mkpath in assignmentsJeff King, Aug 10, 2015
  13. 08/17 remote.c: drop extraneous local variable from migrate_fileJeff King, Aug 10, 2015
  14. 09/17 refs.c: remove extra git_path calls from read_loose_refsJeff King, Aug 10, 2015
  15. 10/17 path.c: drop git_path_submoduleJeff King, Aug 10, 2015
  16. Junio C HamanoAug 10, 2015
  17. Junio C HamanoAug 10, 2015
  18. Junio C HamanoAug 10, 2015
  19. Jeff KingAug 11, 2015
  20. 11/17 refs.c: simplify strbufs in reflog setup and writingJeff King, Aug 10, 2015
  21. Michael HaggertyAug 10, 2015
  22. Jeff KingAug 10, 2015
  23. 12/17 refs.c: avoid repeated git_path calls in rename_tmp_logJeff King, Aug 10, 2015
  24. 13/17 refs.c: avoid git_path assignment in lock_ref_sha1_basicJeff King, Aug 10, 2015
  25. 14/17 refs.c: remove_empty_directories can take a strbufJeff King, Aug 10, 2015
  26. 15/17 find_hook: keep our own static bufferJeff King, Aug 10, 2015
  27. 16/17 get_repo_path: refactor path-allocationJeff King, Aug 10, 2015
  28. 17/17 memoize common git-path "constant" filesJeff King, Aug 10, 2015
  29. Michael HaggertyAug 10, 2015
  30. Jeff KingAug 10, 2015
  31. Michael HaggertyAug 10, 2015
  32. Junio C HamanoAug 10, 2015
  33. Jeff KingAug 10, 2015
  34. Duy NguyenAug 15, 2015

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.