Merge commit 'v1.5.3-rc4'

This commit is contained in:
Johannes Sixt
2007-08-06 22:38:37 +02:00
51 changed files with 1015 additions and 377 deletions

View File

@@ -120,6 +120,7 @@ clean:
mv $@+ $@
%.1 %.5 %.7 : %.xml
$(RM) $@
xmlto -m callouts.xsl man $<
%.xml : %.txt

View File

@@ -40,16 +40,22 @@ Updates since v1.5.2
$GIT_DIR to work in a subdirectory of a working tree that is
not located at "$GIT_DIR/..".
- Giving "--file=<file>" option to "git config" is the same as
running the command with GIT_CONFIG=<file> environment.
- "git log" learned a new option "--follow", to follow
renaming history of a single file.
- "git-filter-branch" lets you rewrite the revision history of
the current branch, creating a new branch. You can specify a
number of filters to modify the commits, files and trees.
specified branches. You can specify a number of filters to
modify the commits, files and trees.
- "git-cvsserver" learned new options (--base-path, --export-all,
--strict-paths) inspired by git-daemon.
- "git daemon --base-path-relaxed" can help migrating a repository URL
that did not use to use --base-path to use --base-path.
- "git-commit" can use "-t templatefile" option and commit.template
configuration variable to prime the commit message given to you in the
editor.
@@ -120,6 +126,9 @@ Updates since v1.5.2
of the format ('tgz', 'tbz2' or 'zip'). Please update the
your configuration file accordingly.
- "git diff" (but not the plumbing level "git diff-tree") now
recursively descends into trees by default.
- The editor to use with many interactive commands can be
overridden with GIT_EDITOR environment variable, or if it
does not exist, with core.editor configuration variable. As
@@ -165,6 +174,9 @@ Updates since v1.5.2
- The diffstat given after a merge (or a pull) honors the
color.diff configuration.
- "git commit --amend" is now compatible with various message source
options such as -m/-C/-c/-F.
- "git-apply --whitespace=strip" removes blank lines added at
the end of the file.
@@ -262,6 +274,6 @@ this release, unless otherwise noted.
--
exec >/var/tmp/1
O=v1.5.3-rc3
O=v1.5.3-rc4
echo O=`git describe refs/heads/master`
git shortlog --no-merges $O..refs/heads/master ^refs/heads/maint

View File

@@ -101,7 +101,7 @@ Example
# Proxy settings
[core]
gitProxy="ssh" for "ssh://kernel.org/"
gitProxy="ssh" for "kernel.org"
gitProxy=default-proxy ; for the rest
Variables

View File

@@ -134,8 +134,8 @@ $ git branch -d -r origin/todo origin/html origin/man <1>
$ git branch -D test <2>
------------
+
<1> delete remote-tracking branches "todo", "html", "man"
<2> delete "test" branch even if the "master" branch does not have all
<1> Delete remote-tracking branches "todo", "html", "man"
<2> Delete "test" branch even if the "master" branch does not have all
commits from test branch.

View File

