Merge branch 'master' into cc/trace

* master:
  Trace into a file or an open fd and refactor tracing code.
  Replace uses of strdup with xstrdup.
  consolidate two copies of new style object header parsing code.
  Documentation: Fix howto/revert-branch-rebase.html generation
  fmt-merge-msg: fix off-by-one bug
  git-rev-list(1): group options; reformat; document more options
  Constness tightening for move/link_temp_to_file()
  gitweb: Fix git_blame
  Include config.mak.autogen in the doc Makefile
  Use xmalloc instead of malloc
  git(7): move gitk(1) to the list of porcelain commands
  gitk: Fix some bugs in the new cherry-picking code
  gitk: Improve responsiveness while reading and layout out the graph
  gitk: Update preceding/following tag info when creating a tag
  gitk: Add a menu item for cherry-picking commits
  gitk: Fix a couple of buglets in the branch head menu items
  gitk: Add a context menu for heads
  gitk: Add a row context-menu item for creating a new branch
  gitk: Recompute ancestor/descendent heads/tags when rereading refs
  gitk: Minor cleanups
This commit is contained in:
Junio C Hamano 2006-09-02 17:10:16 -07:00
commit 1bbb2cff62
45 changed files with 972 additions and 312 deletions

View File

@ -33,6 +33,8 @@ man7dir=$(mandir)/man7
INSTALL?=install
-include ../config.mak.autogen
#
# Please note that there is a minor bug in asciidoc.
# The version after 6.0.3 _will_ include the patch found here:
@ -105,7 +107,7 @@ WEBDOC_DEST = /pub/software/scm/git/docs
$(patsubst %.txt,%.html,$(wildcard howto/*.txt)): %.html : %.txt
rm -f $@+ $@
sed -e '1,/^$$/d' $? | asciidoc -b xhtml11 - >$@+
sed -e '1,/^$$/d' $< | asciidoc -b xhtml11 - >$@+
mv $@+ $@
install-webdoc : html

View File

@ -27,111 +27,233 @@ SYNOPSIS
DESCRIPTION
-----------
Lists commit objects in reverse chronological order starting at the
given commit(s), taking ancestry relationship into account. This is
useful to produce human-readable log output.
Commits which are stated with a preceding '{caret}' cause listing to stop at
that point. Their parents are implied. "git-rev-list foo bar {caret}baz" thus
Commits which are stated with a preceding '{caret}' cause listing to
stop at that point. Their parents are implied. Thus the following
command:
-----------------------------------------------------------------------
$ git-rev-list foo bar ^baz
-----------------------------------------------------------------------
means "list all the commits which are included in 'foo' and 'bar', but
not in 'baz'".
A special notation <commit1>..<commit2> can be used as a
short-hand for {caret}<commit1> <commit2>.
A special notation "'<commit1>'..'<commit2>'" can be used as a
short-hand for "{caret}'<commit1>' '<commit2>'". For example, either of
the following may be used interchangeably:
Another special notation is <commit1>...<commit2> which is useful for
merges. The resulting set of commits is the symmetric difference
-----------------------------------------------------------------------
$ git-rev-list origin..HEAD
$ git-rev-list HEAD ^origin
-----------------------------------------------------------------------
Another special notation is "'<commit1>'...'<commit2>'" which is useful
for merges. The resulting set of commits is the symmetric difference
between the two operands. The following two commands are equivalent:
------------
$ git-rev-list A B --not $(git-merge-base --all A B)
$ git-rev-list A...B
------------
-----------------------------------------------------------------------
$ git-rev-list A B --not $(git-merge-base --all A B)
$ git-rev-list A...B
-----------------------------------------------------------------------
gitlink:git-rev-list[1] is a very essential git program, since it
provides the ability to build and traverse commit ancestry graphs. For
this reason, it has a lot of different options that enables it to be
used by commands as different as gitlink:git-bisect[1] and
gitlink:git-repack[1].
OPTIONS
-------
--pretty::
Print the contents of the commit changesets in human-readable form.
Commit Formatting
~~~~~~~~~~~~~~~~~
Using these options, gitlink:git-rev-list[1] will act similar to the
more specialized family of commit log tools: gitlink:git-log[1],
gitlink:git-show[1], and gitlink:git-whatchanged[1]
--pretty[='<format>']::
Pretty print the contents of the commit logs in a given format,
where '<format>' can be one of 'raw', 'medium', 'short', 'full',
and 'oneline'. When left out the format default to 'medium'.
--relative-date::
Show dates relative to the current time, e.g. "2 hours ago".
Only takes effect for dates shown in human-readable format, such
as when using "--pretty".
--header::
Print the contents of the commit in raw-format; each
record is separated with a NUL character.
Print the contents of the commit in raw-format; each record is
separated with a NUL character.
--parents::
Print the parents of the commit.
--objects::
Print the object IDs of any object referenced by the listed commits.
'git-rev-list --objects foo ^bar' thus means "send me all object IDs
which I need to download if I have the commit object 'bar', but
not 'foo'".
Diff Formatting
~~~~~~~~~~~~~~~
--objects-edge::
Similar to `--objects`, but also print the IDs of
excluded commits prefixed with a `-` character. This is
used by `git-pack-objects` to build 'thin' pack, which
records objects in deltified form based on objects
contained in these excluded commits to reduce network
traffic.
Below are listed options that control the formatting of diff output.
Some of them are specific to gitlink:git-rev-list[1], however other diff
options may be given. See gitlink:git-diff-files[1] for more options.
--unpacked::
Only useful with `--objects`; print the object IDs that
are not in packs.
-c::
--bisect::
Limit output to the one commit object which is roughly halfway
between the included and excluded commits. Thus, if 'git-rev-list
--bisect foo {caret}bar {caret}baz' outputs 'midpoint', the output
of 'git-rev-list foo {caret}midpoint' and 'git-rev-list midpoint
{caret}bar {caret}baz' would be of roughly the same length.
Finding the change
which introduces a regression is thus reduced to a binary search:
repeatedly generate and test new 'midpoint's until the commit chain
is of length one.
This flag changes the way a merge commit is displayed. It shows
the differences from each of the parents to the merge result
simultaneously instead of showing pairwise diff between a parent
and the result one at a time. Furthermore, it lists only files
which were modified from all parents.
--cc::
This flag implies the '-c' options and further compresses the
patch output by omitting hunks that show differences from only
one parent, or show the same change from all but one parent for
an Octopus merge.
-r::
Show recursive diffs.
-t::
Show the tree objects in the diff output. This implies '-r'.
Commit Limiting
~~~~~~~~~~~~~~~
Besides specifying a range of commits that should be listed using the
special notations explained in the description, additional commit
limiting may be applied.
--
-n 'number', --max-count='number'::
--max-count::
Limit the number of commits output.
--max-age=timestamp, --min-age=timestamp::
--since='date', --after='date'::
Show commits more recent than a specific date.
--until='date', --before='date'::
Show commits older than a specific date.
--max-age='timestamp', --min-age='timestamp'::
Limit the commits output to specified time range.
--sparse::
When optional paths are given, the command outputs only
the commits that changes at least one of them, and also
ignores merges that do not touch the given paths. This
flag makes the command output all eligible commits
(still subject to count and age limitation), but apply
merge simplification nevertheless.
--remove-empty::
Stop when a given path disappears from the tree.
--no-merges::
Do not print commits with more than one parent.
--not::
Reverses the meaning of the '{caret}' prefix (or lack
thereof) for all following revision specifiers, up to
the next `--not`.
Reverses the meaning of the '{caret}' prefix (or lack thereof)
for all following revision specifiers, up to the next '--not'.
--all::
Pretend as if all the refs in `$GIT_DIR/refs/` are
listed on the command line as <commit>.
--topo-order::
By default, the commits are shown in reverse
chronological order. This option makes them appear in
topological order (i.e. descendant commits are shown
before their parents).
Pretend as if all the refs in `$GIT_DIR/refs/` are listed on the
command line as '<commit>'.
--merge::
After a failed merge, show refs that touch files having a
conflict and don't exist on all heads to merge.
--relative-date::
Show dates relative to the current time, e.g. "2 hours ago".
Only takes effect for dates shown in human-readable format,
such as when using "--pretty".
--boundary::
Output uninteresting commits at the boundary, which are usually
not shown.
--dense, --sparse::
When optional paths are given, the default behaviour ('--dense') is to
only output commits that changes at least one of them, and also ignore
merges that do not touch the given paths.
Use the '--sparse' flag to makes the command output all eligible commits
(still subject to count and age limitation), but apply merge
simplification nevertheless.
--bisect::
Limit output to the one commit object which is roughly halfway between
the included and excluded commits. Thus, if
-----------------------------------------------------------------------
$ git-rev-list --bisect foo ^bar ^baz
-----------------------------------------------------------------------
outputs 'midpoint', the output of the two commands
-----------------------------------------------------------------------
$ git-rev-list foo ^midpoint
$ git-rev-list midpoint ^bar ^baz
-----------------------------------------------------------------------
would be of roughly the same length. Finding the change which
introduces a regression is thus reduced to a binary search: repeatedly
generate and test new 'midpoint's until the commit chain is of length
one.
--
Commit Ordering
~~~~~~~~~~~~~~~
By default, the commits are shown in reverse chronological order.
--topo-order::
This option makes them appear in topological order (i.e.
descendant commits are shown before their parents).
--date-order::
This option is similar to '--topo-order' in the sense that no
parent comes before all of its children, but otherwise things
are still ordered in the commit timestamp order.
Object Traversal
~~~~~~~~~~~~~~~~
These options are mostly targeted for packing of git repositories.
--objects::
Print the object IDs of any object referenced by the listed
commits. 'git-rev-list --objects foo ^bar' thus means "send me
all object IDs which I need to download if I have the commit
object 'bar', but not 'foo'".
--objects-edge::
Similar to '--objects', but also print the IDs of excluded
commits prefixed with a "-" character. This is used by
gitlink:git-pack-objects[1] to build "thin" pack, which records
objects in deltified form based on objects contained in these
excluded commits to reduce network traffic.
--unpacked::
Only useful with '--objects'; print the object IDs that are not
in packs.
Author
------
@ -139,9 +261,9 @@ Written by Linus Torvalds <torvalds@osdl.org>
Documentation
--------------
Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
Documentation by David Greaves, Junio C Hamano, Jonas Fonseca
and the git-list <git@vger.kernel.org>.
GIT
---
Part of the gitlink:git[7] suite

View File

@ -303,6 +303,9 @@ gitlink:git-format-patch[1]::
gitlink:git-grep[1]::
Print lines matching a pattern.
gitlink:gitk[1]::
The git repository browser.
gitlink:git-log[1]::
Shows commit logs.
@ -483,13 +486,6 @@ gitlink:git-stripspace[1]::
Filter out empty lines.
Commands not yet documented
---------------------------
gitlink:gitk[1]::
The gitk repository browser.
Configuration Mechanism
-----------------------

View File

@ -617,7 +617,7 @@ static void simplify_commit(struct rev_info *revs, struct commit *commit)
if (new_name) {
struct util_info* putil = get_util(p);
if (!putil->pathname)
putil->pathname = strdup(new_name);
putil->pathname = xstrdup(new_name);
} else {
*pp = parent->next;
continue;

View File

@ -2449,7 +2449,7 @@ static int apply_patch(int fd, const char *filename, int inaccurate_eof)
static int git_apply_config(const char *var, const char *value)
{
if (!strcmp(var, "apply.whitespace")) {
apply_default_whitespace = strdup(value);
apply_default_whitespace = xstrdup(value);
return 0;
}
return git_default_config(var, value);

View File

@ -111,43 +111,43 @@ static int handle_line(char *line)
i = find_in_list(&srcs, src);
if (i < 0) {
i = srcs.nr;
append_to_list(&srcs, strdup(src),
append_to_list(&srcs, xstrdup(src),
xcalloc(1, sizeof(struct src_data)));
}
src_data = srcs.payload[i];
if (pulling_head) {
origin = strdup(src);
origin = xstrdup(src);
src_data->head_status |= 1;
} else if (!strncmp(line, "branch ", 7)) {
origin = strdup(line + 7);
origin = xstrdup(line + 7);
append_to_list(&src_data->branch, origin, NULL);
src_data->head_status |= 2;
} else if (!strncmp(line, "tag ", 4)) {
origin = line;
append_to_list(&src_data->tag, strdup(origin + 4), NULL);
append_to_list(&src_data->tag, xstrdup(origin + 4), NULL);
src_data->head_status |= 2;
} else if (!strncmp(line, "remote branch ", 14)) {
origin = strdup(line + 14);
origin = xstrdup(line + 14);
append_to_list(&src_data->r_branch, origin, NULL);
src_data->head_status |= 2;
} else {
origin = strdup(src);
append_to_list(&src_data->generic, strdup(line), NULL);
origin = xstrdup(src);
append_to_list(&src_data->generic, xstrdup(line), NULL);
src_data->head_status |= 2;
}
if (!strcmp(".", src) || !strcmp(src, origin)) {
int len = strlen(origin);
if (origin[0] == '\'' && origin[len - 1] == '\'') {
char *new_origin = malloc(len - 1);
char *new_origin = xmalloc(len - 1);
memcpy(new_origin, origin + 1, len - 2);
new_origin[len - 1] = 0;
new_origin[len - 2] = 0;
origin = new_origin;
} else
origin = strdup(origin);
origin = xstrdup(origin);
} else {
char *new_origin = malloc(strlen(origin) + strlen(src) + 5);
char *new_origin = xmalloc(strlen(origin) + strlen(src) + 5);
sprintf(new_origin, "%s of %s", origin, src);
origin = new_origin;
}
@ -203,7 +203,7 @@ static void shortlog(const char *name, unsigned char *sha1,
bol = strstr(commit->buffer, "\n\n");
if (!bol) {
append_to_list(&subjects, strdup(sha1_to_hex(
append_to_list(&subjects, xstrdup(sha1_to_hex(
commit->object.sha1)),
NULL);
continue;
@ -214,11 +214,11 @@ static void shortlog(const char *name, unsigned char *sha1,
if (eol) {
int len = eol - bol;
oneline = malloc(len + 1);
oneline = xmalloc(len + 1);
memcpy(oneline, bol, len);
oneline[len] = 0;
} else
oneline = strdup(bol);
oneline = xstrdup(bol);
append_to_list(&subjects, oneline, NULL);
}
@ -277,7 +277,7 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
usage(fmt_merge_msg_usage);
/* get current branch */
head = strdup(git_path("HEAD"));
head = xstrdup(git_path("HEAD"));
current_branch = resolve_ref(head, head_sha1, 1);
current_branch += strlen(head) - 4;
free((char *)head);

View File

@ -1048,7 +1048,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
/* ignore empty line like grep does */
if (!buf[0])
continue;
add_pattern(&opt, strdup(buf), argv[1], ++lno,
add_pattern(&opt, xstrdup(buf), argv[1], ++lno,
GREP_PATTERN);
}
fclose(patterns);

View File

@ -100,7 +100,7 @@ static int name_ref(const char *path, const unsigned char *sha1)
else if (!strncmp(path, "refs/", 5))
path = path + 5;
name_rev(commit, strdup(path), 0, 0, deref);
name_rev(commit, xstrdup(path), 0, 0, deref);
}
return 0;
}

