Merge branch 'pt/am-builtin'

Rewrite "am" in "C".

* pt/am-builtin: (46 commits)
  git-am: add am.threeWay config variable
  builtin-am: remove redirection to git-am.sh
  builtin-am: check for valid committer ident
  builtin-am: implement legacy -b/--binary option
  builtin-am: implement -i/--interactive
  builtin-am: support and auto-detect mercurial patches
  builtin-am: support and auto-detect StGit series files
  builtin-am: support and auto-detect StGit patches
  builtin-am: rerere support
  builtin-am: invoke post-applypatch hook
  builtin-am: invoke pre-applypatch hook
  builtin-am: invoke applypatch-msg hook
  builtin-am: support automatic notes copying
  builtin-am: invoke post-rewrite hook
  builtin-am: implement -S/--gpg-sign, commit.gpgsign
  builtin-am: implement --committer-date-is-author-date
  builtin-am: implement --ignore-date
  builtin-am: pass git-apply's options to git-apply
  builtin-am: implement --[no-]scissors
  builtin-am: support --keep-cr, am.keepcr
  ...
This commit is contained in:
Junio C Hamano 2015-08-12 14:09:55 -07:00
commit 7aa2da6162
12 changed files with 2432 additions and 15 deletions

View file

@ -769,6 +769,14 @@ am.keepcr::
by giving '--no-keep-cr' from the command line. by giving '--no-keep-cr' from the command line.
See linkgit:git-am[1], linkgit:git-mailsplit[1]. See linkgit:git-am[1], linkgit:git-mailsplit[1].
am.threeWay::
By default, `git am` will fail if the patch does not apply cleanly. When
set to true, this setting tells `git am` to fall back on 3-way merge if
the patch records the identity of blobs it is supposed to apply to and
we have those blobs available locally (equivalent to giving the `--3way`
option from the command line). Defaults to `false`.
See linkgit:git-am[1].
apply.ignoreWhitespace:: apply.ignoreWhitespace::
When set to 'change', tells 'git apply' to ignore changes in When set to 'change', tells 'git apply' to ignore changes in
whitespace, in the same way as the '--ignore-space-change' whitespace, in the same way as the '--ignore-space-change'

View file

@ -10,7 +10,7 @@ SYNOPSIS
-------- --------
[verse] [verse]
'git am' [--signoff] [--keep] [--[no-]keep-cr] [--[no-]utf8] 'git am' [--signoff] [--keep] [--[no-]keep-cr] [--[no-]utf8]
[--3way] [--interactive] [--committer-date-is-author-date] [--[no-]3way] [--interactive] [--committer-date-is-author-date]
[--ignore-date] [--ignore-space-change | --ignore-whitespace] [--ignore-date] [--ignore-space-change | --ignore-whitespace]
[--whitespace=<option>] [-C<n>] [-p<n>] [--directory=<dir>] [--whitespace=<option>] [-C<n>] [-p<n>] [--directory=<dir>]
[--exclude=<path>] [--include=<path>] [--reject] [-q | --quiet] [--exclude=<path>] [--include=<path>] [--reject] [-q | --quiet]
@ -90,10 +90,13 @@ default. You can use `--no-utf8` to override this.
-3:: -3::
--3way:: --3way::
--no-3way::
When the patch does not apply cleanly, fall back on When the patch does not apply cleanly, fall back on
3-way merge if the patch records the identity of blobs 3-way merge if the patch records the identity of blobs
it is supposed to apply to and we have those blobs it is supposed to apply to and we have those blobs
available locally. available locally. `--no-3way` can be used to override
am.threeWay configuration variable. For more information,
see am.threeWay in linkgit:git-config[1].
--ignore-space-change:: --ignore-space-change::
--ignore-whitespace:: --ignore-whitespace::

View file

