Revision 130fcca63fe8e7e087e7419907e018cbbaf434a3 authored by Junio C Hamano on 05 February 2006, 08:07:44 UTC, committed by Junio C Hamano on 07 February 2006, 07:20:32 UTC
 - "git commit" without _any_ parameter keeps the traditional
   behaviour.  It commits the current index.

   We commit the whole index even when this form is run from a
   subdirectory.

 - "git commit --include paths..." (or "git commit -i paths...")
   is equivalent to:

   	git update-index --remove paths...
        git commit

 - "git commit paths..." acquires a new semantics.  This is an
   incompatible change that needs user training, which I am
   still a bit reluctant to swallow, but enough people seem to
   have complained that it is confusing to them.  It

   1. refuses to run if $GIT_DIR/MERGE_HEAD exists, and reminds
      trained git users that the traditional semantics now needs
      -i flag.

   2. refuses to run if named paths... are different in HEAD and
      the index (ditto about reminding).  Added paths are OK.

   3. reads HEAD commit into a temporary index file.

   4. updates named paths... from the working tree in this
      temporary index.

   5. does the same updates of the paths... from the working
      tree to the real index.

   6. makes a commit using the temporary index that has the
      current HEAD as the parent, and updates the HEAD with this
      new commit.

 - "git commit --all" can run from a subdirectory, but it updates
   the index with all the modified files and does a whole tree
   commit.

 - In all cases, when the command decides not to create a new
   commit, the index is left as it was before the command is
   run.  This means that the two "git diff" in the following
   sequence:

       $ git diff
       $ git commit -a
       $ git diff

   would show the same diff if you abort the commit process by
   making the commit log message empty.

This commit also introduces much requested --author option.

	$ git commit --author 'A U Thor <author@example.com>'

Signed-off-by: Junio C Hamano <junkio@cox.net>
1 parent 8389b52
Raw File
mktag.c
#include "cache.h"

/*
 * A signature file has a very simple fixed format: three lines
 * of "object <sha1>" + "type <typename>" + "tag <tagname>",
 * followed by some free-form signature that git itself doesn't
 * care about, but that can be verified with gpg or similar.
 *
 * The first three lines are guaranteed to be at least 63 bytes:
 * "object <sha1>\n" is 48 bytes, "type tag\n" at 9 bytes is the
 * shortest possible type-line, and "tag .\n" at 6 bytes is the
 * shortest single-character-tag line. 
 *
 * We also artificially limit the size of the full object to 8kB.
 * Just because I'm a lazy bastard, and if you can't fit a signature
 * in that size, you're doing something wrong.
 */

// Some random size
#define MAXSIZE (8192)

/*
 * We refuse to tag something we can't verify. Just because.
 */
static int verify_object(unsigned char *sha1, const char *expected_type)
{
	int ret = -1;
	char type[100];
	unsigned long size;
	void *buffer = read_sha1_file(sha1, type, &size);

	if (buffer) {
		if (!strcmp(type, expected_type))
			ret = check_sha1_signature(sha1, buffer, size, type);
		free(buffer);
	}
	return ret;
}

static int verify_tag(char *buffer, unsigned long size)
{
	int typelen;
	char type[20];
	unsigned char sha1[20];
	const char *object, *type_line, *tag_line, *tagger_line;

	if (size < 64 || size > MAXSIZE-1)
		return -1;
	buffer[size] = 0;

	/* Verify object line */
	object = buffer;
	if (memcmp(object, "object ", 7))
		return -1;
	if (get_sha1_hex(object + 7, sha1))
		return -1;

	/* Verify type line */
	type_line = object + 48;
	if (memcmp(type_line - 1, "\ntype ", 6))
		return -1;

	/* Verify tag-line */
	tag_line = strchr(type_line, '\n');
	if (!tag_line)
		return -1;
	tag_line++;
	if (memcmp(tag_line, "tag ", 4) || tag_line[4] == '\n')
		return -1;

	/* Get the actual type */
	typelen = tag_line - type_line - strlen("type \n");
	if (typelen >= sizeof(type))
		return -1;
	memcpy(type, type_line+5, typelen);
	type[typelen] = 0;

	/* Verify that the object matches */
	if (get_sha1_hex(object + 7, sha1))
		return -1;
	if (verify_object(sha1, type))
		return -1;

	/* Verify the tag-name: we don't allow control characters or spaces in it */
	tag_line += 4;
	for (;;) {
		unsigned char c = *tag_line++;
		if (c == '\n')
			break;
		if (c > ' ')
			continue;
		return -1;
	}

	/* Verify the tagger line */
	tagger_line = tag_line;

	if (memcmp(tagger_line, "tagger", 6) || (tagger_line[6] == '\n'))
		return -1;

	/* The actual stuff afterwards we don't care about.. */
	return 0;
}

int main(int argc, char **argv)
{
	unsigned long size;
	char buffer[MAXSIZE];
	unsigned char result_sha1[20];

	if (argc != 1)
		usage("cat <signaturefile> | git-mktag");

	setup_git_directory();

	// Read the signature
	size = 0;
	for (;;) {
		int ret = xread(0, buffer + size, MAXSIZE - size);
		if (ret <= 0)
			break;
		size += ret;
	}

	// Verify it for some basic sanity: it needs to start with "object <sha1>\ntype\ntagger "
	if (verify_tag(buffer, size) < 0)
		die("invalid tag signature file");

	if (write_sha1_file(buffer, size, "tag", result_sha1) < 0)
		die("unable to write tag file");
	printf("%s\n", sha1_to_hex(result_sha1));
	return 0;
}
back to top