All of lore.kernel.org
 help / color / mirror / Atom feed
From: Junio C Hamano <gitster@pobox.com>
To: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Cc: "Kristian Høgsberg" <krh@redhat.com>, git@vger.kernel.org
Subject: Re: [PATCH] builtin-commit: fix "git add x y && git commit y" committing x, too
Date: Sat, 17 Nov 2007 00:45:56 -0800	[thread overview]
Message-ID: <7vk5ohuunv.fsf@gitster.siamese.dyndns.org> (raw)
In-Reply-To: <Pine.LNX.4.64.0711160036450.30886@racer.site> (Johannes Schindelin's message of "Fri, 16 Nov 2007 00:43:17 +0000 (GMT)")

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:

> It's not only about discarding the cache.  It's also about avoiding do 
> regenerate the index completely; this would waste time, especially for big 
> trees.

I was looking at this code earlier tonight but I am too tired so
here are a few comments before I stop.

> But the code you are referencing is only updating the index.  The code I 
> added is to build the temporary index in a correct manner.

Yes, except that it is only in the partial commit codepath and
there is not much point optimizing it, as there are more to it.

If a path that was not in the HEAD was added to the index
earlier, and the path was named on the command line, the
add_files_to_index() function you are borrowing from the
implementation of "add -u" would not notice it.  Look at the
script version of git-commit.sh and look for places near
"ls-files --error-unmatch --with-tree=HEAD".

I _think_ we need to do the equivalent of this, keep the
affected paths in a path-list and use add_file_to_cache()
instead.  We need to feed the same set of paths to update the
index twice (once for the fake one for partial commit, and
another for the real index to be used after the commit is made),
and (1) using add_files_to_index() is more expensive than
walking a path-list, and (2) add_files_to_index() is a wrong
thing to use anyway (by definition you cannot notice addition
when you are comparing the index and the work tree, so I think
your patch to update_callback() is a no-op).


I noticed that the implementation left next-index crufts almost
every time it was run, and started to clean it up.  Here is
still a WIP and it does not optimize the read_tree(HEAD) part,
but you should be able to replace that part with your one-way
merge easily.  As I haven't done that ls-files --error-unmatch
equivalent, this does not pass tests that involve partial
commits with added or removed paths.

---

 builtin-commit.c |  174 +++++++++++++++++++++++++++++++++++++++++++-----------
 1 files changed, 139 insertions(+), 35 deletions(-)

diff --git a/builtin-commit.c b/builtin-commit.c
index 3e7d281..187d613 100644
--- a/builtin-commit.c
+++ b/builtin-commit.c
@@ -7,6 +7,7 @@
 
 #include "cache.h"
 #include "cache-tree.h"
+#include "dir.h"
 #include "builtin.h"
 #include "diff.h"
 #include "diffcore.h"
@@ -28,7 +29,13 @@ static const char * const builtin_commit_usage[] = {
 static unsigned char head_sha1[20], merge_head_sha1[20];
 static char *use_message_buffer;
 static const char commit_editmsg[] = "COMMIT_EDITMSG";
-static struct lock_file lock_file;
+static struct lock_file index_lock; /* real index */
+static struct lock_file false_lock; /* used only for partial commits */
+static enum {
+	COMMIT_AS_IS = 1,
+	COMMIT_NORMAL,
+	COMMIT_PARTIAL,
+} commit_style;
 
 static char *logfile, *force_author, *template_file;
 static char *edit_message, *use_message;
@@ -78,41 +85,122 @@ static struct option builtin_commit_options[] = {
 	OPT_END()
 };
 
+static void rollback_index_files(void)
+{
+	switch (commit_style) {
+	case COMMIT_AS_IS:
+		break; /* nothing to do */
+	case COMMIT_NORMAL:
+		rollback_lock_file(&index_lock);
+		break;
+	case COMMIT_PARTIAL:
+		rollback_lock_file(&index_lock);
+		rollback_lock_file(&false_lock);
+		break;
+	}
+}
+
+static void commit_index_files(void)
+{
+	switch (commit_style) {
+	case COMMIT_AS_IS:
+		break; /* nothing to do */
+	case COMMIT_NORMAL:
+		commit_lock_file(&index_lock);
+		break;
+	case COMMIT_PARTIAL:
+		commit_lock_file(&index_lock);
+		rollback_lock_file(&false_lock);
+		break;
+	}
+}
+
 static char *prepare_index(const char **files, const char *prefix)
 {
 	int fd;
 	struct tree *tree;
-	struct lock_file *next_index_lock;
 
 	if (interactive) {
 		interactive_add();
 		return get_index_file();
 	}
 
-	fd = hold_locked_index(&lock_file, 1);
 	if (read_cache() < 0)
 		die("index file corrupt");
 
+	/*
+	 * Non partial, non as-is commit.
+	 *
+	 * (1) get the real index;
+	 * (2) update the_index as necessary;
+	 * (3) write the_index out to the real index (still locked);
+	 * (4) return the name of the locked index file.
+	 *
+	 * The caller should run hooks on the locked real index, and
+	 * (A) if all goes well, commit the real index;
+	 * (B) on failure, rollback the real index.
+	 */
 	if (all || also) {
+		fd = hold_locked_index(&index_lock, 1);
 		add_files_to_cache(verbose, also ? prefix : NULL, files);
 		refresh_cache(REFRESH_QUIET);
 		if (write_cache(fd, active_cache, active_nr) || close(fd))
 			die("unable to write new_index file");
-		return lock_file.filename;
+		commit_style = COMMIT_NORMAL;
+		return index_lock.filename;
 	}
 
+	/*
+	 * As-is commit.
+	 *
+	 * (1) return the name of the real index file.
+	 *
+	 * The caller should run hooks on the real index, and run
+	 * hooks on the real index, and create commit from the_index.
+	 * No lockfile is needed.
+	 */
 	if (*files == NULL) {
-		/* Commit index as-is. */
-		rollback_lock_file(&lock_file);
+		fd = hold_locked_index(&index_lock, 1);
+		refresh_cache(REFRESH_QUIET);
+		if (write_cache(fd, active_cache, active_nr) ||
+		    close(fd) || commit_locked_index(&index_lock))
+			die("unable to write new_index file");
+		commit_style = COMMIT_AS_IS;
 		return get_index_file();
 	}
 
-	/* update the user index file */
+	/*
+	 * A partial commit.
+	 *
+	 * (0) find the set of affected paths [NEEDSWORK: NOT DONE YET]
+	 * (1) get lock on the real index file;
+	 * (2) update the_index with the given paths;
+	 * (3) write the_index out to the real index (still locked);
+	 * (4) get lock on the false index file;
+	 * (5) reset the_index from HEAD, but keep the addition;
+	 * (6) update the_index the same way as (2);
+	 * (7) write the_index out to the false index file;
+	 * (8) return the name of the false index file (still locked);
+	 *
+	 * The caller should run hooks on the locked false index, and
+	 * (A) if all goes well, commit the real index;
+	 * (B) on failure, rollback the real index;
+	 * In either case, rollback the false index.
+	 */
+	commit_style = COMMIT_PARTIAL;
+
+	if (file_exists(git_path("MERGE_HEAD")))
+		die("cannot do a partial commit during a merge.");
+
+	fd = hold_locked_index(&index_lock, 1);
 	add_files_to_cache(verbose, prefix, files);
 	refresh_cache(REFRESH_QUIET);
 	if (write_cache(fd, active_cache, active_nr) || close(fd))
 		die("unable to write new_index file");
 
+	fd = hold_lock_file_for_update(&false_lock,
+				       git_path("next-index-%d", getpid()), 1);
+	discard_cache();
 	if (!initial_commit) {
 		tree = parse_tree_indirect(head_sha1);
 		if (!tree)
@@ -120,17 +208,12 @@ static char *prepare_index(const char **files, const char *prefix)
 		if (read_tree(tree, 0, NULL))
 			die("failed to read HEAD tree object");
 	}
-
-	/* Use a lock file to garbage collect the temporary index file. */
-	next_index_lock = xmalloc(sizeof(*next_index_lock));
-	fd = hold_lock_file_for_update(next_index_lock,
-				       git_path("next-index-%d", getpid()), 1);
 	add_files_to_cache(verbose, prefix, files);
 	refresh_cache(REFRESH_QUIET);
-	if (write_cache(fd, active_cache, active_nr) || close(fd))
-		die("unable to write new_index file");
 
-	return next_index_lock->filename;
+	if (write_cache(fd, active_cache, active_nr) || close(fd))
+		die("unable to write temporary index file");
+	return false_lock.filename;
 }
 
 static int run_status(FILE *fp, const char *index_file, const char *prefix)
@@ -437,7 +520,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)
 
 	commitable = run_status(stdout, index_file, prefix);
 
-	rollback_lock_file(&lock_file);
+	rollback_index_files();
 
 	return commitable ? 0 : 1;
 }