@ -467,7 +467,6 @@ TEST_PROGRAMS_NEED_X =
# interactive shell sessions without exporting it. # interactive shell sessions without exporting it.
unexport CDPATH unexport CDPATH
SCRIPT_SH += git-am.sh
SCRIPT_SH += git-bisect.sh SCRIPT_SH += git-bisect.sh
SCRIPT_SH += git-difftool--helper.sh SCRIPT_SH += git-difftool--helper.sh
SCRIPT_SH += git-filter-branch.sh SCRIPT_SH += git-filter-branch.sh
@ -813,6 +812,7 @@ LIB_OBJS += xdiff-interface.o
LIB_OBJS += zlib.o LIB_OBJS += zlib.o
BUILTIN_OBJS += builtin/add.o BUILTIN_OBJS += builtin/add.o
BUILTIN_OBJS += builtin/am.o
BUILTIN_OBJS += builtin/annotate.o BUILTIN_OBJS += builtin/annotate.o
BUILTIN_OBJS += builtin/apply.o BUILTIN_OBJS += builtin/apply.o
BUILTIN_OBJS += builtin/archive.o BUILTIN_OBJS += builtin/archive.o

View file

@ -30,6 +30,7 @@ extern int textconv_object(const char *path, unsigned mode, const unsigned char
extern int is_builtin(const char *s); extern int is_builtin(const char *s);
extern int cmd_add(int argc, const char **argv, const char *prefix); extern int cmd_add(int argc, const char **argv, const char *prefix);
extern int cmd_am(int argc, const char **argv, const char *prefix);
extern int cmd_annotate(int argc, const char **argv, const char *prefix); extern int cmd_annotate(int argc, const char **argv, const char *prefix);
extern int cmd_apply(int argc, const char **argv, const char *prefix); extern int cmd_apply(int argc, const char **argv, const char *prefix);
extern int cmd_archive(int argc, const char **argv, const char *prefix); extern int cmd_archive(int argc, const char **argv, const char *prefix);

2321
builtin/am.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -592,7 +592,7 @@ static struct cache_tree *cache_tree_find(struct cache_tree *it, const char *pat
return it; return it;
} }
int write_cache_as_tree(unsigned char *sha1, int flags, const char *prefix) int write_index_as_tree(unsigned char *sha1, struct index_state *index_state, const char *index_path, int flags, const char *prefix)
{ {
int entries, was_valid, newfd; int entries, was_valid, newfd;
struct lock_file *lock_file; struct lock_file *lock_file;
@ -603,23 +603,23 @@ int write_cache_as_tree(unsigned char *sha1, int flags, const char *prefix)
*/ */
lock_file = xcalloc(1, sizeof(struct lock_file)); lock_file = xcalloc(1, sizeof(struct lock_file));
newfd = hold_locked_index(lock_file, 1); newfd = hold_lock_file_for_update(lock_file, index_path, LOCK_DIE_ON_ERROR);
entries = read_cache(); entries = read_index_from(index_state, index_path);
if (entries < 0) if (entries < 0)
return WRITE_TREE_UNREADABLE_INDEX; return WRITE_TREE_UNREADABLE_INDEX;
if (flags & WRITE_TREE_IGNORE_CACHE_TREE) if (flags & WRITE_TREE_IGNORE_CACHE_TREE)
cache_tree_free(&(active_cache_tree)); cache_tree_free(&index_state->cache_tree);
if (!active_cache_tree) if (!index_state->cache_tree)
active_cache_tree = cache_tree(); index_state->cache_tree = cache_tree();
was_valid = cache_tree_fully_valid(active_cache_tree); was_valid = cache_tree_fully_valid(index_state->cache_tree);
if (!was_valid) { if (!was_valid) {
if (cache_tree_update(&the_index, flags) < 0) if (cache_tree_update(index_state, flags) < 0)
return WRITE_TREE_UNMERGED_INDEX; return WRITE_TREE_UNMERGED_INDEX;
if (0 <= newfd) { if (0 <= newfd) {
if (!write_locked_index(&the_index, lock_file, COMMIT_LOCK)) if (!write_locked_index(index_state, lock_file, COMMIT_LOCK))
newfd = -1; newfd = -1;
} }
/* Not being able to write is fine -- we are only interested /* Not being able to write is fine -- we are only interested
@ -631,14 +631,14 @@ int write_cache_as_tree(unsigned char *sha1, int flags, const char *prefix)
} }
if (prefix) { if (prefix) {
struct cache_tree *subtree = struct cache_tree *subtree;
cache_tree_find(active_cache_tree, prefix); subtree = cache_tree_find(index_state->cache_tree, prefix);
if (!subtree) if (!subtree)
return WRITE_TREE_PREFIX_ERROR; return WRITE_TREE_PREFIX_ERROR;
hashcpy(sha1, subtree->sha1); hashcpy(sha1, subtree->sha1);
} }
else else
hashcpy(sha1, active_cache_tree->sha1); hashcpy(sha1, index_state->cache_tree->sha1);
if (0 <= newfd) if (0 <= newfd)
rollback_lock_file(lock_file); rollback_lock_file(lock_file);
@ -646,6 +646,11 @@ int write_cache_as_tree(unsigned char *sha1, int flags, const char *prefix)
return 0; return 0;
} }
int write_cache_as_tree(unsigned char *sha1, int flags, const char *prefix)
{
return write_index_as_tree(sha1, &the_index, get_index_file(), flags, prefix);
}
static void prime_cache_tree_rec(struct cache_tree *it, struct tree *tree) static void prime_cache_tree_rec(struct cache_tree *it, struct tree *tree)
{ {
struct tree_desc desc; struct tree_desc desc;

View file

@ -46,6 +46,7 @@ int update_main_cache_tree(int);
#define WRITE_TREE_UNMERGED_INDEX (-2) #define WRITE_TREE_UNMERGED_INDEX (-2)
#define WRITE_TREE_PREFIX_ERROR (-3) #define WRITE_TREE_PREFIX_ERROR (-3)
int write_index_as_tree(unsigned char *sha1, struct index_state *index_state, const char *index_path, int flags, const char *prefix);
int write_cache_as_tree(unsigned char *sha1, int flags, const char *prefix); int write_cache_as_tree(unsigned char *sha1, int flags, const char *prefix);
void prime_cache_tree(struct index_state *, struct tree *); void prime_cache_tree(struct index_state *, struct tree *);

View file

@ -717,10 +717,12 @@ extern void *xrealloc(void *ptr, size_t size);
extern void *xcalloc(size_t nmemb, size_t size); extern void *xcalloc(size_t nmemb, size_t size);
extern void *xmmap(void *start, size_t length, int prot, int flags, int fd, off_t offset); extern void *xmmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
extern void *xmmap_gently(void *start, size_t length, int prot, int flags, int fd, off_t offset); extern void *xmmap_gently(void *start, size_t length, int prot, int flags, int fd, off_t offset);
extern int xopen(const char *path, int flags, ...);
extern ssize_t xread(int fd, void *buf, size_t len); extern ssize_t xread(int fd, void *buf, size_t len);
extern ssize_t xwrite(int fd, const void *buf, size_t len); extern ssize_t xwrite(int fd, const void *buf, size_t len);
extern ssize_t xpread(int fd, void *buf, size_t len, off_t offset); extern ssize_t xpread(int fd, void *buf, size_t len, off_t offset);
extern int xdup(int fd); extern int xdup(int fd);
extern FILE *xfopen(const char *path, const char *mode);
extern FILE *xfdopen(int fd, const char *mode); extern FILE *xfdopen(int fd, const char *mode);
extern int xmkstemp(char *template); extern int xmkstemp(char *template);
extern int xmkstemp_mode(char *template, int mode); extern int xmkstemp_mode(char *template, int mode);

1
git.c
View file

@ -370,6 +370,7 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
static struct cmd_struct commands[] = { static struct cmd_struct commands[] = {
{ "add", cmd_add, RUN_SETUP | NEED_WORK_TREE }, { "add", cmd_add, RUN_SETUP | NEED_WORK_TREE },
{ "am", cmd_am, RUN_SETUP | NEED_WORK_TREE },
{ "annotate", cmd_annotate, RUN_SETUP }, { "annotate", cmd_annotate, RUN_SETUP },
{ "apply", cmd_apply, RUN_SETUP_GENTLY }, { "apply", cmd_apply, RUN_SETUP_GENTLY },
{ "archive", cmd_archive }, { "archive", cmd_archive },

View file

@ -551,6 +551,25 @@ test_expect_success 'am -3 -p0 can read --no-prefix patch' '
git diff --exit-code lorem git diff --exit-code lorem
' '
test_expect_success 'am with config am.threeWay falls back to 3-way merge' '
rm -fr .git/rebase-apply &&
git reset --hard &&
git checkout -b lorem4 base3way &&
test_config am.threeWay 1 &&
git am lorem-move.patch &&
test_path_is_missing .git/rebase-apply &&
git diff --exit-code lorem
'
test_expect_success 'am with config am.threeWay overridden by --no-3way' '
rm -fr .git/rebase-apply &&
git reset --hard &&
git checkout -b lorem5 base3way &&
test_config am.threeWay 1 &&
test_must_fail git am --no-3way lorem-move.patch &&
test_path_is_dir .git/rebase-apply
'
test_expect_success 'am can rename a file' ' test_expect_success 'am can rename a file' '
grep "^rename from" rename.patch && grep "^rename from" rename.patch &&
rm -fr .git/rebase-apply && rm -fr .git/rebase-apply &&

View file

@ -189,6 +189,41 @@ void *xcalloc(size_t nmemb, size_t size)
# endif # endif
#endif #endif
/**
* xopen() is the same as open(), but it die()s if the open() fails.
*/
int xopen(const char *path, int oflag, ...)
{
mode_t mode = 0;
va_list ap;
/*
* va_arg() will have undefined behavior if the specified type is not
* compatible with the argument type. Since integers are promoted to
* ints, we fetch the next argument as an int, and then cast it to a
* mode_t to avoid undefined behavior.
*/
va_start(ap, oflag);
if (oflag & O_CREAT)
mode = va_arg(ap, int);
va_end(ap);
for (;;) {
int fd = open(path, oflag, mode);
if (fd >= 0)
return fd;
if (errno == EINTR)
continue;
if ((oflag & O_RDWR) == O_RDWR)
die_errno(_("could not open '%s' for reading and writing"), path);
else if ((oflag & O_WRONLY) == O_WRONLY)
die_errno(_("could not open '%s' for writing"), path);
else
die_errno(_("could not open '%s' for reading"), path);
}
}
/* /*
* xread() is the same a read(), but it automatically restarts read() * xread() is the same a read(), but it automatically restarts read()
* operations with a recoverable error (EAGAIN and EINTR). xread() * operations with a recoverable error (EAGAIN and EINTR). xread()
@ -311,6 +346,27 @@ int xdup(int fd)
return ret; return ret;
} }
/**
* xfopen() is the same as fopen(), but it die()s if the fopen() fails.
*/
FILE *xfopen(const char *path, const char *mode)
{
for (;;) {
FILE *fp = fopen(path, mode);
if (fp)
return fp;
if (errno == EINTR)
continue;
if (*mode && mode[1] == '+')
die_errno(_("could not open '%s' for reading and writing"), path);
else if (*mode == 'w' || *mode == 'a')
die_errno(_("could not open '%s' for writing"), path);
else
die_errno(_("could not open '%s' for reading"), path);
}
}
FILE *xfdopen(int fd, const char *mode) FILE *xfdopen(int fd, const char *mode)
{ {
FILE *stream = fdopen(fd, mode); FILE *stream = fdopen(fd, mode);