Commits

Anonymous committed 125a5f1 Merge

Merge branch 'maint'

* maint:
Small correction in reading of commit headers
Documentation: fix typo in git-remote.txt
Add test for blame corner cases.
blame: -C -C -C
blame: Notice a wholesale incorporation of an existing file.
Fix --boundary output
diff format documentation: describe raw combined diff format
Mention version 1.5.1 in tutorial and user-manual
Add --no-rebase option to git-svn dcommit
Fix markup in git-svn man page

  • Participants
  • Parent commits cc0e6c5, e102d43

Comments (0)

Files changed (10)

Documentation/diff-format.txt

 in pathnames are represented as `\t`, `\n`, and `\\`,
 respectively.
 
+diff format for merges
+----------------------
+
+"git-diff-tree" and "git-diff-files" can take '-c' or '--cc' option
+to generate diff output also for merge commits.  The output differs
+from the format described above in the following way:
+
+. there is a colon for each parent
+. there are more "src" modes and "src" sha1
+. status is concatenated status characters for each parent
+. no optional "score" number
+. single path, only for "dst"
+
+Example:
+
+------------------------------------------------
+::100644 100644 100644 fabadb8... cc95eb0... 4866510... MM	describe.c
+------------------------------------------------
+
+Note that 'combined diff' lists only files which were modified from
+all parents.
+
 
 Generating patches with -p
 --------------------------

Documentation/git-remote.txt

 refspec for the remote to track all branches under
 `$GIT_DIR/remotes/<name>/`, a refspec to track only `<branch>`
 is created.  You can give more than one `-t <branch>` to track
-multiple branche without grabbing all branches.
+multiple branches without grabbing all branches.
 +
 With `-m <master>` option, `$GIT_DIR/remotes/<name>/HEAD` is set
 up to point at remote's `<master>` branch instead of whatever

Documentation/git-svn.txt

 	argument.  Normally this command initializes the current
 	directory.
 