View File

@ -106,7 +106,7 @@ static void process_tree(struct tree *tree,
obj->flags |= SEEN;
if (parse_tree(tree) < 0)
die("bad tree object %s", sha1_to_hex(obj->sha1));
name = strdup(name);
name = xstrdup(name);
add_object(obj, p, path, name);
me.up = path;
me.elem = name;

View File

@ -33,7 +33,7 @@ static int expand_one_ref(const char *ref, const unsigned char *sha1)
ref += 5;
if (!strncmp(ref, "tags/", 5))
add_refspec(strdup(ref));
add_refspec(xstrdup(ref));
return 0;
}
@ -100,12 +100,12 @@ static int get_remotes_uri(const char *repo, const char *uri[MAX_URI])
if (!is_refspec) {
if (n < MAX_URI)
uri[n++] = strdup(s);
uri[n++] = xstrdup(s);
else
error("more than %d URL's specified, ignoring the rest", MAX_URI);
}
else if (is_refspec && !has_explicit_refspec)
add_refspec(strdup(s));
add_refspec(xstrdup(s));
}
fclose(f);
if (!n)
@ -125,13 +125,13 @@ static int get_remote_config(const char* key, const char* value)
!strncmp(key + 7, config_repo, config_repo_len)) {
if (!strcmp(key + 7 + config_repo_len, ".url")) {
if (config_current_uri < MAX_URI)
config_uri[config_current_uri++] = strdup(value);
config_uri[config_current_uri++] = xstrdup(value);
else
error("more than %d URL's specified, ignoring the rest", MAX_URI);
}
else if (config_get_refspecs &&
!strcmp(key + 7 + config_repo_len, ".push"))
add_refspec(strdup(value));
add_refspec(xstrdup(value));
}
return 0;
}

View File

@ -72,19 +72,19 @@ static int get_value(const char* key_, const char* regex_)
const char *home = getenv("HOME");
local = getenv("GIT_CONFIG_LOCAL");
if (!local)
local = repo_config = strdup(git_path("config"));
local = repo_config = xstrdup(git_path("config"));
if (home)
global = strdup(mkpath("%s/.gitconfig", home));
global = xstrdup(mkpath("%s/.gitconfig", home));
}
key = strdup(key_);
key = xstrdup(key_);
for (tl=key+strlen(key)-1; tl >= key && *tl != '.'; --tl)
*tl = tolower(*tl);
for (tl=key; *tl && *tl != '.'; ++tl)
*tl = tolower(*tl);
if (use_key_regexp) {
key_regexp = (regex_t*)malloc(sizeof(regex_t));
key_regexp = (regex_t*)xmalloc(sizeof(regex_t));
if (regcomp(key_regexp, key, REG_EXTENDED)) {
fprintf(stderr, "Invalid key pattern: %s\n", key_);
goto free_strings;
@ -97,7 +97,7 @@ static int get_value(const char* key_, const char* regex_)
regex_++;
}
regexp = (regex_t*)malloc(sizeof(regex_t));
regexp = (regex_t*)xmalloc(sizeof(regex_t));
if (regcomp(regexp, regex_, REG_EXTENDED)) {
fprintf(stderr, "Invalid pattern: %s\n", regex_);
goto free_strings;

View File

@ -109,7 +109,7 @@ static void process_blob(struct blob *blob,
if (obj->flags & (UNINTERESTING | SEEN))
return;
obj->flags |= SEEN;
name = strdup(name);
name = xstrdup(name);
add_object(obj, p, path, name);
}
@ -130,7 +130,7 @@ static void process_tree(struct tree *tree,
if (parse_tree(tree) < 0)
die("bad tree object %s", sha1_to_hex(obj->sha1));
obj->flags |= SEEN;
name = strdup(name);
name = xstrdup(name);
add_object(obj, p, path, name);
me.up = path;
me.elem = name;

View File

@ -32,7 +32,7 @@ static int remove_file(const char *name)
ret = unlink(name);
if (!ret && (slash = strrchr(name, '/'))) {
char *n = strdup(name);
char *n = xstrdup(name);
do {
n[slash - name] = 0;
name = n;

View File

@ -163,7 +163,7 @@ static void name_commits(struct commit_list *list,
en += sprintf(en, "^");
else
en += sprintf(en, "^%d", nth);
name_commit(p, strdup(newname), 0);
name_commit(p, xstrdup(newname), 0);
i++;
name_first_parent_chain(p);
}
@ -364,7 +364,7 @@ static int append_ref(const char *refname, const unsigned char *sha1)
refname, MAX_REVS);
return 0;
}
ref_name[ref_name_cnt++] = strdup(refname);
ref_name[ref_name_cnt++] = xstrdup(refname);
ref_name[ref_name_cnt] = NULL;
return 0;
}
@ -521,7 +521,7 @@ static int git_show_branch_config(const char *var, const char *value)
default_alloc = default_alloc * 3 / 2 + 20;
default_arg = xrealloc(default_arg, sizeof *default_arg * default_alloc);
}
default_arg[default_num++] = strdup(value);
default_arg[default_num++] = xstrdup(value);
default_arg[default_num] = NULL;
return 0;
}