@@ -527,23 +610,36 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 
 	index_file = prepare_index(argv, prefix);
 
-	if (!no_verify && run_hook(index_file, "pre-commit", NULL))
-		exit(1);
+	if (!no_verify && run_hook(index_file, "pre-commit", NULL)) {
+		rollback_index_files();
+		return 1;
+	}
 
 	if (!prepare_log_message(index_file, prefix) && !in_merge) {
 		run_status(stdout, index_file, prefix);
+		rollback_index_files();
 		unlink(commit_editmsg);
 		return 1;
 	}
 
-	strbuf_init(&sb, 0);
-
-	/* Start building up the commit header */
+	/*
+	 * Re-read the index as pre-commit hook could have updated it,
+	 * and write it out as a tree.
+	 */
+	discard_cache();
 	read_cache_from(index_file);
-	active_cache_tree = cache_tree();
+	if (!active_cache_tree)
+		active_cache_tree = cache_tree();
 	if (cache_tree_update(active_cache_tree,
-			      active_cache, active_nr, 0, 0) < 0)
+			      active_cache, active_nr, 0, 0) < 0) {
+		rollback_index_files();
 		die("Error building trees");
+	}
+
+	/*
+	 * The commit object
+	 */
+	strbuf_init(&sb, 0);
 	strbuf_addf(&sb, "tree %s\n",
 		    sha1_to_hex(active_cache_tree->sha1));
 