@@ -9,17 +9,17 @@ git-config - Get and set repository or global options
SYNOPSIS
--------
[verse]
'git-config' [--system | --global] [type] [-z|--null] name [value [value_regex]]
'git-config' [--system | --global] [type] --add name value
'git-config' [--system | --global] [type] --replace-all name [value [value_regex]]
'git-config' [--system | --global] [type] [-z|--null] --get name [value_regex]
'git-config' [--system | --global] [type] [-z|--null] --get-all name [value_regex]
'git-config' [--system | --global] [type] [-z|--null] --get-regexp name_regex [value_regex]
'git-config' [--system | --global] --unset name [value_regex]
'git-config' [--system | --global] --unset-all name [value_regex]
'git-config' [--system | --global] --rename-section old_name new_name
'git-config' [--system | --global] --remove-section name
'git-config' [--system | --global] [-z|--null] -l | --list
'git-config' [<file-option>] [type] [-z|--null] name [value [value_regex]]
'git-config' [<file-option>] [type] --add name value
'git-config' [<file-option>] [type] --replace-all name [value [value_regex]]
'git-config' [<file-option>] [type] [-z|--null] --get name [value_regex]
'git-config' [<file-option>] [type] [-z|--null] --get-all name [value_regex]
'git-config' [<file-option>] [type] [-z|--null] --get-regexp name_regex [value_regex]
'git-config' [<file-option>] --unset name [value_regex]
'git-config' [<file-option>] --unset-all name [value_regex]
'git-config' [<file-option>] --rename-section old_name new_name
'git-config' [<file-option>] --remove-section name
'git-config' [<file-option>] [-z|--null] -l | --list
DESCRIPTION
-----------
@@ -40,10 +40,16 @@ convert the value to the canonical form (simple decimal number for int,
a "true" or "false" string for bool). If no type specifier is passed,
no checks or transformations are performed on the value.
The file-option can be one of '--system', '--global' or '--file'
which specify where the values will be read from or written to.
The default is to assume the config file of the current repository,
.git/config unless defined otherwise with GIT_DIR and GIT_CONFIG
(see <<FILES>>).
This command will fail if:
. The .git/config file is invalid,
. Can not write to .git/config,
. The config file is invalid,
. Can not write to the config file,
. no section was provided,
. the section or key is invalid,
. you try to unset an option which does not exist,
@@ -93,6 +99,9 @@ rather than from all available files.
+
See also <<FILES>>.
-f config-file, --file config-file::
Use the given config file instead of the one specified by GIT_CONFIG.
--remove-section::
Remove the given section from the configuration file.
@@ -130,8 +139,8 @@ See also <<FILES>>.
FILES
-----
There are three files where git-config will search for configuration
options:
If not set explicitely with '--file', there are three files where
git-config will search for configuration options:
.git/config::
Repository specific configuration file. (The filename is
@@ -205,9 +214,7 @@ Given a .git/config like this:
; Proxy settings
[core]
gitproxy="ssh" for "ssh://kernel.org/"
gitproxy="proxy-command" for kernel.org
gitproxy="myprotocol-command" for "my://"
gitproxy=default-proxy ; for all the rest
you can set the filemode to true with
@@ -282,7 +289,7 @@ To actually match only values with an exclamation mark, you have to
To add a new proxy, without altering any of the existing ones, use
------------
% git config core.gitproxy '"proxy" for example.com'
% git config core.gitproxy '"proxy-command" for example.com'
------------

View File

@@ -54,6 +54,12 @@ OPTIONS
'git://example.com/hello.git', `git-daemon` will interpret the path
as '/srv/git/hello.git'.
--base-path-relaxed::
If --base-path is enabled and repo lookup fails, with this option
`git-daemon` will attempt to lookup without prefixing the base path.
This is useful for switching to --base-path usage, while still
allowing the old paths.
--interpolated-path=pathtemplate::
To support virtual hosting, an interpolated path template can be
used to dynamically construct alternate paths. The template

View File

@@ -76,10 +76,10 @@ $ git diff --cached <2>
$ git diff HEAD <3>
------------
+
<1> changes in the working tree not yet staged for the next commit.
<2> changes between the index and your last commit; what you
<1> Changes in the working tree not yet staged for the next commit.
<2> Changes between the index and your last commit; what you
would be committing if you run "git commit" without "-a" option.
<3> changes in the working tree since your last commit; what you
<3> Changes in the working tree since your last commit; what you
would be committing if you run "git commit -a"
Comparing with arbitrary commits::
@@ -90,12 +90,12 @@ $ git diff HEAD -- ./test <2>
$ git diff HEAD^ HEAD <3>
------------
+
<1> instead of using the tip of the current branch, compare with the
<1> Instead of using the tip of the current branch, compare with the
tip of "test" branch.
<2> instead of comparing with the tip of "test" branch, compare with
<2> Instead of comparing with the tip of "test" branch, compare with
the tip of the current branch, but limit the comparison to the
file "test".
<3> compare the version before the last commit and the last commit.
<3> Compare the version before the last commit and the last commit.
Limiting the diff output::
@@ -106,11 +106,11 @@ $ git diff --name-status <2>
$ git diff arch/i386 include/asm-i386 <3>
------------
+
<1> show only modification, rename and copy, but not addition
<1> Show only modification, rename and copy, but not addition
nor deletion.
<2> show only names and the nature of change, but not actual
<2> Show only names and the nature of change, but not actual
diff output.
<3> limit diff output to named subtrees.
<3> Limit diff output to named subtrees.
Munging the diff output::
+
@@ -119,9 +119,9 @@ $ git diff --find-copies-harder -B -C <1>
$ git diff -R <2>
------------
+
<1> spend extra cycles to find renames, copies and complete
<1> Spend extra cycles to find renames, copies and complete
rewrites (very expensive).
<2> output diff in reverse.
<2> Output diff in reverse.
Author

View File

@@ -107,11 +107,11 @@ pull after you are done and ready.
When things cleanly merge, these things happen:
1. the results are updated both in the index file and in your
working tree,
2. index file is written out as a tree,
3. the tree gets committed, and
4. the `HEAD` pointer gets advanced.
1. The results are updated both in the index file and in your
working tree;
2. Index file is written out as a tree;
3. The tree gets committed; and
4. The `HEAD` pointer gets advanced.
Because of 2., we require that the original state of the index
file to match exactly the current `HEAD` commit; otherwise we

View File

@@ -63,7 +63,7 @@ $ git commit -a -c ORIG_HEAD <3>
<1> This is most often done when you remembered what you
just committed is incomplete, or you misspelled your commit
message, or both. Leaves working tree as it was before "reset".
<2> make corrections to working tree files.
<2> Make corrections to working tree files.
<3> "reset" copies the old head to .git/ORIG_HEAD; redo the
commit by starting with its log message. If you do not need to
edit the message further, you can give -C option instead.
@@ -106,17 +106,17 @@ $ git reset <3>
$ git pull git://info.example.com/ nitfol <4>
------------
+
<1> you are happily working on something, and find the changes
<1> You are happily working on something, and find the changes
in these files are in good order. You do not want to see them
when you run "git diff", because you plan to work on other files
and changes with these files are distracting.
<2> somebody asks you to pull, and the changes sounds worthy of merging.
<3> however, you already dirtied the index (i.e. your index does
<2> Somebody asks you to pull, and the changes sounds worthy of merging.
<3> However, you already dirtied the index (i.e. your index does
not match the HEAD commit). But you know the pull you are going
to make does not affect frotz.c nor filfre.c, so you revert the
index changes for these two files. Your changes in working tree
remain there.
<4> then you can pull and merge, leaving frotz.c and filfre.c
<4> Then you can pull and merge, leaving frotz.c and filfre.c
changes still in the working tree.
Undo a merge or pull::
@@ -133,15 +133,15 @@ Fast forward
$ git reset --hard ORIG_HEAD <4>
------------
+
<1> try to update from the upstream resulted in a lot of
<1> Try to update from the upstream resulted in a lot of
conflicts; you were not ready to spend a lot of time merging
right now, so you decide to do that later.
<2> "pull" has not made merge commit, so "git reset --hard"
which is a synonym for "git reset --hard HEAD" clears the mess
from the index file and the working tree.
<3> merge a topic branch into the current branch, which resulted
<3> Merge a topic branch into the current branch, which resulted
in a fast forward.
<4> but you decided that the topic branch is not ready for public
<4> But you decided that the topic branch is not ready for public
consumption yet. "pull" or "merge" always leaves the original
tip of the current branch in ORIG_HEAD, so resetting hard to it
brings your index file and the working tree back to that state,

View File

@@ -405,7 +405,7 @@ the attributes given to path `t/abc` are computed as follows:
and `bar` attributes should be given to this path, so it
leaves `foo` and `bar` unset. Attribute `baz` is set.
3. Finally it examines `$GIT_DIR/info/gitattributes`. This file
3. Finally it examines `$GIT_DIR/info/attributes`. This file
is used to override the in-tree settings. The first line is
a match, and `foo` is set, `bar` is reverted to unspecified
state, and `baz` is unset.

View File

@@ -151,6 +151,7 @@ sysconfdir = /etc
else
sysconfdir = $(prefix)/etc
endif
lib = lib
ETC_GITCONFIG = $(sysconfdir)/gitconfig
# DESTDIR=
@@ -374,7 +375,7 @@ BUILTIN_OBJS = \
builtin-pack-refs.o
GITLIBS = $(LIB_FILE) $(XDIFF_LIB)
EXTLIBS = -lz
EXTLIBS =
#
# Platform specific tweaks
@@ -458,6 +459,10 @@ ifeq ($(uname_S),AIX)
NO_STRLCPY = YesPlease
NEEDS_LIBICONV=YesPlease
endif
ifeq ($(uname_S),GNU)
# GNU/Hurd
NO_STRLCPY=YesPlease
endif
ifeq ($(uname_S),IRIX64)
NO_IPV6=YesPlease
NO_SETENV=YesPlease
@@ -531,9 +536,9 @@ endif
ifndef NO_CURL
ifdef CURLDIR
# Try "-Wl,-rpath=$(CURLDIR)/lib" in such a case.
# Try "-Wl,-rpath=$(CURLDIR)/$(lib)" in such a case.
BASIC_CFLAGS += -I$(CURLDIR)/include
CURL_LIBCURL = -L$(CURLDIR)/lib $(CC_LD_DYNPATH)$(CURLDIR)/lib -lcurl
CURL_LIBCURL = -L$(CURLDIR)/$(lib) $(CC_LD_DYNPATH)$(CURLDIR)/$(lib) -lcurl
else
CURL_LIBCURL = -lcurl
endif
@@ -549,11 +554,17 @@ ifndef NO_CURL
endif
endif
ifdef ZLIB_PATH
BASIC_CFLAGS += -I$(ZLIB_PATH)/include
EXTLIBS += -L$(ZLIB_PATH)/$(lib) $(CC_LD_DYNPATH)$(ZLIB_PATH)/$(lib)
endif
EXTLIBS += -lz
ifndef NO_OPENSSL
OPENSSL_LIBSSL = -lssl
ifdef OPENSSLDIR
BASIC_CFLAGS += -I$(OPENSSLDIR)/include
OPENSSL_LINK = -L$(OPENSSLDIR)/lib $(CC_LD_DYNPATH)$(OPENSSLDIR)/lib
OPENSSL_LINK = -L$(OPENSSLDIR)/$(lib) $(CC_LD_DYNPATH)$(OPENSSLDIR)/$(lib)
else
OPENSSL_LINK =
endif
@@ -570,7 +581,7 @@ endif
ifdef NEEDS_LIBICONV
ifdef ICONVDIR
BASIC_CFLAGS += -I$(ICONVDIR)/include
ICONV_LINK = -L$(ICONVDIR)/lib $(CC_LD_DYNPATH)$(ICONVDIR)/lib
ICONV_LINK = -L$(ICONVDIR)/$(lib) $(CC_LD_DYNPATH)$(ICONVDIR)/$(lib)
else
ICONV_LINK =
endif
@@ -968,7 +979,7 @@ endif
### Testing rules
TEST_PROGRAMS = test-chmtime$X test-genrandom$X test-date$X test-delta$X test-sha1$X test-match-trees$X
TEST_PROGRAMS = test-chmtime$X test-genrandom$X test-date$X test-delta$X test-sha1$X test-match-trees$X test-absolute-path$X
all: $(TEST_PROGRAMS)

View File

@@ -2,7 +2,7 @@
#include "cache.h"
static const char git_config_set_usage[] =
"git-config [ --global | --system ] [ --bool | --int ] [ -z | --null ] [--get | --get-all | --get-regexp | --replace-all | --add | --unset | --unset-all] name [value [value_regex]] | --rename-section old_name new_name | --remove-section name | --list";
"git-config [ --global | --system | [ -f | --file ] config-file ] [ --bool | --int ] [ -z | --null ] [--get | --get-all | --get-regexp | --replace-all | --add | --unset | --unset-all] name [value [value_regex]] | --rename-section old_name new_name | --remove-section name | --list";
static char *key;
static regex_t *key_regexp;
@@ -186,6 +186,13 @@ int cmd_config(int argc, const char **argv, const char *prefix)
}
else if (!strcmp(argv[1], "--system"))
setenv(CONFIG_ENVIRONMENT, git_etc_gitconfig(), 1);
else if (!strcmp(argv[1], "--file") || !strcmp(argv[1], "-f")) {
if (argc < 3)
usage(git_config_set_usage);
setenv(CONFIG_ENVIRONMENT, argv[2], 1);
argc--;
argv++;
}
else if (!strcmp(argv[1], "--null") || !strcmp(argv[1], "-z")) {
term = '\0';
delim = '\n';

View File

@@ -184,36 +184,7 @@ static void copy_templates(const char *git_dir, int len, const char *template_di
closedir(dir);
}
/*
* Get the full path to the working tree specified in $GIT_WORK_TREE
* or NULL if no working tree is specified.
*/
static const char *get_work_tree(void)
{
const char *git_work_tree;
char cwd[PATH_MAX];
static char worktree[PATH_MAX];
git_work_tree = getenv(GIT_WORK_TREE_ENVIRONMENT);
if (!git_work_tree)
return NULL;
if (!getcwd(cwd, sizeof(cwd)))
die("Unable to read current working directory");
if (chdir(git_work_tree))
die("Cannot change directory to specified working tree '%s'",
git_work_tree);
if (git_work_tree[0] != '/') {
if (!getcwd(worktree, sizeof(worktree)))
die("Unable to read current working directory");
git_work_tree = worktree;
}
if (chdir(cwd))
die("Cannot come back to cwd");
return git_work_tree;
}
static int create_default_files(const char *git_dir, const char *git_work_tree,
const char *template_path)
static int create_default_files(const char *git_dir, const char *template_path)
{
unsigned len = strlen(git_dir);
static char path[PATH_MAX];
@@ -292,16 +263,16 @@ static int create_default_files(const char *git_dir, const char *git_work_tree,
}
git_config_set("core.filemode", filemode ? "true" : "false");
if (is_bare_repository() && !git_work_tree) {
if (is_bare_repository())
git_config_set("core.bare", "true");
}
else {
const char *work_tree = get_git_work_tree();
git_config_set("core.bare", "false");
/* allow template config file to override the default */
if (log_all_ref_updates == -1)
git_config_set("core.logallrefupdates", "true");
if (git_work_tree)
git_config_set("core.worktree", git_work_tree);
if (work_tree != git_work_tree_cfg)
git_config_set("core.worktree", work_tree);
}
return reinit;
}
@@ -318,7 +289,6 @@ static const char init_db_usage[] =
int cmd_init_db(int argc, const char **argv, const char *prefix)
{
const char *git_dir;
const char *git_work_tree;
const char *sha1_dir;
const char *template_dir = NULL;
char *path;
@@ -339,7 +309,11 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
usage(init_db_usage);
}
git_work_tree = get_work_tree();
git_work_tree_cfg = xcalloc(PATH_MAX, 1);
if (!getcwd(git_work_tree_cfg, PATH_MAX))
die ("Cannot access current working directory.");
if (access(get_git_work_tree(), X_OK))
die ("Cannot access work tree '%s'", get_git_work_tree());
/*
* Set up the default .git directory contents
@@ -356,7 +330,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
*/
check_repository_format();
reinit = create_default_files(git_dir, git_work_tree, template_dir);
reinit = create_default_files(git_dir, template_dir);
/*
* And set up the object store.

View File

@@ -469,9 +469,11 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
break;
}
if (require_work_tree &&
(!is_inside_work_tree() || is_inside_git_dir()))
die("This operation must be run in a work tree");
if (require_work_tree && !is_inside_work_tree()) {
const char *work_tree = get_git_work_tree();
if (!work_tree || chdir(work_tree))
die("This operation must be run in a work tree");
}
pathspec = get_pathspec(prefix, argv + i);

View File

@@ -237,8 +237,6 @@ static int eatspace(char *line)
static char *cleanup_subject(char *subject)
{
if (keep_subject)
return subject;
for (;;) {
char *p;
int len, remove;
@@ -425,6 +423,7 @@ static int read_one_header_line(char *line, int sz, FILE *in)
if (addlen >= sz - len)
addlen = sz - len - 1;
memcpy(line + len, continuation, addlen);
line[len] = '\n';
len += addlen;
}
}
@@ -846,6 +845,22 @@ static void handle_body(void)
return;
}
static void output_header_lines(FILE *fout, const char *hdr, char *data)
{
while (1) {
char *ep = strchr(data, '\n');
int len;
if (!ep)
len = strlen(data);
else
len = ep - data;
fprintf(fout, "%s: %.*s\n", hdr, len, data);
if (!ep)
break;
data = ep + 1;
}
}
static void handle_info(void)
{
char *sub;
@@ -863,9 +878,13 @@ static void handle_info(void)
continue;
if (!memcmp(header[i], "Subject", 7)) {
sub = cleanup_subject(hdr);
cleanup_space(sub);
fprintf(fout, "Subject: %s\n", sub);
if (keep_subject)
sub = hdr;
else {
sub = cleanup_subject(hdr);
cleanup_space(sub);
}
output_header_lines(fout, "Subject", sub);
} else if (!memcmp(header[i], "From", 4)) {
handle_from(hdr);
fprintf(fout, "Author: %s\n", name);

View File

@@ -97,7 +97,6 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
memset(&opts, 0, sizeof(opts));
opts.head_idx = -1;
setup_git_directory();
git_config(git_default_config);
newfd = hold_locked_index(&lock_file, 1);

View File

@@ -300,7 +300,7 @@ static struct commit_list *find_bisection(struct commit_list *list,
show_list("bisection 2 sorted", 0, nr, list);
*all = nr;
weights = xcalloc(on_list, sizeof(int*));
weights = xcalloc(on_list, sizeof(*weights));
counted = 0;
for (n = 0, p = list; p; p = p->next) {

View File

@@ -321,6 +321,13 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
}
if (!strcmp(arg, "--show-cdup")) {
const char *pfx = prefix;
if (!is_inside_work_tree()) {
const char *work_tree =
get_git_work_tree();
if (work_tree)
printf("%s\n", work_tree);
continue;
}
while (pfx) {
pfx = strchr(pfx, '/');
if (pfx) {

View File

@@ -43,8 +43,6 @@ int cmd_symbolic_ref(int argc, const char **argv, const char *prefix)
msg = argv[1];
if (!*msg)
die("Refusing to perform update with empty message");
if (strchr(msg, '\n'))
die("Refusing to perform update with \\n in message");
}
else if (!strcmp("--", arg)) {
argc--;

View File

@@ -23,8 +23,6 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
msg = argv[++i];
if (!*msg)
die("Refusing to perform update with empty message.");
if (strchr(msg, '\n'))
die("Refusing to perform update with \\n in message.");
continue;
}
if (!strcmp("-d", argv[i])) {

View File

@@ -208,12 +208,15 @@ enum object_type {
extern int is_bare_repository_cfg;
extern int is_bare_repository(void);
extern int is_inside_git_dir(void);
extern char *git_work_tree_cfg;
extern int is_inside_work_tree(void);
extern const char *get_git_dir(void);
extern char *get_object_directory(void);
extern char *get_refs_directory(void);
extern char *get_index_file(void);
extern char *get_graft_file(void);
extern int set_git_dir(const char *path);
extern const char *get_git_work_tree(void);
#define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES"
@@ -359,6 +362,11 @@ int git_config_perm(const char *var, const char *value);
int adjust_shared_perm(const char *path);
int safe_create_leading_directories(char *path);
char *enter_repo(char *path, int strict);
static inline int is_absolute_path(const char *path)
{
return path[0] == '/';
}
const char *make_absolute_path(const char *path);
/* Read and unpack a sha1 file into memory, write memory to a sha1 file */
extern int sha1_object_info(const unsigned char *, unsigned long *);

View File

@@ -731,7 +731,7 @@ int git_config_set_multivar(const char* key, const char* value,
int fd = -1, in_fd;
int ret;
char* config_filename;
char* lock_file;
struct lock_file *lock = NULL;
const char* last_dot = strrchr(key, '.');
config_filename = getenv(CONFIG_ENVIRONMENT);
@@ -741,7 +741,6 @@ int git_config_set_multivar(const char* key, const char* value,
config_filename = git_path("config");
}
config_filename = xstrdup(config_filename);
lock_file = xstrdup(mkpath("%s.lock", config_filename));
/*
* Since "key" actually contains the section name and the real
@@ -786,11 +785,12 @@ int git_config_set_multivar(const char* key, const char* value,
store.key[i] = 0;
/*
* The lock_file serves a purpose in addition to locking: the new
* The lock serves a purpose in addition to locking: the new
* contents of .git/config will be written into it.
*/
fd = open(lock_file, O_WRONLY | O_CREAT | O_EXCL, 0666);
if (fd < 0 || adjust_shared_perm(lock_file)) {
lock = xcalloc(sizeof(struct lock_file), 1);
fd = hold_lock_file_for_update(lock, config_filename, 0);
if (fd < 0) {
fprintf(stderr, "could not lock config file\n");
free(store.key);
ret = -1;
@@ -930,27 +930,31 @@ int git_config_set_multivar(const char* key, const char* value,
goto write_err_out;
munmap(contents, contents_sz);
unlink(config_filename);
}
close(fd);
fd = -1;
if (rename(lock_file, config_filename) < 0) {
fprintf(stderr, "Could not rename the lock file?\n");
if (close(fd) || commit_lock_file(lock) < 0) {
fprintf(stderr, "Cannot commit config file!\n");
ret = 4;
goto out_free;
}
/* fd is closed, so don't try to close it below. */
fd = -1;
/*
* lock is committed, so don't try to roll it back below.
* NOTE: Since lockfile.c keeps a linked list of all created
* lock_file structures, it isn't safe to free(lock). It's
* better to just leave it hanging around.
*/
lock = NULL;
ret = 0;
out_free:
if (0 <= fd)
close(fd);
if (lock)
rollback_lock_file(lock);
free(config_filename);
if (lock_file) {
unlink(lock_file);
free(lock_file);
}
return ret;
write_err_out:

View File

@@ -69,12 +69,26 @@ fi \
## Site configuration related to programs (before tests)
## --with-PACKAGE[=ARG] and --without-PACKAGE
#
# Set lib to alternative name of lib directory (e.g. lib64)
AC_ARG_WITH([lib],
[AS_HELP_STRING([--with-lib=ARG],
[ARG specifies alternative name for lib directory])],
[if test "$withval" = "no" -o "$withval" = "yes"; then \
AC_MSG_WARN([You should provide name for --with-lib=ARG]); \
else \
GIT_CONF_APPEND_LINE(lib=$withval); \
fi; \
],[])
#
# Define SHELL_PATH to provide path to shell.
GIT_ARG_SET_PATH(shell)
#
# Define PERL_PATH to provide path to Perl.
GIT_ARG_SET_PATH(perl)
#
# Define ZLIB_PATH to provide path to zlib.
GIT_ARG_SET_PATH(zlib)
#
# Declare the with-tcltk/without-tcltk options.
AC_ARG_WITH(tcltk,
AS_HELP_STRING([--with-tcltk],[use Tcl/Tk GUI (default is YES)])

View File

@@ -530,7 +530,7 @@ and returns the process output as a string."
(setf (git-fileinfo->needs-refresh info) t)
(when node ;preserve the marked flag
(setf (git-fileinfo->marked info) (git-fileinfo->marked (ewoc-data node))))
(if node (ewoc-set-data node info) (ewoc-enter-last status info))))
(if node (setf (ewoc-data node) info) (ewoc-enter-last status info))))
(defun git-run-diff-index (status files)
"Run git-diff-index on FILES and parse the results into STATUS.
@@ -589,6 +589,16 @@ Return the list of files that haven't been handled."
(when node (push (ewoc-data node) unmerged-files))))
(git-set-files-state unmerged-files 'unmerged))))
(defun git-get-exclude-files ()
"Get the list of exclude files to pass to git-ls-files."
(let (files
(config (git-config "core.excludesfile")))
(when (file-readable-p ".git/info/exclude")
(push ".git/info/exclude" files))
(when (and config (file-readable-p config))
(push config files))
files))
(defun git-update-status-files (files &optional default-state)
"Update the status of FILES from the index."
(unless git-status (error "Not in git-status buffer."))
@@ -598,11 +608,11 @@ Return the list of files that haven't been handled."
(git-run-ls-files status files 'added "-c")
(git-run-diff-index status files))))
(git-run-ls-unmerged status files)
(when (and (or (not files) remaining-files)
(file-readable-p ".git/info/exclude"))
(setq remaining-files (git-run-ls-files status remaining-files
'unknown "-o" "--exclude-from=.git/info/exclude"
(concat "--exclude-per-directory=" git-per-dir-ignore-file))))
(when (or (not files) remaining-files)
(let ((exclude-files (git-get-exclude-files)))
(setq remaining-files (apply #'git-run-ls-files status remaining-files 'unknown "-o"
(concat "--exclude-per-directory=" git-per-dir-ignore-file)
(mapcar (lambda (f) (concat "--exclude-from=" f)) exclude-files)))))
; mark remaining files with the default state (or remove them if nil)
(when remaining-files
(if default-state

View File

@@ -1181,11 +1181,11 @@ class P4Sync(Command):
elif ',' not in self.changeRange:
self.revision = self.changeRange
self.changeRange = ""
p = p[0:atIdx]
p = p[:atIdx]
elif p.find("#") != -1:
hashIdx = p.index("#")
self.revision = p[hashIdx:]
p = p[0:hashIdx]
p = p[:hashIdx]
elif self.previousDepotPaths == []:
self.revision = "#head"
@@ -1296,10 +1296,10 @@ class P4Sync(Command):
changeNum = line.split(" ")[1]
changes.append(changeNum)
changes.reverse()
changes.sort()
if len(self.maxChanges) > 0:
changes = changes[0:min(int(self.maxChanges), len(changes))]
changes = changes[:min(int(self.maxChanges), len(changes))]
if len(changes) == 0:
if not self.silent:

View File

@@ -16,7 +16,8 @@ static int reuseaddr;
static const char daemon_usage[] =
"git-daemon [--verbose] [--syslog] [--export-all]\n"
" [--timeout=n] [--init-timeout=n] [--strict-paths]\n"
" [--base-path=path] [--user-path | --user-path=path]\n"
" [--base-path=path] [--base-path-relaxed]\n"
" [--user-path | --user-path=path]\n"
" [--interpolated-path=path]\n"
" [--reuseaddr] [--detach] [--pid-file=file]\n"
" [--[enable|disable|allow-override|forbid-override]=service]\n"
@@ -34,6 +35,7 @@ static int export_all_trees;
/* Take all paths relative to this one if non-NULL */
static char *base_path;
static char *interpolated_path;
static int base_path_relaxed;
/* Flag indicating client sent extra args. */
static int saw_extended_args;
@@ -180,6 +182,7 @@ static char *path_ok(struct interp *itable)
{
static char rpath[PATH_MAX];
static char interp_path[PATH_MAX];
int retried_path = 0;
char *path;
char *dir;
@@ -235,7 +238,22 @@ static char *path_ok(struct interp *itable)
dir = rpath;
}
path = enter_repo(dir, strict_paths);
do {
path = enter_repo(dir, strict_paths);
if (path)
break;
/*
* if we fail and base_path_relaxed is enabled, try without
* prefixing the base path
*/
if (base_path && base_path_relaxed && !retried_path) {
dir = itable[INTERP_SLOT_DIR].value;
retried_path = 1;
continue;
}
break;
} while (1);
if (!path) {
logerror("'%s': unable to chdir or not a git archive", dir);
@@ -1061,6 +1079,10 @@ int main(int argc, char **argv)
base_path = arg+12;
continue;
}
if (!strcmp(arg, "--base-path-relaxed")) {
base_path_relaxed = 1;
continue;
}
if (!prefixcmp(arg, "--interpolated-path=")) {
interpolated_path = arg+20;
continue;

43
dir.c
View File

@@ -642,3 +642,46 @@ file_exists(const char *f)
struct stat sb;
return stat(f, &sb) == 0;
}
/*
* get_relative_cwd() gets the prefix of the current working directory
* relative to 'dir'. If we are not inside 'dir', it returns NULL.
*
* As a convenience, it also returns NULL if 'dir' is already NULL. The
* reason for this behaviour is that it is natural for functions returning
* directory names to return NULL to say "this directory does not exist"
* or "this directory is invalid". These cases are usually handled the
* same as if the cwd is not inside 'dir' at all, so get_relative_cwd()
* returns NULL for both of them.
*
* Most notably, get_relative_cwd(buffer, size, get_git_work_tree())
* unifies the handling of "outside work tree" with "no work tree at all".
*/
char *get_relative_cwd(char *buffer, int size, const char *dir)
{
char *cwd = buffer;
if (!dir)
return NULL;
if (!getcwd(buffer, size))
die("can't find the current directory: %s", strerror(errno));
if (!is_absolute_path(dir))
dir = make_absolute_path(dir);
while (*dir && *dir == *cwd) {
dir++;
cwd++;
}
if (*dir)
return NULL;
if (*cwd == '/')
return cwd + 1;
return cwd;
}
int is_inside_dir(const char *dir)
{
char buffer[PATH_MAX];
return get_relative_cwd(buffer, sizeof(buffer), dir) != NULL;
}

3
dir.h
View File

@@ -61,4 +61,7 @@ extern void add_exclude(const char *string, const char *base,
extern int file_exists(const char *);
extern struct dir_entry *dir_add_name(struct dir_struct *dir, const char *pathname, int len);
extern char *get_relative_cwd(char *buffer, int size, const char *dir);
extern int is_inside_dir(const char *dir);
#endif

View File

@@ -35,6 +35,10 @@ int pager_in_use;
int pager_use_color = 1;
int auto_crlf = 0; /* 1: both ways, -1: only when adding git objects */
/* This is set by setup_git_dir_gently() and/or git_default_config() */
char *git_work_tree_cfg;
static const char *work_tree;
static const char *git_dir;
static char *git_object_dir, *git_index_file, *git_refs_dir, *git_graft_file;
@@ -62,15 +66,8 @@ static void setup_git_env(void)
int is_bare_repository(void)
{
const char *dir, *s;
if (0 <= is_bare_repository_cfg)
return is_bare_repository_cfg;
dir = get_git_dir();
if (!strcmp(dir, DEFAULT_GIT_DIR_ENVIRONMENT))
return 0;
s = strrchr(dir, '/');
return !s || strcmp(s + 1, DEFAULT_GIT_DIR_ENVIRONMENT);
/* if core.bare is not 'false', let's see if there is a work tree */
return is_bare_repository_cfg && !get_git_work_tree();
}
const char *get_git_dir(void)
@@ -80,6 +77,26 @@ const char *get_git_dir(void)
return git_dir;
}
const char *get_git_work_tree(void)
{
static int initialized = 0;
if (!initialized) {
work_tree = getenv(GIT_WORK_TREE_ENVIRONMENT);
/* core.bare = true overrides implicit and config work tree */
if (!work_tree && is_bare_repository_cfg < 1) {
work_tree = git_work_tree_cfg;
/* make_absolute_path also normalizes the path */
if (work_tree && !is_absolute_path(work_tree))
work_tree = xstrdup(make_absolute_path(git_path(work_tree)));
} else if (work_tree)
work_tree = xstrdup(make_absolute_path(work_tree));
initialized = 1;
if (work_tree)
is_bare_repository_cfg = 0;
}
return work_tree;
}
char *get_object_directory(void)
{
if (!git_object_dir)
@@ -107,3 +124,11 @@ char *get_graft_file(void)
setup_git_env();
return git_graft_file;
}
int set_git_dir(const char *path)
{
if (setenv(GIT_DIR_ENVIRONMENT, path, 1))
return error("Could not set GIT_DIR to '%s'", path);
setup_git_env();
return 0;
}

View File

@@ -3,9 +3,16 @@
use strict;
sub run_cmd_pipe {
my $fh = undef;
open($fh, '-|', @_) or die;
return <$fh>;
if ($^O eq 'MSWin32') {
my @invalid = grep {m/[":*]/} @_;
die "$^O does not support: @invalid\n" if @invalid;
my @args = map { m/ /o ? "\"$_\"": $_ } @_;
return qx{@args};
} else {
my $fh = undef;
open($fh, '-|', @_) or die;
return <$fh>;
}
}
my ($GIT_DIR) = run_cmd_pipe(qw(git rev-parse --git-dir));
@@ -17,7 +24,7 @@ chomp($GIT_DIR);
sub refresh {
my $fh;
open $fh, '-|', qw(git update-index --refresh)
open $fh, 'git update-index --refresh |'
or die;
while (<$fh>) {
;# ignore 'needs update'
@@ -296,7 +303,7 @@ sub revert_cmd {
my @lines = run_cmd_pipe(qw(git ls-tree HEAD --),
map { $_->{VALUE} } @update);
my $fh;
open $fh, '|-', qw(git update-index --index-info)
open $fh, '| git update-index --index-info'
or die;
for (@lines) {
print $fh $_;
@@ -725,7 +732,7 @@ sub patch_update_cmd {
if (@result) {
my $fh;
open $fh, '|-', qw(git apply --cached);
open $fh, '| git apply --cached';
for (@{$head->{TEXT}}, @result) {
print $fh $_;
}

View File

@@ -190,7 +190,6 @@ $1"
;;
--a|--am|--ame|--amen|--amend)
amend=t
log_given=t$log_given
use_commit=HEAD
shift
;;
@@ -298,9 +297,9 @@ esac
case "$log_given" in
tt*)
die "Only one of -c/-C/-F/--amend can be used." ;;
die "Only one of -c/-C/-F can be used." ;;
*tm*|*mt*)
die "Option -m cannot be combined with -c/-C/-F/--amend." ;;
die "Option -m cannot be combined with -c/-C/-F." ;;
esac
case "$#,$also,$only,$amend" in

View File

@@ -405,6 +405,7 @@ do
require_clean_work_tree
mkdir "$DOTEST" || die "Could not create temporary $DOTEST"
if test ! -z "$2"
then
output git show-ref --verify --quiet "refs/heads/$2" ||
@@ -418,7 +419,6 @@ do
test -z "$ONTO" && ONTO=$UPSTREAM
mkdir "$DOTEST" || die "Could not create temporary $DOTEST"
: > "$DOTEST"/interactive || die "Could not mark as interactive"
git symbolic-ref HEAD > "$DOTEST"/head-name ||
die "Could not get HEAD"
@@ -463,8 +463,9 @@ do
#
EOF
git rev-list $MERGES_OPTION --pretty=oneline --abbrev-commit \
--abbrev=7 --reverse $UPSTREAM..$HEAD | \
sed "s/^/pick /" >> "$TODO"
--abbrev=7 --reverse --left-right --cherry-pick \
$UPSTREAM...$HEAD | \
sed -n "s/^>/pick /p" >> "$TODO"
test -z "$(grep -ve '^$' -e '^#' < $TODO)" &&
die_abort "Nothing to do"

View File

@@ -29,7 +29,8 @@ set_reflog_action() {
}
git_editor() {
GIT_EDITOR=${GIT_EDITOR:-$(git config core.editor || echo ${VISUAL:-${EDITOR}})}
: "${GIT_EDITOR:=$(git config core.editor)}"
: "${GIT_EDITOR:=${VISUAL:-${EDITOR}}}"
case "$GIT_EDITOR,$TERM" in
,dumb)
echo >&2 "No editor specified in GIT_EDITOR, core.editor, VISUAL,"
@@ -40,7 +41,7 @@ git_editor() {
exit 1
;;
esac
"${GIT_EDITOR:-vi}" "$1"
eval "${GIT_EDITOR:=vi}" '"$@"'
}
is_bare_repository () {
@@ -59,8 +60,7 @@ cd_to_toplevel () {
}
require_work_tree () {
test $(git rev-parse --is-inside-work-tree) = true &&
test $(git rev-parse --is-inside-git-dir) = false ||
test $(git rev-parse --is-inside-work-tree) = true ||
die "fatal: $0 cannot be used without a working tree."
}

View File

@@ -938,8 +938,8 @@ sub resolve_local_globs {
foreach (command(qw#for-each-ref --format=%(refname) refs/remotes#)) {
next unless m#^refs/remotes/$ref->{regex}$#;
my $p = $1;
my $pathname = $path->full_path($p);
my $refname = $ref->full_path($p);
my $pathname = desanitize_refname($path->full_path($p));
my $refname = desanitize_refname($ref->full_path($p));
if (my $existing = $fetch->{$pathname}) {
if ($existing ne $refname) {
die "Refspec conflict:\n",
@@ -1239,7 +1239,40 @@ sub new {
$self;
}
sub refname { "refs/remotes/$_[0]->{ref_id}" }
sub refname {
my ($refname) = "refs/remotes/$_[0]->{ref_id}" ;
# It cannot end with a slash /, we'll throw up on this because
# SVN can't have directories with a slash in their name, either:
if ($refname =~ m{/$}) {
die "ref: '$refname' ends with a trailing slash, this is ",
"not permitted by git nor Subversion\n";
}
# It cannot have ASCII control character space, tilde ~, caret ^,
# colon :, question-mark ?, asterisk *, space, or open bracket [
# anywhere.
#
# Additionally, % must be escaped because it is used for escaping
# and we want our escaped refname to be reversible
$refname =~ s{([ \%~\^:\?\*\[\t])}{uc sprintf('%%%02x',ord($1))}eg;
# no slash-separated component can begin with a dot .
# /.* becomes /%2E*
$refname =~ s{/\.}{/%2E}g;
# It cannot have two consecutive dots .. anywhere
# .. becomes %2E%2E
$refname =~ s{\.\.}{%2E%2E}g;
return $refname;
}
sub desanitize_refname {
my ($refname) = @_;
$refname =~ s{%(?:([0-9A-F]{2}))}{chr hex($1)}eg;
return $refname;
}
sub svm_uuid {
my ($self) = @_;

21
git.c
View File

@@ -276,9 +276,14 @@ static int run_command(struct cmd_struct *p, int argc, const char **argv)
prefix = setup_git_directory();
if (p->option & USE_PAGER)
setup_pager();
if ((p->option & NEED_WORK_TREE) &&
(!is_inside_work_tree() || is_inside_git_dir()))
die("%s must be run in a work tree", p->cmd);
if (p->option & NEED_WORK_TREE) {
const char *work_tree = get_git_work_tree();
const char *git_dir = get_git_dir();
if (!is_absolute_path(git_dir))
set_git_dir(make_absolute_path(git_dir));
if (!work_tree || chdir(work_tree))
die("%s must be run in a work tree", p->cmd);
}
trace_argv_printf(argv, argc, "trace: built-in: git");
status = p->fn(argc, argv, prefix);
@@ -466,11 +471,11 @@ int main(int argc, const char **argv)
cmd = argv[0];
/*
* We search for git commands in the following order:
* - git_exec_path()
* - the path of the "git" command if we could find it
* in $0
* - the regular PATH.
* We execute external git command via execv_git_cmd(),
* which looks at "--exec-path" option, GIT_EXEC_PATH
* environment, and $(gitexecdir) in Makefile while built,
* in this order. For scripted commands, we prepend
* the value of the exec_path variable to the PATH.
*/
if (exec_path)
prepend_to_path(exec_path, strlen(exec_path));

View File

@@ -1515,6 +1515,7 @@ sub git_get_projects_list {
File::Find::find({
follow_fast => 1, # follow symbolic links
follow_skip => 2, # ignore duplicates
dangling_symlinks => 0, # ignore dangling symlinks, silently
wanted => sub {
# skip project-list toplevel, if we get it.

View File

@@ -27,22 +27,109 @@ static void remove_lock_file_on_signal(int signo)
raise(signo);
}
/*
* p = absolute or relative path name
*
* Return a pointer into p showing the beginning of the last path name
* element. If p is empty or the root directory ("/"), just return p.
*/
static char *last_path_elm(char *p)
{
/* r starts pointing to null at the end of the string */
char *r = strchr(p, '\0');
if (r == p)
return p; /* just return empty string */
r--; /* back up to last non-null character */
/* back up past trailing slashes, if any */
while (r > p && *r == '/')
r--;
/*
* then go backwards until I hit a slash, or the beginning of
* the string
*/
while (r > p && *(r-1) != '/')
r--;
return r;
}
/* We allow "recursive" symbolic links. Only within reason, though */
#define MAXDEPTH 5
/*
* p = path that may be a symlink
* s = full size of p
*
* If p is a symlink, attempt to overwrite p with a path to the real
* file or directory (which may or may not exist), following a chain of
* symlinks if necessary. Otherwise, leave p unmodified.
*
* This is a best-effort routine. If an error occurs, p will either be
* left unmodified or will name a different symlink in a symlink chain
* that started with p's initial contents.
*
* Always returns p.
*/
static char *resolve_symlink(char *p, size_t s)
{
int depth = MAXDEPTH;
while (depth--) {
char link[PATH_MAX];
int link_len = readlink(p, link, sizeof(link));
if (link_len < 0) {
/* not a symlink anymore */
return p;
}
else if (link_len < sizeof(link))
/* readlink() never null-terminates */
link[link_len] = '\0';
else {
warning("%s: symlink too long", p);
return p;
}
if (link[0] == '/') {
/* absolute path simply replaces p */
if (link_len < s)
strcpy(p, link);
else {
warning("%s: symlink too long", p);
return p;
}
} else {
/*
* link is a relative path, so I must replace the
* last element of p with it.
*/
char *r = (char*)last_path_elm(p);
if (r - p + link_len < s)
strcpy(r, link);
else {
warning("%s: symlink too long", p);
return p;
}
}
}
return p;
}
static int lock_file(struct lock_file *lk, const char *path)
{
struct stat st;
if ((!lstat(path, &st)) && S_ISLNK(st.st_mode)) {
ssize_t sz;
static char target[PATH_MAX];
sz = readlink(path, target, sizeof(target));
if (sz < 0)
warning("Cannot readlink %s", path);
else if (target[0] != '/')
warning("Cannot lock target of relative symlink %s", path);
else
path = target;
}
sprintf(lk->filename, "%s.lock", path);
if (strlen(path) >= sizeof(lk->filename)) return -1;
strcpy(lk->filename, path);
/*
* subtract 5 from size to make sure there's room for adding
* ".lock" for the lock file name
*/
resolve_symlink(lk->filename, sizeof(lk->filename)-5);
strcat(lk->filename, ".lock");
lk->fd = open(lk->filename, O_RDWR | O_CREAT | O_EXCL, 0666);
if (0 <= lk->fd) {
if (!lock_file_list) {

65
path.c
View File

@@ -304,3 +304,68 @@ int adjust_shared_perm(const char *path)
return -2;
return 0;
}
/* We allow "recursive" symbolic links. Only within reason, though. */
#define MAXDEPTH 5
const char *make_absolute_path(const char *path)
{
static char bufs[2][PATH_MAX + 1], *buf = bufs[0], *next_buf = bufs[1];
char cwd[1024] = "";
int buf_index = 1, len;
int depth = MAXDEPTH;
char *last_elem = NULL;
struct stat st;
if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX)
die ("Too long path: %.*s", 60, path);
while (depth--) {
if (stat(buf, &st) || !S_ISDIR(st.st_mode)) {
char *last_slash = strrchr(buf, '/');
if (last_slash) {
*last_slash = '\0';
last_elem = xstrdup(last_slash + 1);
} else
last_elem = xstrdup(buf);
}
if (*buf) {
if (!*cwd && !getcwd(cwd, sizeof(cwd)))
die ("Could not get current working directory");
if (chdir(buf))
die ("Could not switch to '%s'", buf);
}
if (!getcwd(buf, PATH_MAX))
die ("Could not get current working directory");
if (last_elem) {
int len = strlen(buf);
if (len + strlen(last_elem) + 2 > PATH_MAX)
die ("Too long path name: '%s/%s'",
buf, last_elem);
buf[len] = '/';
strcpy(buf + len + 1, last_elem);
free(last_elem);
last_elem = NULL;
}
if (!lstat(buf, &st) && S_ISLNK(st.st_mode)) {
len = readlink(buf, next_buf, PATH_MAX);
if (len < 0)
die ("Invalid symlink: %s", buf);
next_buf[len] = '\0';
buf = next_buf;
buf_index = 1 - buf_index;
next_buf = bufs[buf_index];
} else
break;
}
if (*cwd && chdir(cwd))
die ("Could not change back to '%s'", cwd);
return buf;
}

View File

@@ -380,7 +380,7 @@ static int index_name_pos_also_unmerged(struct index_state *istate,
int add_file_to_index(struct index_state *istate, const char *path, int verbose)
{
int size, namelen;
int size, namelen, pos;
struct stat st;
struct cache_entry *ce;
@@ -414,6 +414,15 @@ int add_file_to_index(struct index_state *istate, const char *path, int verbose)
ce->ce_mode = ce_mode_from_stat(ent, st.st_mode);
}
pos = index_name_pos(istate, ce->name, namelen);
if (0 <= pos &&
!ce_stage(istate->cache[pos]) &&
!ie_modified(istate, istate->cache[pos], &st, 1)) {
/* Nothing changed, really */
free(ce);
return 0;
}
if (index_path(ce->sha1, path, &st, 1))
die("unable to index file %s", path);
if (add_index_entry(istate, ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE))

44
refs.c
View File

@@ -1045,6 +1045,32 @@ void unlock_ref(struct ref_lock *lock)
free(lock);
}
/*
* copy the reflog message msg to buf, which has been allocated sufficiently
* large, while cleaning up the whitespaces. Especially, convert LF to space,
* because reflog file is one line per entry.
*/
static int copy_msg(char *buf, const char *msg)
{
char *cp = buf;
char c;
int wasspace = 1;
*cp++ = '\t';
while ((c = *msg++)) {
if (wasspace && isspace(c))
continue;
wasspace = isspace(c);
if (wasspace)
c = ' ';
*cp++ = c;
}
while (buf < cp && isspace(cp[-1]))
cp--;
*cp++ = '\n';
return cp - buf;
}
static int log_ref_write(const char *ref_name, const unsigned char *old_sha1,
const unsigned char *new_sha1, const char *msg)
{
@@ -1098,21 +1124,7 @@ static int log_ref_write(const char *ref_name, const unsigned char *old_sha1,
adjust_shared_perm(log_file);
msglen = 0;
if (msg) {
/* clean up the message and make sure it is a single line */
for ( ; *msg; msg++)
if (!isspace(*msg))
break;
if (*msg) {
const char *ep = strchr(msg, '\n');
if (ep)
msglen = ep - msg;
else
msglen = strlen(msg);
}
}
msglen = msg ? strlen(msg) : 0;
committer = git_committer_info(-1);
maxlen = strlen(committer) + msglen + 100;
logrec = xmalloc(maxlen);
@@ -1121,7 +1133,7 @@ static int log_ref_write(const char *ref_name, const unsigned char *old_sha1,
sha1_to_hex(new_sha1),
committer);
if (msglen)
len += sprintf(logrec + len - 1, "\t%.*s\n", msglen, msg) - 1;
len += copy_msg(logrec + len - 1, msg) - 1;
written = len <= maxlen ? write_in_full(logfd, logrec, len) : -1;
free(logrec);
if (close(logfd) != 0 || written != len)

301
setup.c
View File

@@ -1,4 +1,8 @@
#include "cache.h"
#include "dir.h"
static int inside_git_dir = -1;
static int inside_work_tree = -1;
#ifdef __MINGW32__
static inline int is_dir_sep(char c) { return c == '/' || c == '\\'; }
@@ -207,105 +211,96 @@ static int is_git_directory(const char *suspect)
return 1;
}
static int inside_git_dir = -1;
int is_inside_git_dir(void)
{
if (inside_git_dir >= 0)
return inside_git_dir;
die("BUG: is_inside_git_dir called before setup_git_directory");
if (inside_git_dir < 0)
inside_git_dir = is_inside_dir(get_git_dir());
return inside_git_dir;
}
static int inside_work_tree = -1;
int is_inside_work_tree(void)
{
if (inside_git_dir >= 0)
return inside_work_tree;
die("BUG: is_inside_work_tree called before setup_git_directory");
if (inside_work_tree < 0)
inside_work_tree = is_inside_dir(get_git_work_tree());
return inside_work_tree;
}
static char *gitworktree_config;
static int git_setup_config(const char *var, const char *value)
/*
* If no worktree was given, and we are outside of a default work tree,
* now is the time to set it.
*
* In other words, if the user calls git with something like
*
* git --git-dir=/some/where/else/.git bla
*
* default to /some/where/else as working directory; if the specified
* git-dir does not end in "/.git", the cwd is used as working directory.
*/
const char *set_work_tree(const char *dir)
{
if (!strcmp(var, "core.worktree")) {
if (gitworktree_config)
strlcpy(gitworktree_config, value, PATH_MAX);
return 0;
char dir_buffer[PATH_MAX], *rel = NULL;
static char buffer[PATH_MAX + 1];
int len, suffix_len = strlen(DEFAULT_GIT_DIR_ENVIRONMENT) + 1;
/* strip the variable 'dir' of the postfix "/.git" if it has it */
len = strlen(dir);
if (len > suffix_len &&
!strcmp(dir + len - suffix_len, "/" DEFAULT_GIT_DIR_ENVIRONMENT)) {
if ((len - suffix_len) >= sizeof(dir_buffer))
die("directory name too long");
memcpy(dir_buffer, dir, len - suffix_len);
dir_buffer[len - suffix_len] = '\0';
/* are we inside the default work tree? */
rel = get_relative_cwd(buffer, sizeof(buffer), dir_buffer);
}
return git_default_config(var, value);
/* if rel is set, the cwd is _not_ the current working tree */
if (rel && *rel) {
if (!is_absolute_path(dir))
set_git_dir(make_absolute_path(dir));
dir = dir_buffer;
if (chdir(dir))
die("cannot chdir to %s: %s", dir, strerror(errno));
else
strcat(rel, "/");
inside_git_dir = 0;
} else {
rel = NULL;
dir = getcwd(buffer, sizeof(buffer));
}
git_work_tree_cfg = xstrdup(dir);
inside_work_tree = 1;
return rel;
}
/*
* We cannot decide in this function whether we are in the work tree or
* not, since the config can only be read _after_ this function was called.
*/
const char *setup_git_directory_gently(int *nongit_ok)
{
const char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT);
static char cwd[PATH_MAX+1];
char worktree[PATH_MAX+1], gitdir[PATH_MAX+1];
const char *gitdirenv, *gitworktree;
int wt_rel_gitdir = 0;
const char *gitdirenv;
int len, offset;
int minoffset = 0;
/*
* If GIT_DIR is set explicitly, we're not going
* to do any discovery, but we still do repository
* validation.
*/
gitdirenv = getenv(GIT_DIR_ENVIRONMENT);
if (!gitdirenv) {
int len, offset;
int minoffset = 0;
if (!getcwd(cwd, sizeof(cwd)-1))
die("Unable to read current working directory");
#ifdef __MINGW32__
if (cwd[1] == ':')
minoffset = 2;
#endif
offset = len = strlen(cwd);
for (;;) {
if (is_git_directory(".git"))
break;
if (offset == 0) {
offset = -1;
break;
}
chdir("..");
while (offset > minoffset && cwd[--offset] != '/')
; /* do nothing */
}
if (offset >= 0) {
inside_work_tree = 1;
git_config(git_default_config);
if (offset == len) {
inside_git_dir = 0;
return NULL;
}
cwd[len++] = '/';
cwd[len] = '\0';
inside_git_dir = !prefixcmp(cwd + offset + 1, ".git/");
return cwd + offset + 1;
}
if (chdir(cwd))
die("Cannot come back to cwd");
if (!is_git_directory(".")) {
if (nongit_ok) {
*nongit_ok = 1;
return NULL;
}
die("Not a git repository");
}
setenv(GIT_DIR_ENVIRONMENT, cwd, 1);
gitdirenv = getenv(GIT_DIR_ENVIRONMENT);
if (!gitdirenv)
die("getenv after setenv failed");
}
if (PATH_MAX - 40 < strlen(gitdirenv)) {
if (nongit_ok) {
*nongit_ok = 1;
if (gitdirenv) {
if (PATH_MAX - 40 < strlen(gitdirenv))
die("'$%s' too big", GIT_DIR_ENVIRONMENT);
if (is_git_directory(gitdirenv)) {
if (!work_tree_env)
return set_work_tree(gitdirenv);
return NULL;
}
die("$%s too big", GIT_DIR_ENVIRONMENT);
}
if (!is_git_directory(gitdirenv)) {
if (nongit_ok) {
*nongit_ok = 1;
return NULL;
@@ -315,92 +310,57 @@ const char *setup_git_directory_gently(int *nongit_ok)
if (!getcwd(cwd, sizeof(cwd)-1))
die("Unable to read current working directory");
if (chdir(gitdirenv)) {
if (nongit_ok) {
*nongit_ok = 1;
return NULL;
}
die("Cannot change directory to $%s '%s'",
GIT_DIR_ENVIRONMENT, gitdirenv);
}
if (!getcwd(gitdir, sizeof(gitdir)-1))
die("Unable to read current working directory");
if (chdir(cwd))
die("Cannot come back to cwd");
#ifdef __MINGW32__
if (cwd[1] == ':')
minoffset = 2;
#endif
/*
* In case there is a work tree we may change the directory,
* therefore make GIT_DIR an absolute path.
* Test in the following order (relative to the cwd):
* - .git/
* - ./ (bare)
* - ../.git/
* - ../ (bare)
* - ../../.git/
* etc.
*/
if (gitdirenv[0] != '/') {
setenv(GIT_DIR_ENVIRONMENT, gitdir, 1);
gitdirenv = getenv(GIT_DIR_ENVIRONMENT);
if (!gitdirenv)
die("getenv after setenv failed");
if (PATH_MAX - 40 < strlen(gitdirenv)) {
if (nongit_ok) {
*nongit_ok = 1;
return NULL;
}
die("$%s too big after expansion to absolute path",
GIT_DIR_ENVIRONMENT);
}
}
strcat(cwd, "/");
strcat(gitdir, "/");
inside_git_dir = !prefixcmp(cwd, gitdir);
gitworktree = getenv(GIT_WORK_TREE_ENVIRONMENT);
if (!gitworktree) {
gitworktree_config = worktree;
worktree[0] = '\0';
}
git_config(git_setup_config);
if (!gitworktree) {
gitworktree_config = NULL;
if (worktree[0])
gitworktree = worktree;
if (gitworktree && gitworktree[0] != '/')
wt_rel_gitdir = 1;
}
if (wt_rel_gitdir && chdir(gitdirenv))
die("Cannot change directory to $%s '%s'",
GIT_DIR_ENVIRONMENT, gitdirenv);
if (gitworktree && chdir(gitworktree)) {
if (nongit_ok) {
if (wt_rel_gitdir && chdir(cwd))
die("Cannot come back to cwd");
*nongit_ok = 1;
offset = len = strlen(cwd);
for (;;) {
if (is_git_directory(DEFAULT_GIT_DIR_ENVIRONMENT))
break;
if (is_git_directory(".")) {
inside_git_dir = 1;
if (!work_tree_env)
inside_work_tree = 0;
setenv(GIT_DIR_ENVIRONMENT, ".", 1);
return NULL;
}
if (wt_rel_gitdir)
die("Cannot change directory to working tree '%s'"
" from $%s", gitworktree, GIT_DIR_ENVIRONMENT);
else
die("Cannot change directory to working tree '%s'",
gitworktree);
}
if (!getcwd(worktree, sizeof(worktree)-1))
die("Unable to read current working directory");
strcat(worktree, "/");
inside_work_tree = !prefixcmp(cwd, worktree);
if (gitworktree && inside_work_tree && !prefixcmp(worktree, gitdir) &&
strcmp(worktree, gitdir)) {
inside_git_dir = 0;
chdir("..");
do {
if (!offset) {
if (nongit_ok) {
if (chdir(cwd))
die("Cannot come back to cwd");
*nongit_ok = 1;
return NULL;
}
die("Not a git repository");
}
} while (offset > minoffset && cwd[--offset] != '/');
}
if (!inside_work_tree) {
if (chdir(cwd))
die("Cannot come back to cwd");
inside_git_dir = 0;
if (!work_tree_env)
inside_work_tree = 1;
git_work_tree_cfg = xstrndup(cwd, offset);
if (offset == len)
return NULL;
}
if (!strcmp(cwd, worktree))
return NULL;
return cwd+strlen(worktree);
/* Make "offset" point to past the '/', and add a '/' at the end */
offset++;
cwd[len++] = '/';
cwd[len] = 0;
return cwd + offset;
}
int git_config_perm(const char *var, const char *value)
@@ -424,11 +384,21 @@ int git_config_perm(const char *var, const char *value)
int check_repository_format_version(const char *var, const char *value)
{
if (strcmp(var, "core.repositoryformatversion") == 0)
repository_format_version = git_config_int(var, value);
if (strcmp(var, "core.repositoryformatversion") == 0)
repository_format_version = git_config_int(var, value);
else if (strcmp(var, "core.sharedrepository") == 0)
shared_repository = git_config_perm(var, value);
return 0;
else if (strcmp(var, "core.bare") == 0) {
is_bare_repository_cfg = git_config_bool(var, value);
if (is_bare_repository_cfg == 1)
inside_work_tree = -1;
} else if (strcmp(var, "core.worktree") == 0) {
if (git_work_tree_cfg)
free(git_work_tree_cfg);
git_work_tree_cfg = xstrdup(value);
inside_work_tree = -1;
}
return 0;
}
int check_repository_format(void)
@@ -444,5 +414,16 @@ const char *setup_git_directory(void)
{
const char *retval = setup_git_directory_gently(NULL);
check_repository_format();
/* If the work tree is not the default one, recompute prefix */
if (inside_work_tree < 0) {
static char buffer[PATH_MAX + 1];
char *rel;
if (retval && chdir(retval))
die ("Could not jump back into original cwd");
rel = get_relative_cwd(buffer, PATH_MAX, get_git_work_tree());
return rel && *rel ? strcat(rel, "/") : NULL;
}
return retval;
}

View File

@@ -305,4 +305,20 @@ test_expect_success 'update-index D/F conflict' '
test $numpath0 = 1
'
test_expect_success 'absolute path works as expected' '
mkdir first &&
ln -s ../.git first/.git &&
mkdir second &&
ln -s ../first second/other &&
mkdir third &&
dir="$(cd .git; pwd -P)" &&
dir2=third/../second/other/.git &&
test "$dir" = "$(test-absolute-path $dir2)" &&
file="$dir"/index &&
test "$file" = "$(test-absolute-path $dir2/index)" &&
ln -s ../first/file .git/syml &&
sym="$(cd first; pwd -P)"/file &&
test "$sym" = "$(test-absolute-path $dir2/syml)"
'
test_done

View File

@@ -325,6 +325,9 @@ EOF
test_expect_success 'new variable inserts into proper section' 'cmp .git/config expect'
test_expect_success 'alternative GIT_CONFIG (non-existing file should fail)' \
'git config --file non-existing-config -l; test $? != 0'
cat > other-config << EOF
[ein]
bahn = strasse
@@ -338,6 +341,9 @@ GIT_CONFIG=other-config git config -l > output
test_expect_success 'alternative GIT_CONFIG' 'cmp output expect'
test_expect_success 'alternative GIT_CONFIG (--file)' \
'git config --file other-config -l > output && cmp output expect'
GIT_CONFIG=other-config git config anwohner.park ausweis
cat > expect << EOF
@@ -595,4 +601,19 @@ echo >>result
test_expect_success '--null --get-regexp' 'cmp result expect'
test_expect_success 'symlinked configuration' '
ln -s notyet myconfig &&
GIT_CONFIG=myconfig git config test.frotz nitfol &&
test -h myconfig &&
test -f notyet &&
test "z$(GIT_CONFIG=notyet git config test.frotz)" = znitfol &&
GIT_CONFIG=myconfig git config test.xyzzy rezrov &&
test -h myconfig &&
test -f notyet &&
test "z$(GIT_CONFIG=notyet git config test.frotz)" = znitfol &&
test "z$(GIT_CONFIG=notyet git config test.xyzzy)" = zrezrov
'
test_done

View File

@@ -31,9 +31,9 @@ test_rev_parse() {
test_rev_parse toplevel false false true ''
cd .git || exit 1
test_rev_parse .git/ false true true .git/
test_rev_parse .git/ true true false ''
cd objects || exit 1
test_rev_parse .git/objects/ false true true .git/objects/
test_rev_parse .git/objects/ true true false ''
cd ../.. || exit 1
mkdir -p sub/dir || exit 1
@@ -42,7 +42,7 @@ test_rev_parse subdirectory false false true sub/dir/
cd ../.. || exit 1
git config core.bare true
test_rev_parse 'core.bare = true' true false true
test_rev_parse 'core.bare = true' true false false
git config --unset core.bare
test_rev_parse 'core.bare undefined' false false true
@@ -50,28 +50,28 @@ test_rev_parse 'core.bare undefined' false false true
mkdir work || exit 1
cd work || exit 1
export GIT_DIR=../.git
export GIT_CONFIG="$GIT_DIR"/config
export GIT_CONFIG="$(pwd)"/../.git/config
git config core.bare false
test_rev_parse 'GIT_DIR=../.git, core.bare = false' false false true ''
test_rev_parse 'GIT_DIR=../.git, core.bare = false' false false true work/
git config core.bare true
test_rev_parse 'GIT_DIR=../.git, core.bare = true' true false true ''
test_rev_parse 'GIT_DIR=../.git, core.bare = true' true false false ''
git config --unset core.bare
test_rev_parse 'GIT_DIR=../.git, core.bare undefined' false false true ''
test_rev_parse 'GIT_DIR=../.git, core.bare undefined' false false true work/
mv ../.git ../repo.git || exit 1
export GIT_DIR=../repo.git
export GIT_CONFIG="$GIT_DIR"/config
export GIT_CONFIG="$(pwd)"/../repo.git/config
git config core.bare false
test_rev_parse 'GIT_DIR=../repo.git, core.bare = false' false false true ''
git config core.bare true
test_rev_parse 'GIT_DIR=../repo.git, core.bare = true' true false true ''
test_rev_parse 'GIT_DIR=../repo.git, core.bare = true' true false false ''
git config --unset core.bare
test_rev_parse 'GIT_DIR=../repo.git, core.bare undefined' true false true ''
test_rev_parse 'GIT_DIR=../repo.git, core.bare undefined' false false true ''
test_done

View File

@@ -33,17 +33,17 @@ mv .git repo.git || exit 1
say "core.worktree = relative path"
export GIT_DIR=repo.git
export GIT_CONFIG=$GIT_DIR/config
export GIT_CONFIG="$(pwd)"/$GIT_DIR/config
unset GIT_WORK_TREE
git config core.worktree ../work
test_rev_parse 'outside' false false false
cd work || exit 1
export GIT_DIR=../repo.git
export GIT_CONFIG=$GIT_DIR/config
export GIT_CONFIG="$(pwd)"/$GIT_DIR/config
test_rev_parse 'inside' false false true ''
cd sub/dir || exit 1
export GIT_DIR=../../../repo.git
export GIT_CONFIG=$GIT_DIR/config
export GIT_CONFIG="$(pwd)"/$GIT_DIR/config
test_rev_parse 'subdirectory' false false true sub/dir/
cd ../../.. || exit 1
@@ -84,9 +84,23 @@ test_rev_parse 'in repo.git' false true false
cd objects || exit 1
test_rev_parse 'in repo.git/objects' false true false
cd ../work || exit 1
test_rev_parse 'in repo.git/work' false false true ''
test_rev_parse 'in repo.git/work' false true true ''
cd sub/dir || exit 1
test_rev_parse 'in repo.git/sub/dir' false false true sub/dir/
test_rev_parse 'in repo.git/sub/dir' false true true sub/dir/
cd ../../../.. || exit 1
test_expect_success 'repo finds its work tree' '
(cd repo.git &&
: > work/sub/dir/untracked &&
test sub/dir/untracked = "$(git ls-files --others)")
'
test_expect_success 'repo finds its work tree from work tree, too' '
(cd repo.git/work/sub/dir &&
: > tracked &&
git --git-dir=../../.. add tracked &&
cd ../../.. &&
test sub/dir/tracked = "$(git ls-files)")
'
test_done

View File

@@ -68,6 +68,9 @@ test "\$1" = .git/COMMIT_EDITMSG && {
test -z "\$FAKE_COMMIT_AMEND" || echo "\$FAKE_COMMIT_AMEND" >> "\$1"
exit
}
test -z "\$EXPECT_COUNT" ||
test "\$EXPECT_COUNT" = \$(grep -ve "^#" -e "^$" < "\$1" | wc -l) ||
exit
test -z "\$FAKE_LINES" && exit
grep -v "^#" < "\$1" > "\$1".tmp
rm "\$1"
@@ -95,6 +98,14 @@ test_expect_success 'no changes are a nop' '
test $(git rev-parse I) = $(git rev-parse HEAD)
'
test_expect_success 'test the [branch] option' '
git checkout -b dead-end &&
git rm file6 &&
git commit -m "stop here" &&
git rebase -i F branch2 &&
test $(git rev-parse I) = $(git rev-parse HEAD)
'
test_expect_success 'rebase on top of a non-conflicting commit' '
git checkout branch1 &&
git tag original-branch1 &&
@@ -251,4 +262,16 @@ test_expect_success 'interrupted squash works as expected' '
test $one = $(git rev-parse HEAD~2)
'
test_expect_success 'ignore patch if in upstream' '
HEAD=$(git rev-parse HEAD) &&
git checkout -b has-cherry-picked HEAD^ &&
echo unrelated > file7 &&
git add file7 &&
test_tick &&
git commit -m "unrelated change" &&
git cherry-pick $HEAD &&
EXPECT_COUNT=1 git rebase -i $HEAD &&
test $HEAD = $(git rev-parse HEAD^)
'
test_done

48
t/t3405-rebase-malformed.sh Executable file
View File

@@ -0,0 +1,48 @@
#!/bin/sh
test_description='rebase should not insist on git message convention'
. ./test-lib.sh
cat >F <<\EOF
This is an example of a commit log message
that does not conform to git commit convention.
It has two paragraphs, but its first paragraph is not friendly
to oneline summary format.
EOF
test_expect_success setup '
>file1 &&
>file2 &&
git add file1 file2 &&
test_tick &&
git commit -m "Initial commit" &&
git checkout -b side &&
cat F >file2 &&
git add file2 &&
test_tick &&
git commit -F F &&
git cat-file commit HEAD | sed -e "1,/^\$/d" >F0 &&
git checkout master &&
echo One >file1 &&
test_tick &&
git add file1 &&
git commit -m "Second commit"
'
test_expect_success rebase '
git rebase master side &&
git cat-file commit HEAD | sed -e "1,/^\$/d" >F1 &&
diff -u F0 F1 &&
diff -u F F0
'
test_done

View File

@@ -135,8 +135,8 @@ test_expect_success 'git add with filemode=0, symlinks=0 prefers stage 2 over st
(
echo "100644 $(git hash-object -w stage1) 1 file"
echo "100755 $(git hash-object -w stage2) 2 file"
echo "100644 $(printf $s | git hash-object -w -t blob --stdin) 1 symlink"
echo "120000 $(printf $s | git hash-object -w -t blob --stdin) 2 symlink"
echo "100644 $(printf 1 | git hash-object -w -t blob --stdin) 1 symlink"
echo "120000 $(printf 2 | git hash-object -w -t blob --stdin) 2 symlink"
) | git update-index --index-info &&
git config core.filemode 0 &&
git config core.symlinks 0 &&

134
t/t7501-commit.sh Normal file
View File

@@ -0,0 +1,134 @@
#!/bin/sh
#
# Copyright (c) 2007 Kristian Høgsberg <krh@redhat.com>
#
# FIXME: Test the various index usages, -i and -o, test reflog,
# signoff, hooks
test_description='git-commit'
. ./test-lib.sh
test_tick
test_expect_success \
"initial status" \
"echo 'bongo bongo' >file &&
git-add file && \
git-status | grep 'Initial commit'"
test_expect_failure \
"fail initial amend" \
"git-commit --amend"
test_expect_success \
"initial commit" \
"git-commit -m initial"
test_expect_failure \
"invalid options 1" \
"git-commit -m foo -m bar -F file"
test_expect_failure \
"invalid options 2" \
"git-commit -C HEAD -m illegal"
test_expect_failure \
"using invalid commit with -C" \
"git-commit -C bogus"
test_expect_failure \
"testing nothing to commit" \
"git-commit -m initial"
test_expect_success \
"next commit" \
"echo 'bongo bongo bongo' >file \
git-commit -m next -a"
test_expect_failure \
"commit message from non-existing file" \
"echo 'more bongo: bongo bongo bongo bongo' >file && \
git-commit -F gah -a"
# Empty except stray tabs and spaces on a few lines.
sed -e 's/@$//' >msg <<EOF
@
@
Signed-off-by: hula
EOF
test_expect_failure \
"empty commit message" \
"git-commit -F msg -a"
test_expect_success \
"commit message from file" \
"echo 'this is the commit message, coming from a file' >msg && \
git-commit -F msg -a"
cat >editor <<\EOF
#!/bin/sh
sed -i -e "s/a file/an amend commit/g" $1
EOF
chmod 755 editor
test_expect_success \
"amend commit" \
"VISUAL=./editor git-commit --amend"
test_expect_failure \
"passing -m and -F" \
"echo 'enough with the bongos' >file && \
git-commit -F msg -m amending ."
test_expect_success \
"using message from other commit" \
"git-commit -C HEAD^ ."
cat >editor <<\EOF
#!/bin/sh
sed -i -e "s/amend/older/g" $1
EOF
chmod 755 editor
test_expect_success \
"editing message from other commit" \
"echo 'hula hula' >file && \
VISUAL=./editor git-commit -c HEAD^ -a"
test_expect_success \
"message from stdin" \
"echo 'silly new contents' >file && \
echo commit message from stdin | git-commit -F - -a"
test_expect_success \
"overriding author from command line" \
"echo 'gak' >file && \
git-commit -m 'author' --author 'Rubber Duck <rduck@convoy.org>' -a"
test_expect_success \
"interactive add" \
"echo 7 | git-commit --interactive | grep 'What now'"
test_expect_success \
"showing committed revisions" \
"git-rev-list HEAD >current"
# We could just check the head sha1, but checking each commit makes it
# easier to isolate bugs.
cat >expected <<\EOF
72c0dc9855b0c9dadcbfd5a31cab072e0cb774ca
9b88fc14ce6b32e3d9ee021531a54f18a5cf38a2
3536bbb352c3a1ef9a420f5b4242d48578b92aa7
d381ac431806e53f3dd7ac2f1ae0534f36d738b9
4fd44095ad6334f3ef72e4c5ec8ddf108174b54a
402702b49136e7587daa9280e91e4bb7cb2179f7
EOF
test_expect_success \
'validate git-rev-list output.' \
'diff current expected'
test_done

View File

@@ -11,6 +11,7 @@ TZ=UTC
export LANG LC_ALL PAGER TZ
EDITOR=:
VISUAL=:
unset GIT_EDITOR
unset AUTHOR_DATE
unset AUTHOR_EMAIL
unset AUTHOR_NAME

11
test-absolute-path.c Normal file
View File

@@ -0,0 +1,11 @@
#include "cache.h"
int main(int argc, char **argv)
{
while (argc > 1) {
puts(make_absolute_path(argv[1]));
argc--;
argv++;
}
return 0;
}