View File

@ -7,7 +7,7 @@ static const char git_symbolic_ref_usage[] =
static void check_symref(const char *HEAD)
{
unsigned char sha1[20];
const char *git_HEAD = strdup(git_path("%s", HEAD));
const char *git_HEAD = xstrdup(git_path("%s", HEAD));
const char *git_refs_heads_master = resolve_ref(git_HEAD, sha1, 0);
if (git_refs_heads_master) {
/* we want to strip the .git/ part */
@ -26,7 +26,7 @@ int cmd_symbolic_ref(int argc, const char **argv, const char *prefix)
check_symref(argv[1]);
break;
case 3:
create_symref(strdup(git_path("%s", argv[1])), argv[2]);
create_symref(xstrdup(git_path("%s", argv[1])), argv[2]);
break;
default:
usage(git_symbolic_ref_usage);

View File

@ -351,7 +351,7 @@ static int remote_tar(int argc, const char **argv)
usage(tar_tree_usage);
/* --remote=<repo> */
url = strdup(argv[1]+9);
url = xstrdup(argv[1]+9);
pid = git_connect(fd, url, exec);
if (pid < 0)
return 1;

View File

@ -53,7 +53,7 @@ int cmd_upload_tar(int argc, const char **argv, const char *prefix)
return nak("expected (optional) base");
if (buf[len-1] == '\n')
buf[--len] = 0;
base = strdup(buf + 5);
base = xstrdup(buf + 5);
len = packet_read_line(0, buf, sizeof(buf));
}
if (len)

View File

@ -311,11 +311,11 @@ int cmd_zip_tree(int argc, const char **argv, const char *prefix)
switch (argc) {
case 3:
base = strdup(argv[2]);
base = xstrdup(argv[2]);
baselen = strlen(base);
break;
case 2:
base = strdup("");
base = xstrdup("");
baselen = 0;
break;
default:

View File

@ -257,7 +257,7 @@ extern int check_sha1_signature(const unsigned char *sha1, void *buf, unsigned l
extern int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer,
size_t bufsize, size_t *bufposn);
extern int write_sha1_to_fd(int fd, const unsigned char *sha1);
extern int move_temp_to_file(const char *tmpfile, char *filename);
extern int move_temp_to_file(const char *tmpfile, const char *filename);
extern int has_sha1_pack(const unsigned char *sha1);
extern int has_sha1_file(const unsigned char *sha1);

View File

@ -350,11 +350,11 @@ int git_config(config_fn_t fn)
home = getenv("HOME");
filename = getenv("GIT_CONFIG_LOCAL");
if (!filename)
filename = repo_config = strdup(git_path("config"));
filename = repo_config = xstrdup(git_path("config"));
}
if (home) {
char *user_config = strdup(mkpath("%s/.gitconfig", home));
char *user_config = xstrdup(mkpath("%s/.gitconfig", home));
if (!access(user_config, R_OK))
ret = git_config_from_file(fn, user_config);
free(user_config);
@ -545,8 +545,8 @@ int git_config_set_multivar(const char* key, const char* value,
if (!config_filename)
config_filename = git_path("config");
}
config_filename = strdup(config_filename);
lock_file = strdup(mkpath("%s.lock", config_filename));
config_filename = xstrdup(config_filename);
lock_file = xstrdup(mkpath("%s.lock", config_filename));
/*
* Since "key" actually contains the section name and the real
@ -565,7 +565,7 @@ int git_config_set_multivar(const char* key, const char* value,
/*
* Validate the key and while at it, lower case it for matching.
*/
store.key = (char*)malloc(strlen(key)+1);
store.key = xmalloc(strlen(key) + 1);
dot = 0;
for (i = 0; key[i]; i++) {
unsigned char c = key[i];
@ -633,7 +633,7 @@ int git_config_set_multivar(const char* key, const char* value,
} else
store.do_not_match = 0;
store.value_regex = (regex_t*)malloc(sizeof(regex_t));
store.value_regex = (regex_t*)xmalloc(sizeof(regex_t));
if (regcomp(store.value_regex, value_regex,
REG_EXTENDED)) {
fprintf(stderr, "Invalid pattern: %s\n",

View File

@ -69,7 +69,7 @@ struct ref **get_remote_heads(int in, struct ref **list,
if (len != name_len + 41) {
if (server_capabilities)
free(server_capabilities);
server_capabilities = strdup(name + name_len + 1);
server_capabilities = xstrdup(name + name_len + 1);
}
if (!check_ref(name, name_len, flags))
@ -661,7 +661,7 @@ int git_connect(int fd[2], char *url, const char *prog)
if (path[1] == '~')
path++;
else {
path = strdup(ptr);
path = xstrdup(ptr);
free_path = 1;
}
@ -672,7 +672,7 @@ int git_connect(int fd[2], char *url, const char *prog)
/* These underlying connection commands die() if they
* cannot connect.
*/
char *target_host = strdup(host);
char *target_host = xstrdup(host);
if (git_use_proxy(host))
git_proxy_connect(fd, host);
else

4
diff.c
View File

@ -216,7 +216,7 @@ static char *quote_one(const char *str)
return NULL;
needlen = quote_c_style(str, NULL, NULL, 0);
if (!needlen)
return strdup(str);
return xstrdup(str);
xp = xmalloc(needlen + 1);
quote_c_style(str, xp, NULL, 0);
return xp;
@ -658,7 +658,7 @@ static struct diffstat_file *diffstat_add(struct diffstat_t *diffstat,
x->is_renamed = 1;
}
else
x->name = strdup(name_a);
x->name = xstrdup(name_a);
return x;
}

View File

@ -47,7 +47,7 @@ static void setup_git_env(void)
}
git_graft_file = getenv(GRAFT_ENVIRONMENT);
if (!git_graft_file)
git_graft_file = strdup(git_path("info/grafts"));
git_graft_file = xstrdup(git_path("info/grafts"));
}
const char *get_git_dir(void)

View File

@ -234,8 +234,8 @@ int pull_targets_stdin(char ***target, const char ***write_ref)
*target = xrealloc(*target, targets_alloc * sizeof(**target));
*write_ref = xrealloc(*write_ref, targets_alloc * sizeof(**write_ref));
}
(*target)[targets] = strdup(tg_one);
(*write_ref)[targets] = rf_one ? strdup(rf_one) : NULL;
(*target)[targets] = xstrdup(tg_one);
(*write_ref)[targets] = rf_one ? xstrdup(rf_one) : NULL;
targets++;
}
return targets;

View File

@ -458,7 +458,7 @@ static void fsck_object_dir(const char *path)
static int fsck_head_link(void)
{
unsigned char sha1[20];
const char *git_HEAD = strdup(git_path("HEAD"));
const char *git_HEAD = xstrdup(git_path("HEAD"));
const char *git_refs_heads_master = resolve_ref(git_HEAD, sha1, 1);
int pfxlen = strlen(git_HEAD) - 4; /* strip .../.git/ part */

View File

@ -84,6 +84,14 @@ extern char *gitstrcasestr(const char *haystack, const char *needle);
extern size_t gitstrlcpy(char *, const char *, size_t);
#endif
static inline char* xstrdup(const char *str)
{
char *ret = strdup(str);
if (!ret)
die("Out of memory, strdup failed");
return ret;
}
static inline void *xmalloc(size_t size)
{
void *ret = malloc(size);

4
git.c
View File

@ -29,7 +29,7 @@ static void prepend_to_path(const char *dir, int len)
path_len = len + strlen(old_path) + 1;
path = malloc(path_len + 1);
path = xmalloc(path_len + 1);
memcpy(path, dir, len);
path[len] = ':';
@ -97,7 +97,7 @@ static char *alias_string;
static int git_alias_config(const char *var, const char *value)
{
if (!strncmp(var, "alias.", 6) && !strcmp(var + 6, alias_command)) {
alias_string = strdup(value);
alias_string = xstrdup(value);
}
return 0;
}

682
gitk
View File

@ -2,7 +2,7 @@
# Tcl ignores the next line -*- tcl -*- \
exec wish "$0" -- "$@"
# Copyright (C) 2005 Paul Mackerras. All rights reserved.
# Copyright (C) 2005-2006 Paul Mackerras. All rights reserved.
# This program is free software; it may be used, copied, modified
# and distributed under the terms of the GNU General Public Licence,
# either version 2, or (at your option) any later version.
@ -17,13 +17,12 @@ proc gitdir {} {
}
proc start_rev_list {view} {
global startmsecs nextupdate ncmupdate
global startmsecs nextupdate
global commfd leftover tclencoding datemode
global viewargs viewfiles commitidx
set startmsecs [clock clicks -milliseconds]
set nextupdate [expr {$startmsecs + 100}]
set ncmupdate 1
set commitidx($view) 0
set args $viewargs($view)
if {$viewfiles($view) ne {}} {
@ -79,7 +78,7 @@ proc getcommitlines {fd view} {
global parentlist childlist children curview hlview
global vparentlist vchildlist vdisporder vcmitlisted
set stuff [read $fd]
set stuff [read $fd 500000]
if {$stuff == {}} {
if {![eof $fd]} return
global viewname
@ -185,7 +184,7 @@ proc getcommitlines {fd view} {
}
if {$gotsome} {
if {$view == $curview} {
layoutmore
while {[layoutmore $nextupdate]} doupdate
} elseif {[info exists hlview] && $view == $hlview} {
vhighlightmore
}
@ -196,20 +195,13 @@ proc getcommitlines {fd view} {
}
proc doupdate {} {
global commfd nextupdate numcommits ncmupdate
global commfd nextupdate numcommits
foreach v [array names commfd] {
fileevent $commfd($v) readable {}
}
update
set nextupdate [expr {[clock clicks -milliseconds] + 100}]
if {$numcommits < 100} {
set ncmupdate [expr {$numcommits + 1}]
} elseif {$numcommits < 10000} {
set ncmupdate [expr {$numcommits + 10}]
} else {
set ncmupdate [expr {$numcommits + 100}]
}
foreach v [array names commfd] {
set fd $commfd($v)
fileevent $fd readable [list getcommitlines $fd $v]
@ -341,13 +333,13 @@ proc readrefs {} {
set tag {}
catch {
set commit [exec git rev-parse "$id^0"]
if {"$commit" != "$id"} {
if {$commit != $id} {
set tagids($name) $commit
lappend idtags($commit) $name
}
}
catch {
set tagcontents($name) [exec git cat-file tag "$id"]
set tagcontents($name) [exec git cat-file tag $id]
}
} elseif { $type == "heads" } {
set headids($name) $id
@ -384,6 +376,23 @@ proc error_popup msg {
show_error $w $w $msg
}
proc confirm_popup msg {
global confirm_ok
set confirm_ok 0
set w .confirm
toplevel $w
wm transient $w .
message $w.m -text $msg -justify center -aspect 400
pack $w.m -side top -fill x -padx 20 -pady 20
button $w.ok -text OK -command "set confirm_ok 1; destroy $w"
pack $w.ok -side left -fill x
button $w.cancel -text Cancel -command "destroy $w"
pack $w.cancel -side right -fill x
bind $w <Visibility> "grab $w; focus $w"
tkwait window $w
return $confirm_ok
}
proc makewindow {} {
global canv canv2 canv3 linespc charspc ctext cflist
global textfont mainfont uifont
@ -394,6 +403,7 @@ proc makewindow {} {
global highlight_files gdttype
global searchstring sstring
global bgcolor fgcolor bglist fglist diffcolors
global headctxmenu
menu .bar
.bar add cascade -label "File" -menu .bar.file
@ -711,6 +721,16 @@ proc makewindow {} {
$rowctxmenu add command -label "Make patch" -command mkpatch
$rowctxmenu add command -label "Create tag" -command mktag
$rowctxmenu add command -label "Write commit to file" -command writecommit
$rowctxmenu add command -label "Create new branch" -command mkbranch
$rowctxmenu add command -label "Cherry-pick this commit" \
-command cherrypick
set headctxmenu .headctxmenu
menu $headctxmenu -tearoff 0
$headctxmenu add command -label "Check out this branch" \
-command cobranch
$headctxmenu add command -label "Remove this branch" \
-command rmbranch
}
# mouse-2 makes all windows scan vertically, but only the one
@ -1669,7 +1689,7 @@ proc showview {n} {
show_status "Reading commits..."
}
if {[info exists commfd($n)]} {
layoutmore
layoutmore {}
} else {
finishcommits
}
@ -2350,20 +2370,38 @@ proc visiblerows {} {
return [list $r0 $r1]
}
proc layoutmore {} {
proc layoutmore {tmax} {
global rowlaidout rowoptim commitidx numcommits optim_delay
global uparrowlen curview
set row $rowlaidout
set rowlaidout [layoutrows $row $commitidx($curview) 0]
set orow [expr {$rowlaidout - $uparrowlen - 1}]
if {$orow > $rowoptim} {
optimize_rows $rowoptim 0 $orow
set rowoptim $orow
}
set canshow [expr {$rowoptim - $optim_delay}]
if {$canshow > $numcommits} {
showstuff $canshow
while {1} {
if {$rowoptim - $optim_delay > $numcommits} {
showstuff [expr {$rowoptim - $optim_delay}]
} elseif {$rowlaidout - $uparrowlen - 1 > $rowoptim} {
set nr [expr {$rowlaidout - $uparrowlen - 1 - $rowoptim}]
if {$nr > 100} {
set nr 100
}
optimize_rows $rowoptim 0 [expr {$rowoptim + $nr}]
incr rowoptim $nr
} elseif {$commitidx($curview) > $rowlaidout} {
set nr [expr {$commitidx($curview) - $rowlaidout}]
# may need to increase this threshold if uparrowlen or
# mingaplen are increased...
if {$nr > 150} {
set nr 150
}
set row $rowlaidout
set rowlaidout [layoutrows $row [expr {$row + $nr}] 0]
if {$rowlaidout == $row} {
return 0
}
} else {
return 0
}
if {$tmax ne {} && [clock clicks -milliseconds] >= $tmax} {
return 1
}
}
}
@ -3236,6 +3274,8 @@ proc drawtags {id x xt y1} {
-font $font -tags [list tag.$id text]]
if {$ntags >= 0} {
$canv bind $t <1> [list showtag $tag 1]
} elseif {$nheads >= 0} {
$canv bind $t <Button-3> [list headmenu %X %Y $id $tag]
}
}
return $xt
@ -3263,8 +3303,7 @@ proc show_status {msg} {
proc finishcommits {} {
global commitidx phase curview
global canv mainfont ctext maincursor textcursor
global findinprogress pending_select
global pending_select
if {$commitidx($curview) > 0} {
drawrest
@ -3275,6 +3314,108 @@ proc finishcommits {} {
catch {unset pending_select}
}
# Insert a new commit as the child of the commit on row $row.
# The new commit will be displayed on row $row and the commits
# on that row and below will move down one row.
proc insertrow {row newcmit} {
global displayorder parentlist childlist commitlisted
global commitrow curview rowidlist rowoffsets numcommits
global rowrangelist idrowranges rowlaidout rowoptim numcommits
global linesegends selectedline
if {$row >= $numcommits} {
puts "oops, inserting new row $row but only have $numcommits rows"
return
}
set p [lindex $displayorder $row]
set displayorder [linsert $displayorder $row $newcmit]
set parentlist [linsert $parentlist $row $p]
set kids [lindex $childlist $row]
lappend kids $newcmit
lset childlist $row $kids
set childlist [linsert $childlist $row {}]
set commitlisted [linsert $commitlisted $row 1]
set l [llength $displayorder]
for {set r $row} {$r < $l} {incr r} {
set id [lindex $displayorder $r]
set commitrow($curview,$id) $r
}
set idlist [lindex $rowidlist $row]
set offs [lindex $rowoffsets $row]
set newoffs {}
foreach x $idlist {
if {$x eq {} || ($x eq $p && [llength $kids] == 1)} {
lappend newoffs {}
} else {
lappend newoffs 0
}
}
if {[llength $kids] == 1} {
set col [lsearch -exact $idlist $p]
lset idlist $col $newcmit
} else {
set col [llength $idlist]
lappend idlist $newcmit
lappend offs {}
lset rowoffsets $row $offs
}
set rowidlist [linsert $rowidlist $row $idlist]
set rowoffsets [linsert $rowoffsets [expr {$row+1}] $newoffs]
set rowrangelist [linsert $rowrangelist $row {}]
set l [llength $rowrangelist]
for {set r 0} {$r < $l} {incr r} {
set ranges [lindex $rowrangelist $r]
if {$ranges ne {} && [lindex $ranges end] >= $row} {
set newranges {}
foreach x $ranges {
if {$x >= $row} {
lappend newranges [expr {$x + 1}]
} else {
lappend newranges $x
}
}
lset rowrangelist $r $newranges
}
}
if {[llength $kids] > 1} {
set rp1 [expr {$row + 1}]
set ranges [lindex $rowrangelist $rp1]
if {$ranges eq {}} {
set ranges [list $row $rp1]
} elseif {[lindex $ranges end-1] == $rp1} {
lset ranges end-1 $row
}
lset rowrangelist $rp1 $ranges
}
foreach id [array names idrowranges] {
set ranges $idrowranges($id)
if {$ranges ne {} && [lindex $ranges end] >= $row} {
set newranges {}
foreach x $ranges {
if {$x >= $row} {
lappend newranges [expr {$x + 1}]
} else {
lappend newranges $x
}
}
set idrowranges($id) $newranges
}
}
set linesegends [linsert $linesegends $row {}]
incr rowlaidout
incr rowoptim
incr numcommits
if {[info exists selectedline] && $selectedline >= $row} {
incr selectedline
}
redisplay
}
# Don't change the text pane cursor if it is currently the hand cursor,
# showing that we are over a sha1 ID link.
proc settextcursor {c} {
@ -3307,9 +3448,7 @@ proc notbusy {what} {
}
proc drawrest {} {
global numcommits
global startmsecs
global canvy0 numcommits linespc
global rowlaidout commitidx curview
global pending_select
@ -3323,6 +3462,7 @@ proc drawrest {} {
}
set drawmsecs [expr {[clock clicks -milliseconds] - $startmsecs}]
#global numcommits
#puts "overall $drawmsecs ms for $numcommits commits"
}
@ -3603,27 +3743,20 @@ proc viewnextline {dir} {
# add a list of tag or branch names at position pos
# returns the number of names inserted
proc appendrefs {pos l var} {
global ctext commitrow linknum curview idtags $var
proc appendrefs {pos tags var} {
global ctext commitrow linknum curview $var
if {[catch {$ctext index $pos}]} {
return 0
}
set tags {}
foreach id $l {
foreach tag [set $var\($id\)] {
lappend tags [concat $tag $id]
}
}
set tags [lsort -index 1 $tags]
set tags [lsort $tags]
set sep {}
foreach tag $tags {
set name [lindex $tag 0]
set id [lindex $tag 1]
set id [set $var\($tag\)]
set lk link$linknum
incr linknum
$ctext insert $pos $sep
$ctext insert $pos $name $lk
$ctext insert $pos $tag $lk
$ctext tag conf $lk -foreground blue
if {[info exists commitrow($curview,$id)]} {
$ctext tag bind $lk <1> \
@ -3637,6 +3770,18 @@ proc appendrefs {pos l var} {
return [llength $tags]
}
proc taglist {ids} {
global idtags
set tags {}
foreach id $ids {
foreach tag $idtags($id) {
lappend tags $tag
}
}
return $tags
}
# called when we have finished computing the nearby tags
proc dispneartags {} {
global selectedline currentid ctext anc_tags desc_tags showneartags
@ -3646,15 +3791,15 @@ proc dispneartags {} {
set id $currentid
$ctext conf -state normal
if {[info exists desc_heads($id)]} {
if {[appendrefs branch $desc_heads($id) idheads] > 1} {
if {[appendrefs branch $desc_heads($id) headids] > 1} {
$ctext insert "branch -2c" "es"
}
}
if {[info exists anc_tags($id)]} {
appendrefs follows $anc_tags($id) idtags
appendrefs follows [taglist $anc_tags($id)] tagids
}
if {[info exists desc_tags($id)]} {
appendrefs precedes $desc_tags($id) idtags
appendrefs precedes [taglist $desc_tags($id)] tagids
}
$ctext conf -state disabled
}
@ -3787,7 +3932,7 @@ proc selectline {l isnew} {
$ctext mark set branch "end -1c"
$ctext mark gravity branch left
if {[info exists desc_heads($id)]} {
if {[appendrefs branch $desc_heads($id) idheads] > 1} {
if {[appendrefs branch $desc_heads($id) headids] > 1} {
# turn "Branch" into "Branches"
$ctext insert "branch -2c" "es"
}
@ -3796,13 +3941,13 @@ proc selectline {l isnew} {
$ctext mark set follows "end -1c"
$ctext mark gravity follows left
if {[info exists anc_tags($id)]} {
appendrefs follows $anc_tags($id) idtags
appendrefs follows [taglist $anc_tags($id)] tagids
}
$ctext insert end "\nPrecedes: "
$ctext mark set precedes "end -1c"
$ctext mark gravity precedes left
if {[info exists desc_tags($id)]} {
appendrefs precedes $desc_tags($id) idtags
appendrefs precedes [taglist $desc_tags($id)] tagids
}
$ctext insert end "\n"
}
@ -4463,6 +4608,7 @@ proc redisplay {} {
drawvisible
if {[info exists selectedline]} {
selectline $selectedline 0
allcanvs yview moveto [lindex $span 0]
}
}
@ -4930,6 +5076,7 @@ proc domktag {} {
set tagids($tag) $id
lappend idtags($id) $tag
redrawtags $id
addedtag $id
}
proc redrawtags {id} {
@ -5020,10 +5167,164 @@ proc wrcomcan {} {
unset wrcomtop
}
proc mkbranch {} {
global rowmenuid mkbrtop
set top .makebranch
catch {destroy $top}
toplevel $top
label $top.title -text "Create new branch"
grid $top.title - -pady 10
label $top.id -text "ID:"
entry $top.sha1 -width 40 -relief flat
$top.sha1 insert 0 $rowmenuid
$top.sha1 conf -state readonly
grid $top.id $top.sha1 -sticky w
label $top.nlab -text "Name:"
entry $top.name -width 40
grid $top.nlab $top.name -sticky w
frame $top.buts
button $top.buts.go -text "Create" -command [list mkbrgo $top]
button $top.buts.can -text "Cancel" -command "catch {destroy $top}"
grid $top.buts.go $top.buts.can
grid columnconfigure $top.buts 0 -weight 1 -uniform a
grid columnconfigure $top.buts 1 -weight 1 -uniform a
grid $top.buts - -pady 10 -sticky ew
focus $top.name
}
proc mkbrgo {top} {
global headids idheads
set name [$top.name get]
set id [$top.sha1 get]
if {$name eq {}} {
error_popup "Please specify a name for the new branch"
return
}
catch {destroy $top}
nowbusy newbranch
update
if {[catch {
exec git branch $name $id
} err]} {
notbusy newbranch
error_popup $err
} else {
addedhead $id $name
# XXX should update list of heads displayed for selected commit
notbusy newbranch
redrawtags $id
}
}
proc cherrypick {} {
global rowmenuid curview commitrow
global mainhead desc_heads anc_tags desc_tags allparents allchildren
if {[info exists desc_heads($rowmenuid)]
&& [lsearch -exact $desc_heads($rowmenuid) $mainhead] >= 0} {
set ok [confirm_popup "Commit [string range $rowmenuid 0 7] is already\
included in branch $mainhead -- really re-apply it?"]
if {!$ok} return
}
nowbusy cherrypick
update
set oldhead [exec git rev-parse HEAD]
# Unfortunately git-cherry-pick writes stuff to stderr even when
# no error occurs, and exec takes that as an indication of error...
if {[catch {exec sh -c "git cherry-pick -r $rowmenuid 2>&1"} err]} {
notbusy cherrypick
error_popup $err
return
}
set newhead [exec git rev-parse HEAD]
if {$newhead eq $oldhead} {
notbusy cherrypick
error_popup "No changes committed"
return
}
set allparents($newhead) $oldhead
lappend allchildren($oldhead) $newhead
set desc_heads($newhead) $mainhead
if {[info exists anc_tags($oldhead)]} {
set anc_tags($newhead) $anc_tags($oldhead)
}
set desc_tags($newhead) {}
if {[info exists commitrow($curview,$oldhead)]} {
insertrow $commitrow($curview,$oldhead) $newhead
if {$mainhead ne {}} {
movedhead $newhead $mainhead
}
redrawtags $oldhead
redrawtags $newhead
}
notbusy cherrypick
}
# context menu for a head
proc headmenu {x y id head} {
global headmenuid headmenuhead headctxmenu
set headmenuid $id
set headmenuhead $head
tk_popup $headctxmenu $x $y
}
proc cobranch {} {
global headmenuid headmenuhead mainhead headids
# check the tree is clean first??
set oldmainhead $mainhead
nowbusy checkout
update
if {[catch {
exec git checkout $headmenuhead
} err]} {
notbusy checkout
error_popup $err
} else {
notbusy checkout
set mainhead $headmenuhead
if {[info exists headids($oldmainhead)]} {
redrawtags $headids($oldmainhead)
}
redrawtags $headmenuid
}
}
proc rmbranch {} {
global desc_heads headmenuid headmenuhead mainhead
global headids idheads
set head $headmenuhead
set id $headmenuid
if {$head eq $mainhead} {
error_popup "Cannot delete the currently checked-out branch"
return
}
if {$desc_heads($id) eq $head} {
# the stuff on this branch isn't on any other branch
if {![confirm_popup "The commits on branch $head aren't on any other\
branch.\nReally delete branch $head?"]} return
}
nowbusy rmbranch
update
if {[catch {exec git branch -D $head} err]} {
notbusy rmbranch
error_popup $err
return
}
removedhead $id $head
redrawtags $id
notbusy rmbranch
}
# Stuff for finding nearby tags
proc getallcommits {} {
global allcstart allcommits allcfd
global allcstart allcommits allcfd allids
set allids {}
set fd [open [concat | git rev-list --all --topo-order --parents] r]
set allcfd $fd
fconfigure $fd -blocking 0
@ -5107,10 +5408,52 @@ proc combine_atags {l1 l2} {
return $res
}
proc forward_pass {id children} {
global idtags desc_tags idheads desc_heads alldtags tagisdesc
set dtags {}
set dheads {}
foreach child $children {
if {[info exists idtags($child)]} {
set ctags [list $child]
} else {
set ctags $desc_tags($child)
}
if {$dtags eq {}} {
set dtags $ctags
} elseif {$ctags ne $dtags} {
set dtags [combine_dtags $dtags $ctags]
}
set cheads $desc_heads($child)
if {$dheads eq {}} {
set dheads $cheads
} elseif {$cheads ne $dheads} {
set dheads [lsort -unique [concat $dheads $cheads]]
}
}
set desc_tags($id) $dtags
if {[info exists idtags($id)]} {
set adt $dtags
foreach tag $dtags {
set adt [concat $adt $alldtags($tag)]
}
set adt [lsort -unique $adt]
set alldtags($id) $adt
foreach tag $adt {
set tagisdesc($id,$tag) -1
set tagisdesc($tag,$id) 1
}
}
if {[info exists idheads($id)]} {
set dheads [concat $dheads $idheads($id)]
}
set desc_heads($id) $dheads
}
proc getallclines {fd} {
global allparents allchildren allcommits allcstart
global desc_tags anc_tags idtags alldtags tagisdesc allids
global desc_heads idheads
global desc_tags anc_tags idtags tagisdesc allids
global idheads travindex
while {[gets $fd line] >= 0} {
set id [lindex $line 0]
@ -5125,43 +5468,7 @@ proc getallclines {fd} {
}
# compute nearest tagged descendents as we go
# also compute descendent heads
set dtags {}
set dheads {}
foreach child $allchildren($id) {
if {[info exists idtags($child)]} {
set ctags [list $child]
} else {
set ctags $desc_tags($child)
}
if {$dtags eq {}} {
set dtags $ctags
} elseif {$ctags ne $dtags} {
set dtags [combine_dtags $dtags $ctags]
}
set cheads $desc_heads($child)
if {$dheads eq {}} {
set dheads $cheads
} elseif {$cheads ne $dheads} {
set dheads [lsort -unique [concat $dheads $cheads]]
}
}
set desc_tags($id) $dtags
if {[info exists idtags($id)]} {
set adt $dtags
foreach tag $dtags {
set adt [concat $adt $alldtags($tag)]
}
set adt [lsort -unique $adt]
set alldtags($id) $adt
foreach tag $adt {
set tagisdesc($id,$tag) -1
set tagisdesc($tag,$id) 1
}
}
if {[info exists idheads($id)]} {
lappend dheads $id
}
set desc_heads($id) $dheads
forward_pass $id $allchildren($id)
if {[clock clicks -milliseconds] - $allcstart >= 50} {
fileevent $fd readable {}
after idle restartgetall $fd
@ -5169,7 +5476,9 @@ proc getallclines {fd} {
}
}
if {[eof $fd]} {
after idle restartatags [llength $allids]
set travindex [llength $allids]
set allcommits "traversing"
after idle restartatags
if {[catch {close $fd} err]} {
error_popup "Error reading full commit graph: $err.\n\
Results may be incomplete."
@ -5178,10 +5487,11 @@ proc getallclines {fd} {
}
# walk backward through the tree and compute nearest tagged ancestors
proc restartatags {i} {
global allids allparents idtags anc_tags t0
proc restartatags {} {
global allids allparents idtags anc_tags travindex
set t0 [clock clicks -milliseconds]
set i $travindex
while {[incr i -1] >= 0} {
set id [lindex $allids $i]
set atags {}
@ -5199,17 +5509,195 @@ proc restartatags {i} {
}
set anc_tags($id) $atags
if {[clock clicks -milliseconds] - $t0 >= 50} {
after idle restartatags $i
set travindex $i
after idle restartatags
return
}
}
set allcommits "done"
set travindex 0
notbusy allcommits
dispneartags
}
# update the desc_tags and anc_tags arrays for a new tag just added
proc addedtag {id} {
global desc_tags anc_tags allparents allchildren allcommits
global idtags tagisdesc alldtags
if {![info exists desc_tags($id)]} return
set adt $desc_tags($id)
foreach t $desc_tags($id) {
set adt [concat $adt $alldtags($t)]
}
set adt [lsort -unique $adt]
set alldtags($id) $adt
foreach t $adt {
set tagisdesc($id,$t) -1
set tagisdesc($t,$id) 1
}
if {[info exists anc_tags($id)]} {
set todo $anc_tags($id)
while {$todo ne {}} {
set do [lindex $todo 0]
set todo [lrange $todo 1 end]
if {[info exists tagisdesc($id,$do)]} continue
set tagisdesc($do,$id) -1
set tagisdesc($id,$do) 1
if {[info exists anc_tags($do)]} {
set todo [concat $todo $anc_tags($do)]
}
}
}
set lastold $desc_tags($id)
set lastnew [list $id]
set nup 0
set nch 0
set todo $allparents($id)
while {$todo ne {}} {
set do [lindex $todo 0]
set todo [lrange $todo 1 end]
if {![info exists desc_tags($do)]} continue
if {$desc_tags($do) ne $lastold} {
set lastold $desc_tags($do)
set lastnew [combine_dtags $lastold [list $id]]
incr nch
}
if {$lastold eq $lastnew} continue
set desc_tags($do) $lastnew
incr nup
if {![info exists idtags($do)]} {
set todo [concat $todo $allparents($do)]
}
}
if {![info exists anc_tags($id)]} return
set lastold $anc_tags($id)
set lastnew [list $id]
set nup 0
set nch 0
set todo $allchildren($id)
while {$todo ne {}} {
set do [lindex $todo 0]
set todo [lrange $todo 1 end]
if {![info exists anc_tags($do)]} continue
if {$anc_tags($do) ne $lastold} {
set lastold $anc_tags($do)
set lastnew [combine_atags $lastold [list $id]]
incr nch
}
if {$lastold eq $lastnew} continue
set anc_tags($do) $lastnew
incr nup
if {![info exists idtags($do)]} {
set todo [concat $todo $allchildren($do)]
}
}
}
# update the desc_heads array for a new head just added
proc addedhead {hid head} {
global desc_heads allparents headids idheads
set headids($head) $hid
lappend idheads($hid) $head
set todo [list $hid]
while {$todo ne {}} {
set do [lindex $todo 0]
set todo [lrange $todo 1 end]
if {![info exists desc_heads($do)] ||
[lsearch -exact $desc_heads($do) $head] >= 0} continue
set oldheads $desc_heads($do)
lappend desc_heads($do) $head
set heads $desc_heads($do)
while {1} {
set p $allparents($do)
if {[llength $p] != 1 || ![info exists desc_heads($p)] ||
$desc_heads($p) ne $oldheads} break
set do $p
set desc_heads($do) $heads
}
set todo [concat $todo $p]
}
}
# update the desc_heads array for a head just removed
proc removedhead {hid head} {
global desc_heads allparents headids idheads
unset headids($head)
if {$idheads($hid) eq $head} {
unset idheads($hid)
} else {
set i [lsearch -exact $idheads($hid) $head]
if {$i >= 0} {
set idheads($hid) [lreplace $idheads($hid) $i $i]
}
}
set todo [list $hid]
while {$todo ne {}} {
set do [lindex $todo 0]
set todo [lrange $todo 1 end]
if {![info exists desc_heads($do)]} continue
set i [lsearch -exact $desc_heads($do) $head]
if {$i < 0} continue
set oldheads $desc_heads($do)
set heads [lreplace $desc_heads($do) $i $i]
while {1} {
set desc_heads($do) $heads
set p $allparents($do)
if {[llength $p] != 1 || ![info exists desc_heads($p)] ||
$desc_heads($p) ne $oldheads} break
set do $p
}
set todo [concat $todo $p]
}
}
# update things for a head moved to a child of its previous location
proc movedhead {id name} {
global headids idheads
set oldid $headids($name)
set headids($name) $id
if {$idheads($oldid) eq $name} {
unset idheads($oldid)
} else {
set i [lsearch -exact $idheads($oldid) $name]
if {$i >= 0} {
set idheads($oldid) [lreplace $idheads($oldid) $i $i]
}
}
lappend idheads($id) $name
}
proc changedrefs {} {
global desc_heads desc_tags anc_tags allcommits allids
global allchildren allparents idtags travindex
if {![info exists allcommits]} return
catch {unset desc_heads}
catch {unset desc_tags}
catch {unset anc_tags}
catch {unset alldtags}
catch {unset tagisdesc}
foreach id $allids {
forward_pass $id $allchildren($id)
}
if {$allcommits ne "reading"} {
set travindex [llength $allids]
if {$allcommits ne "traversing"} {
set allcommits "traversing"
after idle restartatags
}
}
}
proc rereadrefs {} {
global idtags idheads idotherrefs
global idtags idheads idotherrefs mainhead
set refids [concat [array names idtags] \
[array names idheads] [array names idotherrefs]]
@ -5218,12 +5706,16 @@ proc rereadrefs {} {
set ref($id) [listrefs $id]
}
}
set oldmainhead $mainhead
readrefs
changedrefs
set refids [lsort -unique [concat $refids [array names idtags] \
[array names idheads] [array names idotherrefs]]]
foreach id $refids {
set v [listrefs $id]
if {![info exists ref($id)] || $ref($id) != $v} {
if {![info exists ref($id)] || $ref($id) != $v ||
($id eq $oldmainhead && $id ne $mainhead) ||
($id eq $mainhead && $id ne $oldmainhead)} {
redrawtags $id
}
}

View File

@ -2251,7 +2251,8 @@ sub git_blame2 {
my $fd;
my $ftype;
if (!gitweb_check_feature('blame')) {
my ($have_blame) = gitweb_check_feature('blame');
if (!$have_blame) {
die_error('403 Permission denied', "Permission denied");
}
die_error('404 Not Found', "File name not defined") if (!$file_name);
@ -2320,7 +2321,8 @@ HTML
sub git_blame {
my $fd;
if (!gitweb_check_feature('blame')) {
my ($have_blame) = gitweb_check_feature('blame');
if (!$have_blame) {
die_error('403 Permission denied', "Permission denied");
}
die_error('404 Not Found', "File name not defined") if (!$file_name);
@ -2494,7 +2496,7 @@ sub git_blob {
die_error(undef, "No file name defined");
}
}
my $have_blame = gitweb_check_feature('blame');
my ($have_blame) = gitweb_check_feature('blame');
open my $fd, "-|", git_cmd(), "cat-file", "blob", $hash
or die_error(undef, "Couldn't cat $file_name, $hash");
my $mimetype = blob_mimetype($fd, $file_name);
@ -2570,7 +2572,7 @@ sub git_tree {
my $ref = format_ref_marker($refs, $hash_base);
git_header_html();
my $base = "";
my $have_blame = gitweb_check_feature('blame');
my ($have_blame) = gitweb_check_feature('blame');
if (defined $hash_base && (my %co = parse_commit($hash_base))) {
git_print_page_nav('tree','', $hash_base);
git_print_header_div('commit', esc_html($co{'title'}) . $ref, $hash_base);

2
help.c
View File

@ -184,7 +184,7 @@ static void show_man_page(const char *git_cmd)
page = git_cmd;
else {
int page_len = strlen(git_cmd) + 4;
char *p = malloc(page_len + 1);
char *p = xmalloc(page_len + 1);
strcpy(p, "git-");
strcpy(p + 4, git_cmd);
p[page_len] = 0;

View File

@ -787,7 +787,7 @@ static int remote_ls(struct alt_base *repo, const char *path, int flags,
ls.flags = flags;
ls.repo = repo;
ls.path = strdup(path);
ls.path = xstrdup(path);
ls.dentry_name = NULL;
ls.dentry_flags = 0;
ls.userData = userData;

View File

@ -1539,7 +1539,7 @@ static void remote_ls(const char *path, int flags,
struct remote_ls_ctx ls;
ls.flags = flags;
ls.path = strdup(path);
ls.path = xstrdup(path);
ls.dentry_name = NULL;
ls.dentry_flags = 0;
ls.userData = userData;
@ -1738,7 +1738,7 @@ static struct object_list **process_tree(struct tree *tree,
die("bad tree object %s", sha1_to_hex(obj->sha1));
obj->flags |= SEEN;
name = strdup(name);
name = xstrdup(name);
p = add_one_object(obj, p);
me.up = path;
me.elem = name;
@ -2467,7 +2467,7 @@ int main(int argc, char **argv)
/* Set up revision info for this refspec */
commit_argc = 3;
new_sha1_hex = strdup(sha1_to_hex(ref->new_sha1));
new_sha1_hex = xstrdup(sha1_to_hex(ref->new_sha1));
old_sha1_hex = NULL;
commit_argv[1] = "--objects";
commit_argv[2] = new_sha1_hex;

View File

@ -1007,7 +1007,7 @@ imap_open_store( imap_server_conf_t *srvc )
* getpass() returns a pointer to a static buffer. make a copy
* for long term storage.
*/
srvc->pass = strdup( arg );
srvc->pass = xstrdup( arg );
}
if (CAP(NOLOGIN)) {
fprintf( stderr, "Skipping account %s@%s, server forbids LOGIN\n", srvc->user, srvc->host );
@ -1263,7 +1263,7 @@ git_imap_config(const char *key, const char *val)
key += sizeof imap_key - 1;
if (!strcmp( "folder", key )) {
imap_folder = strdup( val );
imap_folder = xstrdup( val );
} else if (!strcmp( "host", key )) {
{
if (!strncmp( "imap:", val, 5 ))
@ -1273,16 +1273,16 @@ git_imap_config(const char *key, const char *val)
}
if (!strncmp( "//", val, 2 ))
val += 2;
server.host = strdup( val );
server.host = xstrdup( val );
}
else if (!strcmp( "user", key ))
server.user = strdup( val );
server.user = xstrdup( val );
else if (!strcmp( "pass", key ))
server.pass = strdup( val );
server.pass = xstrdup( val );
else if (!strcmp( "port", key ))
server.port = git_config_int( key, val );
else if (!strcmp( "tunnel", key ))
server.tunnel = strdup( val );
server.tunnel = xstrdup( val );
return 0;
}

View File

@ -21,7 +21,7 @@ static const char *write_temp_file(mmfile_t *f)
fd = mkstemp(filename);
if (fd < 0)
return NULL;
filename = strdup(filename);
filename = xstrdup(filename);
if (f->size != xwrite(fd, f->ptr, f->size)) {
rm_temp_file(filename);
return NULL;

View File

@ -283,7 +283,7 @@ static int save_files_dirs(const unsigned char *sha1,
unsigned int mode, int stage)
{
int len = strlen(path);
char *newpath = malloc(baselen + len + 1);
char *newpath = xmalloc(baselen + len + 1);
memcpy(newpath, base, baselen);
memcpy(newpath + baselen, path, len);
newpath[baselen + len] = '\0';
@ -455,7 +455,7 @@ static int remove_path(const char *name)
if (ret)
return ret;
len = strlen(name);
dirs = malloc(len+1);
dirs = xmalloc(len+1);
memcpy(dirs, name, len);
dirs[len] = '\0';
while ((slash = strrchr(name, '/'))) {
@ -513,8 +513,8 @@ static char *unique_path(const char *path, const char *branch)
static int mkdir_p(const char *path, unsigned long mode)
{
/* path points to cache entries, so strdup before messing with it */
char *buf = strdup(path);
/* path points to cache entries, so xstrdup before messing with it */
char *buf = xstrdup(path);
int result = safe_create_leading_directories(buf);
free(buf);
return result;
@ -572,7 +572,7 @@ void update_file_flags(const unsigned char *sha,
flush_buffer(fd, buf, size);
close(fd);
} else if (S_ISLNK(mode)) {
char *lnk = malloc(size + 1);
char *lnk = xmalloc(size + 1);
memcpy(lnk, buf, size);
lnk[size] = '\0';
mkdir_p(path, 0777);
@ -668,9 +668,9 @@ static struct merge_file_info merge_file(struct diff_filespec *o,
git_unpack_file(a->sha1, src1);
git_unpack_file(b->sha1, src2);
argv[2] = la = strdup(mkpath("%s/%s", branch1, a->path));
argv[6] = lb = strdup(mkpath("%s/%s", branch2, b->path));
argv[4] = lo = strdup(mkpath("orig/%s", o->path));
argv[2] = la = xstrdup(mkpath("%s/%s", branch1, a->path));
argv[6] = lb = xstrdup(mkpath("%s/%s", branch2, b->path));
argv[4] = lo = xstrdup(mkpath("orig/%s", o->path));
argv[7] = src1;
argv[8] = orig;
argv[9] = src2,
@ -1314,9 +1314,9 @@ int main(int argc, char *argv[])
original_index_file = getenv("GIT_INDEX_FILE");
if (!original_index_file)
original_index_file = strdup(git_path("index"));
original_index_file = xstrdup(git_path("index"));
temporary_index_file = strdup(git_path("mrg-rcrsv-tmp-idx"));
temporary_index_file = xstrdup(git_path("mrg-rcrsv-tmp-idx"));
if (argc < 4)
die("Usage: %s <base>... -- <head> <remote> ...\n", argv[0]);

View File

@ -177,7 +177,7 @@ static void resolve(const char *base, struct name_entry *branch1, struct name_en
if (!branch1)
return;
path = strdup(mkpath("%s%s", base, result->path));
path = xstrdup(mkpath("%s%s", base, result->path));
orig = create_entry(2, branch1->mode, branch1->sha1, path);
final = create_entry(0, result->mode, result->sha1, path);
@ -233,7 +233,7 @@ static struct merge_list *link_entry(unsigned stage, const char *base, struct na
if (entry)
path = entry->path;
else
path = strdup(mkpath("%s%s", base, n->path));
path = xstrdup(mkpath("%s%s", base, n->path));
link = create_entry(stage, n->mode, n->sha1, path);
link->link = entry;
return link;

View File

@ -119,7 +119,7 @@ static int verify_tag(char *buffer, unsigned long size)
int main(int argc, char **argv)
{
unsigned long size = 4096;
char *buffer = malloc(size);
char *buffer = xmalloc(size);
unsigned char result_sha1[20];
if (argc != 1)

View File

@ -45,7 +45,7 @@ static int add_entry(struct path_list *list, const char *path)
(list->nr - index)
* sizeof(struct path_list_item));
list->items[index].path = list->strdup_paths ?
strdup(path) : (char *)path;
xstrdup(path) : (char *)path;
list->items[index].util = NULL;
list->nr++;

4
refs.c
View File

@ -313,8 +313,8 @@ static struct ref_lock *lock_ref_sha1_basic(const char *path,
}
lock->lk = xcalloc(1, sizeof(struct lock_file));
lock->ref_file = strdup(path);
lock->log_file = strdup(git_path("logs/%s", lock->ref_file + plen));
lock->ref_file = xstrdup(path);
lock->log_file = xstrdup(git_path("logs/%s", lock->ref_file + plen));
lock->force_write = lstat(lock->ref_file, &st) && errno == ENOENT;
if (safe_create_leading_directories(lock->ref_file))

View File

@ -53,7 +53,7 @@ static void exec_rev_list(struct ref *refs)
if (900 < i)
die("git-rev-list environment overflow");
if (!is_zero_sha1(ref->new_sha1)) {
char *buf = malloc(100);
char *buf = xmalloc(100);
args[i++] = buf;
snprintf(buf, 50, "%s", sha1_to_hex(ref->new_sha1));
buf += 50;
@ -75,7 +75,7 @@ static void exec_rev_list(struct ref *refs)
if (is_zero_sha1(ref->new_sha1) &&
!is_zero_sha1(ref->old_sha1) &&
has_sha1_file(ref->old_sha1)) {
char *buf = malloc(42);
char *buf = xmalloc(42);
args[i++] = buf;
snprintf(buf, 42, "^%s", sha1_to_hex(ref->old_sha1));
}

View File

@ -23,7 +23,7 @@ static int add_info_ref(const char *path, const unsigned char *sha1)
static int update_info_refs(int force)
{
char *path0 = strdup(git_path("info/refs"));
char *path0 = xstrdup(git_path("info/refs"));
int len = strlen(path0);
char *path1 = xmalloc(len + 2);

View File

@ -115,7 +115,7 @@ static void fill_sha1_path(char *pathbuf, const unsigned char *sha1)
/*
* NOTE! This returns a statically allocated buffer, so you have to be
* careful about using it. Do a "strdup()" if you need to save the
* careful about using it. Do a "xstrdup()" if you need to save the
* filename.
*
* Also note that this returns the location for creating. Reading
@ -711,17 +711,39 @@ int legacy_loose_object(unsigned char *map)
return 0;
}
static unsigned long unpack_object_header_gently(const unsigned char *buf, unsigned long len, enum object_type *type, unsigned long *sizep)
{
unsigned shift;
unsigned char c;
unsigned long size;
unsigned long used = 0;
c = buf[used++];
*type = (c >> 4) & 7;
size = c & 15;
shift = 4;
while (c & 0x80) {
if (len <= used)
return 0;
if (sizeof(long) * 8 <= shift)
return 0;
c = buf[used++];
size += (c & 0x7f) << shift;
shift += 7;
}
*sizep = size;
return used;
}
static int unpack_sha1_header(z_stream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz)
{
unsigned char c;
unsigned int bits;
unsigned long size;
static const char *typename[8] = {
NULL, /* OBJ_EXT */
"commit", "tree", "blob", "tag",
NULL, NULL, NULL
unsigned long size, used;
static const char valid_loose_object_type[8] = {
0, /* OBJ_EXT */
1, 1, 1, 1, /* "commit", "tree", "blob", "tag" */
0, /* "delta" and others are invalid in a loose object */
};
const char *type;
enum object_type type;
/* Get the data stream */
memset(stream, 0, sizeof(*stream));
@ -735,22 +757,11 @@ static int unpack_sha1_header(z_stream *stream, unsigned char *map, unsigned lon
return inflate(stream, 0);
}
c = *map++;
mapsize--;
type = typename[(c >> 4) & 7];
if (!type)
used = unpack_object_header_gently(map, mapsize, &type, &size);
if (!used || !valid_loose_object_type[type])
return -1;
bits = 4;
size = c & 0xf;
while ((c & 0x80)) {
if (bits >= 8*sizeof(long))
return -1;
c = *map++;
size += (c & 0x7f) << bits;
bits += 7;
mapsize--;
}
map += used;
mapsize -= used;
/* Set up the stream for the rest.. */
stream->next_in = map;
@ -758,7 +769,8 @@ static int unpack_sha1_header(z_stream *stream, unsigned char *map, unsigned lon
inflateInit(stream);
/* And generate the fake traditional header */
stream->total_out = 1 + snprintf(buffer, bufsiz, "%s %lu", type, size);
stream->total_out = 1 + snprintf(buffer, bufsiz, "%s %lu",
type_names[type], size);
return 0;
}
@ -916,25 +928,18 @@ static int packed_delta_info(unsigned char *base_sha1,
static unsigned long unpack_object_header(struct packed_git *p, unsigned long offset,
enum object_type *type, unsigned long *sizep)
{
unsigned shift;
unsigned char c;
unsigned long size;
unsigned long used;
if (offset >= p->pack_size)
if (p->pack_size <= offset)
die("object offset outside of pack file");
c = *((unsigned char *)p->pack_base + offset++);
*type = (c >> 4) & 7;
size = c & 15;
shift = 4;
while (c & 0x80) {
if (offset >= p->pack_size)
die("object offset outside of pack file");
c = *((unsigned char *)p->pack_base + offset++);
size += (c & 0x7f) << shift;
shift += 7;
}
*sizep = size;
return offset;
used = unpack_object_header_gently((unsigned char *)p->pack_base +
offset,
p->pack_size - offset, type, sizep);
if (!used)
die("object offset outside of pack file");
return offset + used;
}
int check_reuse_pack_delta(struct packed_git *p, unsigned long offset,
@ -1348,7 +1353,7 @@ char *write_sha1_file_prepare(void *buf,
*
* Returns the errno on failure, 0 on success.
*/
static int link_temp_to_file(const char *tmpfile, char *filename)
static int link_temp_to_file(const char *tmpfile, const char *filename)
{
int ret;
char *dir;
@ -1381,7 +1386,7 @@ static int link_temp_to_file(const char *tmpfile, char *filename)
/*
* Move the just written object into its final resting place
*/
int move_temp_to_file(const char *tmpfile, char *filename)
int move_temp_to_file(const char *tmpfile, const char *filename)
{
int ret = link_temp_to_file(tmpfile, filename);
@ -1756,7 +1761,7 @@ int read_pipe(int fd, char** return_buf, unsigned long* return_size)
int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object)
{
unsigned long size = 4096;
char *buf = malloc(size);
char *buf = xmalloc(size);
int ret;
unsigned char hdr[50];
int hdrlen;

View File

@ -279,7 +279,7 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
pathname = resolve_ref(git_path(*p, len, str), this_result, 1);
if (pathname) {
if (!refs_found++)
real_path = strdup(pathname);
real_path = xstrdup(pathname);
if (!warn_ambiguous_refs)
break;
}

View File

@ -34,6 +34,15 @@ export GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME
export GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME
export EDITOR VISUAL
case $(echo $GIT_TRACE |tr [A-Z] [a-z]) in
1|2|true)
echo "* warning: Some tests will not work if GIT_TRACE" \
"is set as to trace on STDERR ! *"
echo "* warning: Please set GIT_TRACE to something" \
"other than 1, 2 or true ! *"
;;
esac
# Each test should start with something like this, after copyright notices:
#
# test_description='Description of this test...

30
trace.c
View File

@ -51,7 +51,7 @@ int nfvasprintf(char **str, const char *fmt, va_list va)
}
/* Get a trace file descriptor from GIT_TRACE env variable. */
static int get_trace_fd()
static int get_trace_fd(int *need_close)
{
char *trace = getenv("GIT_TRACE");
@ -61,9 +61,25 @@ static int get_trace_fd()
return STDERR_FILENO;
if (strlen(trace) == 1 && isdigit(*trace))
return atoi(trace);
if (*trace == '/') {
int fd = open(trace, O_WRONLY | O_APPEND | O_CREAT, 0666);
if (fd == -1) {
fprintf(stderr,
"Could not open '%s' for tracing: %s\n"
"Defaulting to tracing on stderr...\n",
trace, strerror(errno));
return STDERR_FILENO;
}
*need_close = 1;
return fd;
}
fprintf(stderr, "What does '%s' for GIT_TRACE means ?\n", trace);
fprintf(stderr, "If you want to trace into a file, "
"then please set GIT_TRACE to an absolute pathname "
"(starting with /).\n");
fprintf(stderr, "Defaulting to tracing on stderr...\n");
return STDERR_FILENO;
}
@ -74,7 +90,8 @@ void trace_printf(const char *format, ...)
{
char *trace_str;
va_list rest;
int fd = get_trace_fd();
int need_close = 0;
int fd = get_trace_fd(&need_close);
if (!fd)
return;
@ -86,6 +103,9 @@ void trace_printf(const char *format, ...)
write_or_whine(fd, trace_str, strlen(trace_str), err_msg);
free(trace_str);
if (need_close)
close(fd);
}
void trace_argv_printf(const char **argv, int count, const char *format, ...)
@ -93,7 +113,8 @@ void trace_argv_printf(const char **argv, int count, const char *format, ...)
char *argv_str, *format_str, *trace_str;
size_t argv_len, format_len, trace_len;
va_list rest;
int fd = get_trace_fd();
int need_close = 0;
int fd = get_trace_fd(&need_close);
if (!fd)
return;
@ -122,4 +143,7 @@ void trace_argv_printf(const char **argv, int count, const char *format, ...)
free(argv_str);
free(format_str);
free(trace_str);
if (need_close)
close(fd);
}