@@ -592,20 +688,27 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 	header_len = sb.len;
 	if (!no_edit)
 		launch_editor(git_path(commit_editmsg), &sb);
-	else if (strbuf_read_file(&sb, git_path(commit_editmsg), 0) < 0)
+	else if (strbuf_read_file(&sb, git_path(commit_editmsg), 0) < 0) {
+		rollback_index_files();
 		die("could not read commit message\n");
-	if (run_hook(index_file, "commit-msg", commit_editmsg))
+	}
+	if (run_hook(index_file, "commit-msg", commit_editmsg)) {
+		rollback_index_files();
 		exit(1);
+	}
 	stripspace(&sb, 1);
-	if (sb.len < header_len ||
-	    message_is_empty(&sb, header_len))
+	if (sb.len < header_len || message_is_empty(&sb, header_len)) {
+		rollback_index_files();
 		die("* no commit message?  aborting commit.");
+	}
 	strbuf_addch(&sb, '\0');
 	if (is_encoding_utf8(git_commit_encoding) && !is_utf8(sb.buf))
 		fprintf(stderr, commit_utf8_warn);
 
-	if (write_sha1_file(sb.buf, sb.len - 1, commit_type, commit_sha1))
+	if (write_sha1_file(sb.buf, sb.len - 1, commit_type, commit_sha1)) {
+		rollback_index_files();
 		die("failed to write commit object");
+	}
 
 	ref_lock = lock_any_ref_for_update("HEAD",
 					   initial_commit ? NULL : head_sha1,
@@ -620,21 +723,22 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 	strbuf_insert(&sb, 0, reflog_msg, strlen(reflog_msg));
 	strbuf_insert(&sb, strlen(reflog_msg), ": ", 2);
 
-	if (!ref_lock)
+	if (!ref_lock) {
+		rollback_index_files();
 		die("cannot lock HEAD ref");
-	if (write_ref_sha1(ref_lock, commit_sha1, sb.buf) < 0)
+	}
+	if (write_ref_sha1(ref_lock, commit_sha1, sb.buf) < 0) {
+		rollback_index_files();
 		die("cannot update HEAD ref");
+	}
 
 	unlink(git_path("MERGE_HEAD"));
 	unlink(git_path("MERGE_MSG"));
 
-	if (lock_file.filename[0] && commit_locked_index(&lock_file))
-		die("failed to write new index");
+	commit_index_files();
 
 	rerere();
-
-	run_hook(index_file, "post-commit", NULL);
-
+	run_hook(get_index_file(), "post-commit", NULL);
 	if (!quiet)
 		print_summary(prefix, commit_sha1);
 

  reply	other threads:[~2007-11-17  8:46 UTC|newest]

Thread overview: 168+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2007-10-22  6:32 What's cooking in git/spearce.git (topics) Shawn O. Pearce
2007-10-22  6:59 ` Jeff King
2007-10-22  7:16 ` Jeff King
2007-10-23  2:32   ` Linus Torvalds
2007-10-23  3:48     ` Jeff King
2007-10-22  7:24 ` Pierre Habouzit
2007-10-22 15:27 ` Steffen Prohaska
2007-10-23  1:26 ` Junio C Hamano
2007-10-23  3:34   ` Shawn O. Pearce
2007-10-24 12:51 ` What's cooking in git.git (topics) Junio C Hamano
2007-10-24 13:09   ` David Symonds
2007-10-24 16:08   ` Scott Parish
2007-10-24 18:27     ` Andreas Ericsson
2007-10-25  0:35       ` Scott Parish
2007-11-01  5:41   ` Junio C Hamano
2007-11-01 11:02     ` Jakub Narebski
2007-11-01 20:57       ` Junio C Hamano
2007-11-01 18:33     ` Linus Torvalds
2007-11-01 19:19       ` Geert Bosch
2007-11-01 20:27         ` Junio C Hamano
2007-11-01 20:47           ` Mike Hommey
2007-11-01 21:20             ` Junio C Hamano
2007-11-02  0:32               ` Junio C Hamano
2007-11-01 21:44             ` Pierre Habouzit
2007-11-01 21:17           ` Geert Bosch
2007-11-02  0:00             ` Jonas Fonseca
2007-11-01 21:18           ` Theodore Tso
2007-11-01 21:26             ` Melchior FRANZ
2007-11-01 21:32           ` Johan Herland
2007-11-01 21:51             ` Junio C Hamano
2007-11-01 22:05               ` Linus Torvalds
2007-11-01 22:26                 ` Bill Lear
2007-11-01 22:50                 ` Junio C Hamano
2007-11-02  2:19                 ` Petr Baudis
2007-11-01 21:42           ` Pierre Habouzit
2007-11-02  9:39             ` Andreas Ericsson
2007-11-01 21:57       ` Pierre Habouzit
2007-11-02  0:04       ` Jakub Narebski
2007-11-02  2:23         ` Petr Baudis
2007-11-02  7:25           ` Jakub Narebski
2007-11-02  7:28             ` Jakub Narebski
2007-11-02  8:42               ` Pierre Habouzit
2007-11-02  6:06       ` Miles Bader
2007-11-02 15:13         ` Miles Bader
2007-11-02  9:38       ` Andreas Ericsson
2007-11-02 11:03         ` Johannes Schindelin
2007-11-01 21:41     ` Brian Downing
2007-11-01 21:46       ` Pierre Habouzit
2007-11-02 10:26       ` Wincent Colaiuta
2007-11-04  4:14     ` Junio C Hamano
2007-11-04  9:43       ` Jakub Narebski
2007-11-04 11:38       ` Pierre Habouzit
2007-11-08  8:08       ` Junio C Hamano
2007-11-08 20:44         ` Steffen Prohaska
2007-11-12  7:09         ` Junio C Hamano
2007-11-12 12:21           ` Johannes Schindelin
2007-11-12 12:26             ` Pierre Habouzit
2007-11-12 12:33               ` Johannes Schindelin
2007-11-12 13:11                 ` [PATCH] rebase: brown paper bag fix after the detached HEAD patch Johannes Schindelin
2007-11-12 14:53                 ` What's cooking in git.git (topics) Pierre Habouzit
2007-11-12 14:27             ` Steffen Prohaska
2007-11-12 15:02               ` Johannes Schindelin
2007-11-18 16:13                 ` [PATCH 1/2] push: Add '--matching' option and print warning if it should be used Steffen Prohaska
2007-11-18 16:13                   ` [PATCH 2/2] push: Add '--current', which pushes only the current branch Steffen Prohaska
2007-11-19  1:28                     ` Junio C Hamano
2007-11-19  6:41                       ` Steffen Prohaska
2007-11-19  7:27                         ` Junio C Hamano
2007-11-19  7:50                           ` Junio C Hamano
2007-11-19  9:27                             ` Andreas Ericsson
2007-11-19  8:17                           ` Steffen Prohaska
2007-11-19  8:35                             ` Junio C Hamano
2007-11-19  9:54                               ` Steffen Prohaska
2007-11-19 16:51                                 ` [PATCH] push: Add "--current", " Steffen Prohaska
2007-11-19 11:17                               ` [PATCH 2/2] push: Add '--current', " Jakub Narebski
2007-11-19 19:57                                 ` Junio C Hamano
2007-11-19 21:04                                   ` Jakub Narebski
2007-11-19 22:15                                     ` Junio C Hamano
2007-11-19 22:29                                       ` Jakub Narebski
2007-11-19  9:24                         ` Andreas Ericsson
2007-11-12 15:15           ` [PATCH] git-commit: Add tests for invalid usage of -a/--interactive with paths Björn Steinbrink
2007-11-15  0:18           ` What's cooking in git.git (topics) Junio C Hamano
2007-11-15  0:49             ` Johannes Schindelin
2007-11-15 14:49               ` [PATCH] t7501-commit: Add test for git commit <file> with dirty index Kristian Høgsberg
2007-11-15 15:55                 ` Johannes Schindelin
2007-11-15 16:11                 ` [PATCH] builtin-commit: fix "git add x y && git commit y" committing x, too Johannes Schindelin
2007-11-15 16:37                   ` Johannes Schindelin
2007-11-15 17:01                   ` Kristian Høgsberg
2007-11-16  0:43                     ` Johannes Schindelin
2007-11-17  8:45                       ` Junio C Hamano [this message]
2007-11-18  9:18                         ` Junio C Hamano
2007-11-17 12:40             ` What's cooking in git.git (topics) Jeff King
2007-11-17 20:51             ` Junio C Hamano
2007-11-17 23:42               ` Alex Riesen
2007-11-18  1:29                 ` Junio C Hamano
2007-11-21  9:23               ` Junio C Hamano
2007-11-23  8:48                 ` Junio C Hamano
2007-11-23 10:30                   ` Jeff King
2007-11-23 13:23                     ` Johannes Schindelin
2007-11-24 11:38                       ` Jeff King
2007-11-24 15:47                         ` Nicolas Pitre
2007-11-24 19:09                           ` Junio C Hamano
2007-11-25 21:51                             ` J. Bruce Fields
2007-11-25 22:42                               ` Junio C Hamano
2007-11-25 23:08                                 ` J. Bruce Fields
2007-11-26  4:02                               ` Nicolas Pitre
2007-11-26  4:15                                 ` J. Bruce Fields
2007-11-26  4:29                                   ` Nicolas Pitre
2007-11-26  4:45                                     ` J. Bruce Fields
2007-11-26  9:03                                     ` Jakub Narebski
2007-11-26  9:09                                       ` Andreas Ericsson
2007-11-26 19:11                                       ` Nicolas Pitre
2007-11-26 19:24                                         ` David Kastrup
2007-11-26 20:25                                           ` Nicolas Pitre
2007-11-26 20:40                                             ` Junio C Hamano
2007-11-26 20:45                                             ` David Kastrup
2007-11-26 21:09                                               ` Nicolas Pitre
2007-11-26 21:22                                                 ` David Kastrup
2007-11-26 22:02                                                   ` Nicolas Pitre
2007-11-26 23:05                                                     ` David Kastrup
2007-11-26 23:28                                                       ` Nicolas Pitre
2007-11-26 23:52                                                         ` David Kastrup
2007-11-27  4:05                                                           ` Nicolas Pitre
2007-12-05 21:58                                                 ` Miles Bader
2007-11-26 21:14                                             ` Jakub Narebski
2007-11-26 21:36                                               ` Johannes Schindelin
2007-11-26 21:47                                                 ` Nicolas Pitre
2007-11-26  6:15                                   ` Jan Hudec
2007-11-25 20:27                   ` Junio C Hamano
2007-11-25 20:36                     ` Jakub Narebski
2007-11-25 20:53                       ` J. Bruce Fields
2007-12-01  2:37                     ` Junio C Hamano
2007-12-01  8:55                       ` Eric Wong
2007-12-02 14:14                       ` [PATCH, next version] Add 'git fast-export', the sister of 'git fast-import' Johannes Schindelin
2007-12-02 14:40                       ` What's cooking in git.git (topics) Johannes Schindelin
2007-12-04  8:43                       ` Junio C Hamano
2007-12-04  9:40                         ` Johannes Sixt
2007-12-04 10:08                           ` msysGit on FAT32 (was: What's cooking in git.git (topics)) Jakub Narebski
2007-12-04 13:30                             ` Johannes Schindelin
2007-12-04 13:48                               ` msysGit on FAT32 Johannes Sixt
2007-12-04 14:37                                 ` Johannes Schindelin
2007-12-04 20:03                           ` What's cooking in git.git (topics) Steffen Prohaska
2007-12-05 10:59                         ` Junio C Hamano
2007-12-05 11:08                           ` Jakub Narebski
2007-12-05 11:10                           ` Jakub Narebski
2007-12-06  4:43                             ` Jeff King
2007-12-05 11:37                           ` [PATCH] Soft aliases: add "less" and minimal documentation Johannes Schindelin
2007-12-05 19:45                             ` Junio C Hamano
2007-12-06  4:50                               ` Jeff King
2007-12-06  4:32                           ` What's cooking in git.git (topics) Jeff King
2007-12-07  9:51                           ` Junio C Hamano
2007-12-07 11:11                             ` Jakub Narebski
2007-12-07 19:29                               ` Junio C Hamano
2007-12-07 21:36                             ` Miklos Vajna
2007-12-09 10:27                             ` Junio C Hamano
2007-12-13  2:48                               ` Junio C Hamano
2007-12-13  3:22                                 ` Nicolas Pitre
2007-12-13 22:31                                   ` [PATCH 1/2] xdl_diff: identify call sites Junio C Hamano
2007-12-14  7:03                                     ` Junio C Hamano
2007-12-13 22:31                                   ` [PATCH 2/2] xdi_diff: trim common trailing lines Junio C Hamano
2007-12-14  9:06                                     ` Peter Baumann
2007-12-14 19:15                                       ` Junio C Hamano
2007-12-17  8:40                                 ` What's cooking in git.git (topics) Junio C Hamano
2007-12-23  9:20                                   ` Junio C Hamano
2007-12-31 10:47                                     ` checkout --push/--pop idea (Re: What's cooking in git.git (topics)) Jan Hudec
2008-01-05 11:01                                     ` What's cooking in git.git (topics) Junio C Hamano
2008-01-05 16:04                                       ` Johannes Schindelin
2008-01-22  8:47                                       ` What will be cooking in git.git post 1.5.4 (topics) Junio C Hamano
2007-12-04 16:18                       ` What's cooking in git.git (topics) Brian Downing

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=7vk5ohuunv.fsf@gitster.siamese.dyndns.org \
    --to=gitster@pobox.com \
    --cc=Johannes.Schindelin@gmx.de \
    --cc=git@vger.kernel.org \
    --cc=krh@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.