--T<trunk_subdir>::
---trunk=<trunk_subdir>::
--t<tags_subdir>::
---tags=<tags_subdir>::
--b<branches_subdir>::
---branches=<branches_subdir>::
+-T<trunk_subdir>;;
+--trunk=<trunk_subdir>;;
+-t<tags_subdir>;;
+--tags=<tags_subdir>;;
+-b<branches_subdir>;;
+--branches=<branches_subdir>;;
 	These are optional command-line options for init.  Each of
 	these flags can point to a relative repository path
 	(--tags=project/tags') or a full url
 	(--tags=https://foo.org/project/tags)
-
---no-metadata::
+--no-metadata;;
 	Set the 'noMetadata' option in the [svn-remote] config.
---use-svm-props::
+--use-svm-props;;
 	Set the 'useSvmProps' option in the [svn-remote] config.
---use-svnsync-props::
+--use-svnsync-props;;
 	Set the 'useSvnsyncProps' option in the [svn-remote] config.
---rewrite-root=<URL>::
+--rewrite-root=<URL>;;
 	Set the 'rewriteRoot' option in the [svn-remote] config.
---username=<USER>::
+--username=<USER>;;
 	For transports that SVN handles authentication for (http,
 	https, and plain svn), specify the username.  For other
 	transports (eg svn+ssh://), you must include the username in
 	the URL, eg svn+ssh://foo@svn.bar.com/project
-
---prefix=<prefix>::
+--prefix=<prefix>;;
 	This allows one to specify a prefix which is prepended
 	to the names of remotes if trunk/branches/tags are
 	specified.  The prefix does not automatically include a
 	repository.
 
 'fetch'::
-
 	Fetch unfetched revisions from the Subversion remote we are
 	tracking.  The name of the [svn-remote "..."] section in the
 	.git/config file may be specified as an optional command-line
 
 Like 'git-rebase'; this requires that the working tree be clean
 and have no uncommitted changes.
-+
---
+
 -l;;
 --local;;
 	Do not fetch remotely; only run 'git-rebase' against the
 	last fetched commit from the upstream SVN.
---
-+
 
 'dcommit'::
 	Commit each diff from a specified head directly to the SVN
 	alternative to HEAD.
 	This is advantageous over 'set-tree' (below) because it produces
 	cleaner, more linear history.
++
+--no-rebase;;
+	After committing, do not rebase or reset.
 --
 
 'log'::

Documentation/tutorial.txt

-A tutorial introduction to git
-==============================
+A tutorial introduction to git (for version 1.5.1 or newer)
+===========================================================
 
 This tutorial explains how to import a new project into git, make
 changes to it, and share changes with other developers.

Documentation/user-manual.txt

-Git User's Manual
-_________________
+Git User's Manual (for version 1.5.1 or newer)
+______________________________________________
 
 This manual is designed to be readable by someone with basic unix
 command-line skills, but no previous knowledge of git.
 #define PICKAXE_BLAME_MOVE		01
 #define PICKAXE_BLAME_COPY		02
 #define PICKAXE_BLAME_COPY_HARDER	04
+#define PICKAXE_BLAME_COPY_HARDEST	010
 
 /*
  * blame for a blame_entry with score lower than these thresholds
 }
 
 /*
+ * We are looking at a part of the final image represented by
+ * ent (tlno and same are offset by ent->s_lno).
+ * tlno is where we are looking at in the final image.
+ * up to (but not including) same match preimage.
+ * plno is where we are looking at in the preimage.
+ *
+ * <-------------- final image ---------------------->
+ *       <------ent------>
+ *         ^tlno ^same
+ *    <---------preimage----->
+ *         ^plno
+ *
+ * All line numbers are 0-based.
+ */
+static void handle_split(struct scoreboard *sb,
+			 struct blame_entry *ent,
+			 int tlno, int plno, int same,
+			 struct origin *parent,
+			 struct blame_entry *split)
+{
+	if (ent->num_lines <= tlno)
+		return;
+	if (tlno < same) {
+		struct blame_entry this[3];
+		tlno += ent->s_lno;
+		same += ent->s_lno;
+		split_overlap(this, ent, tlno, plno, same, parent);
+		copy_split_if_better(sb, split, this);
+		decref_split(this);
+	}
+}
+
+/*
  * Find the lines from parent that are the same as ent so that
  * we can pass blames to it.  file_p has the blob contents for
  * the parent.
 
 	patch = compare_buffer(file_p, &file_o, 1);
 
+	/*
+	 * file_o is a part of final image we are annotating.
+	 * file_p partially may match that image.
+	 */
 	memset(split, 0, sizeof(struct blame_entry [3]));
 	plno = tlno = 0;
 	for (i = 0; i < patch->num; i++) {
 		struct chunk *chunk = &patch->chunks[i];
 
-		/* tlno to chunk->same are the same as ent */
-		if (ent->num_lines <= tlno)
-			break;
-		if (tlno < chunk->same) {
-			struct blame_entry this[3];
-			split_overlap(this, ent,
-				      tlno + ent->s_lno, plno,
-				      chunk->same + ent->s_lno,
-				      parent);
-			copy_split_if_better(sb, split, this);
-			decref_split(this);
-		}
+		handle_split(sb, ent, tlno, plno, chunk->same, parent, split);
 		plno = chunk->p_next;
 		tlno = chunk->t_next;
 	}
+	/* remainder, if any, all match the preimage */
+	handle_split(sb, ent, tlno, plno, ent->num_lines, parent, split);
 	free_patch(patch);
 }
 
 	 * and this code needs to be after diff_setup_done(), which
 	 * usually makes find-copies-harder imply copy detection.
 	 */
-	if ((opt & PICKAXE_BLAME_COPY_HARDER) &&
-	    (!porigin || strcmp(target->path, porigin->path)))
+	if ((opt & PICKAXE_BLAME_COPY_HARDEST)
+	    || ((opt & PICKAXE_BLAME_COPY_HARDER)
+		&& (!porigin || strcmp(target->path, porigin->path))))
 		diff_opts.find_copies_harder = 1;
 
 	if (is_null_sha1(target->commit->object.sha1))
 			blame_move_score = parse_score(arg+2);
 		}
 		else if (!prefixcmp(arg, "-C")) {
+			/*
+			 * -C enables copy from removed files;
+			 * -C -C enables copy from existing files, but only
+			 *       when blaming a new file;
+			 * -C -C -C enables copy from existing files for
+			 *          everybody
+			 */
+			if (opt & PICKAXE_BLAME_COPY_HARDER)
+				opt |= PICKAXE_BLAME_COPY_HARDEST;
 			if (opt & PICKAXE_BLAME_COPY)
 				opt |= PICKAXE_BLAME_COPY_HARDER;
 			opt |= PICKAXE_BLAME_COPY | PICKAXE_BLAME_MOVE;
 			next = NULL;
 		} else
 			next = eol + 1;
-		if (!strncmp(line, key, key_len) && line[key_len] == ' ') {
+		if (eol - line > key_len &&
+		    !strncmp(line, key, key_len) &&
+		    line[key_len] == ' ') {
 			int len = eol - line - key_len;
 			char *ret = xmalloc(len);
 			memcpy(ret, line + key_len + 1, len - 1);
 my ($_stdin, $_help, $_edit,
 	$_message, $_file,
 	$_template, $_shared,
-	$_version, $_fetch_all,
+	$_version, $_fetch_all, $_no_rebase,
 	$_merge, $_strategy, $_dry_run, $_local,
 	$_prefix, $_no_checkout, $_verbose);
 $Git::SVN::_follow_parent = 1;
 			  'verbose|v' => \$_verbose,
 			  'dry-run|n' => \$_dry_run,
 			  'fetch-all|all' => \$_fetch_all,
+			  'no-rebase' => \$_no_rebase,
 			%cmt_opts, %fc_opts } ],
 	'set-tree' => [ \&cmd_set_tree,
 	                "Set an SVN repository to a git tree-ish",
 		return;
 	}
 	$_fetch_all ? $gs->fetch_all : $gs->fetch;
-	# we always want to rebase against the current HEAD, not any
-	# head that was passed to us
-	my @diff = command('diff-tree', 'HEAD', $gs->refname, '--');
-	my @finish;
-	if (@diff) {
-		@finish = rebase_cmd();
-		print STDERR "W: HEAD and ", $gs->refname, " differ, ",
-		             "using @finish:\n", "@diff";
-	} else {
-		print "No changes between current HEAD and ",
-		      $gs->refname, "\nResetting to the latest ",
-		      $gs->refname, "\n";
-		@finish = qw/reset --mixed/;
+	unless ($_no_rebase) {
+		# we always want to rebase against the current HEAD, not any
+		# head that was passed to us
+		my @diff = command('diff-tree', 'HEAD', $gs->refname, '--');
+		my @finish;
+		if (@diff) {
+			@finish = rebase_cmd();
+			print STDERR "W: HEAD and ", $gs->refname, " differ, ",
+				     "using @finish:\n", "@diff";
+		} else {
+			print "No changes between current HEAD and ",
+			      $gs->refname, "\nResetting to the latest ",
+			      $gs->refname, "\n";
+			@finish = qw/reset --mixed/;
+		}
+		command_noisy(@finish, $gs->refname);
 	}
-	command_noisy(@finish, $gs->refname);
 }
 
 sub cmd_find_rev {
 		      stdout);
 		if (opt->commit_format != CMIT_FMT_ONELINE)
 			fputs("commit ", stdout);
-		if (opt->left_right) {
-			if (commit->object.flags & BOUNDARY)
-				putchar('-');
-			else if (commit->object.flags & SYMMETRIC_LEFT)
+		if (commit->object.flags & BOUNDARY)
+			putchar('-');
+		else if (opt->left_right) {
+			if (commit->object.flags & SYMMETRIC_LEFT)
 				putchar('<');
 			else
 				putchar('>');
+#!/bin/sh
+
+test_description='git blame corner cases'
+. ./test-lib.sh
+
+pick_fc='s/^[0-9a-f^]* *\([^ ]*\) *(\([^ ]*\) .*/\1-\2/'
+
+test_expect_success setup '
+
+	echo A A A A A >one &&
+	echo B B B B B >two &&
+	echo C C C C C >tres &&
+	echo ABC >mouse &&
+	git add one two tres mouse &&
+	test_tick &&
+	GIT_AUTHOR_NAME=Initial git commit -m Initial &&
+
+	cat one >uno &&
+	mv two dos &&
+	cat one >>tres &&
+	echo DEF >>mouse
+	git add uno dos tres mouse &&
+	test_tick &&
+	GIT_AUTHOR_NAME=Second git commit -a -m Second &&
+
+	echo GHIJK >>mouse &&
+	git add mouse &&
+	test_tick &&
+	GIT_AUTHOR_NAME=Third git commit -m Third &&
+
+	cat mouse >cow &&
+	git add cow &&
+	test_tick &&
+	GIT_AUTHOR_NAME=Fourth git commit -m Fourth &&
+
+	{
+		echo ABC
+		echo DEF
+		echo XXXX
+		echo GHIJK
+	} >cow &&
+	git add cow &&
+	test_tick &&
+	GIT_AUTHOR_NAME=Fifth git commit -m Fifth
+'
+
+test_expect_success 'straight copy without -C' '
+
+	git blame uno | grep Second
+
+'
+
+test_expect_success 'straight move without -C' '
+
+	git blame dos | grep Initial
+
+'
+
+test_expect_success 'straight copy with -C' '
+
+	git blame -C1 uno | grep Second
+
+'
+
+test_expect_success 'straight move with -C' '
+
+	git blame -C1 dos | grep Initial
+
+'
+
+test_expect_success 'straight copy with -C -C' '
+
+	git blame -C -C1 uno | grep Initial
+
+'
+
+test_expect_success 'straight move with -C -C' '
+
+	git blame -C -C1 dos | grep Initial
+
+'
+
+test_expect_success 'append without -C' '
+
+	git blame -L2 tres | grep Second
+
+'
+
+test_expect_success 'append with -C' '
+
+	git blame -L2 -C1 tres | grep Second
+
+'
+
+test_expect_success 'append with -C -C' '
+
+	git blame -L2 -C -C1 tres | grep Second
+
+'
+
+test_expect_success 'append with -C -C -C' '
+
+	git blame -L2 -C -C -C1 tres | grep Initial
+
+'
+
+test_expect_success 'blame wholesale copy' '
+
+	git blame -f -C -C1 HEAD^ -- cow | sed -e "$pick_fc" >current &&
+	{
+		echo mouse-Initial
+		echo mouse-Second
+		echo mouse-Third
+	} >expected &&
+	diff -u expected current
+
+'
+
+test_expect_success 'blame wholesale copy and more' '
+
+	git blame -f -C -C1 HEAD -- cow | sed -e "$pick_fc" >current &&
+	{
+		echo mouse-Initial
+		echo mouse-Second
+		echo cow-Fifth
+		echo mouse-Third
+	} >expected &&
+	diff -u expected current
+
+'
+
+test_done