Browse Source
* lt/refs: (58 commits) git-pack-refs --prune pack-refs: do not pack symbolic refs. Tell between packed, unpacked and symbolic refs. Add callback data to for_each_ref() family. symbolit-ref: fix resolve_ref conversion. Fix broken sha1 locking fsck-objects: adjust to resolve_ref() clean-up. gitignore: git-pack-refs is a generated file. wt-status: use simplified resolve_ref to find current branch Fix t1400-update-ref test minimally Enable the packed refs file format Make ref resolution saner Add support for negative refs Start handling references internally as a sorted in-memory list gitweb fix validating pg (page) parameter git-repack(1): document --window and --depth git-apply(1): document --unidiff-zero gitweb: fix warnings in PATH_INFO code and add export_ok/strict_export upload-archive: monitor child communication even more carefully. gitweb: export options ...maint
Junio C Hamano
18 years ago
69 changed files with 2767 additions and 949 deletions
@ -0,0 +1,100 @@
@@ -0,0 +1,100 @@
|
||||
git-archive(1) |
||||
============== |
||||
|
||||
NAME |
||||
---- |
||||
git-archive - Creates a archive of the files in the named tree |
||||
|
||||
|
||||
SYNOPSIS |
||||
-------- |
||||
'git-archive' --format=<fmt> [--list] [--prefix=<prefix>/] [<extra>] |
||||
[--remote=<repo>] <tree-ish> [path...] |
||||
|
||||
DESCRIPTION |
||||
----------- |
||||
Creates an archive of the specified format containing the tree |
||||
structure for the named tree. If <prefix> is specified it is |
||||
prepended to the filenames in the archive. |
||||
|
||||
'git-archive' behaves differently when given a tree ID versus when |
||||
given a commit ID or tag ID. In the first case the current time is |
||||
used as modification time of each file in the archive. In the latter |
||||
case the commit time as recorded in the referenced commit object is |
||||
used instead. Additionally the commit ID is stored in a global |
||||
extended pax header if the tar format is used; it can be extracted |
||||
using 'git-get-tar-commit-id'. In ZIP files it is stored as a file |
||||
comment. |
||||
|
||||
OPTIONS |
||||
------- |
||||
|
||||
--format=<fmt>:: |
||||
Format of the resulting archive: 'tar', 'zip'... |
||||
|
||||
--list:: |
||||
Show all available formats. |
||||
|
||||
--prefix=<prefix>/:: |
||||
Prepend <prefix>/ to each filename in the archive. |
||||
|
||||
<extra>:: |
||||
This can be any options that the archiver backend understand. |
||||
|
||||
--remote=<repo>:: |
||||
Instead of making a tar archive from local repository, |
||||
retrieve a tar archive from a remote repository. |
||||
|
||||
<tree-ish>:: |
||||
The tree or commit to produce an archive for. |
||||
|
||||
path:: |
||||
If one or more paths are specified, include only these in the |
||||
archive, otherwise include all files and subdirectories. |
||||
|
||||
CONFIGURATION |
||||
------------- |
||||
By default, file and directories modes are set to 0666 or 0777 in tar |
||||
archives. It is possible to change this by setting the "umask" variable |
||||
in the repository configuration as follows : |
||||
|
||||
[tar] |
||||
umask = 002 ;# group friendly |
||||
|
||||
The special umask value "user" indicates that the user's current umask |
||||
will be used instead. The default value remains 0, which means world |
||||
readable/writable files and directories. |
||||
|
||||
EXAMPLES |
||||
-------- |
||||
git archive --format=tar --prefix=junk/ HEAD | (cd /var/tmp/ && tar xf -):: |
||||
|
||||
Create a tar archive that contains the contents of the |
||||
latest commit on the current branch, and extracts it in |
||||
`/var/tmp/junk` directory. |
||||
|
||||
git archive --format=tar --prefix=git-1.4.0/ v1.4.0 | gzip >git-1.4.0.tar.gz:: |
||||
|
||||
Create a compressed tarball for v1.4.0 release. |
||||
|
||||
git archive --format=tar --prefix=git-1.4.0/ v1.4.0{caret}\{tree\} | gzip >git-1.4.0.tar.gz:: |
||||
|
||||
Create a compressed tarball for v1.4.0 release, but without a |
||||
global extended pax header. |
||||
|
||||
git archive --format=zip --prefix=git-docs/ HEAD:Documentation/ > git-1.4.0-docs.zip:: |
||||
|
||||
Put everything in the current head's Documentation/ directory |
||||
into 'git-1.4.0-docs.zip', with the prefix 'git-docs/'. |
||||
|
||||
Author |
||||
------ |
||||
Written by Franck Bui-Huu and Rene Scharfe. |
||||
|
||||
Documentation |
||||
-------------- |
||||
Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>. |
||||
|
||||
GIT |
||||
--- |
||||
Part of the gitlink:git[7] suite |
@ -0,0 +1,37 @@
@@ -0,0 +1,37 @@
|
||||
git-upload-archive(1) |
||||
==================== |
||||
|
||||
NAME |
||||
---- |
||||
git-upload-archive - Send archive |
||||
|
||||
|
||||
SYNOPSIS |
||||
-------- |
||||
'git-upload-archive' <directory> |
||||
|
||||
DESCRIPTION |
||||
----------- |
||||
Invoked by 'git-archive --remote' and sends a generated archive to the |
||||
other end over the git protocol. |
||||
|
||||
This command is usually not invoked directly by the end user. The UI |
||||
for the protocol is on the 'git-archive' side, and the program pair |
||||
is meant to be used to get an archive from a remote repository. |
||||
|
||||
OPTIONS |
||||
------- |
||||
<directory>:: |
||||
The repository to get a tar archive from. |
||||
|
||||
Author |
||||
------ |
||||
Written by Franck Bui-Huu. |
||||
|
||||
Documentation |
||||
-------------- |
||||
Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>. |
||||
|
||||
GIT |
||||
--- |
||||
Part of the gitlink:git[7] suite |
@ -0,0 +1,47 @@
@@ -0,0 +1,47 @@
|
||||
#ifndef ARCHIVE_H |
||||
#define ARCHIVE_H |
||||
|
||||
#define MAX_EXTRA_ARGS 32 |
||||
#define MAX_ARGS (MAX_EXTRA_ARGS + 32) |
||||
|
||||
struct archiver_args { |
||||
const char *base; |
||||
struct tree *tree; |
||||
const unsigned char *commit_sha1; |
||||
time_t time; |
||||
const char **pathspec; |
||||
unsigned int verbose : 1; |
||||
void *extra; |
||||
}; |
||||
|
||||
typedef int (*write_archive_fn_t)(struct archiver_args *); |
||||
|
||||
typedef void *(*parse_extra_args_fn_t)(int argc, const char **argv); |
||||
|
||||
struct archiver { |
||||
const char *name; |
||||
struct archiver_args args; |
||||
write_archive_fn_t write_archive; |
||||
parse_extra_args_fn_t parse_extra; |
||||
}; |
||||
|
||||
extern struct archiver archivers[]; |
||||
|
||||
extern int parse_archive_args(int argc, |
||||
const char **argv, |
||||
struct archiver *ar); |
||||
|
||||
extern void parse_treeish_arg(const char **treeish, |
||||
struct archiver_args *ar_args, |
||||
const char *prefix); |
||||
|
||||
extern void parse_pathspec_arg(const char **pathspec, |
||||
struct archiver_args *args); |
||||
/* |
||||
* Archive-format specific backends. |
||||
*/ |
||||
extern int write_tar_archive(struct archiver_args *); |
||||
extern int write_zip_archive(struct archiver_args *); |
||||
extern void *parse_extra_zip_args(int argc, const char **argv); |
||||
|
||||
#endif /* ARCHIVE_H */ |
@ -0,0 +1,263 @@
@@ -0,0 +1,263 @@
|
||||
/* |
||||
* Copyright (c) 2006 Franck Bui-Huu |
||||
* Copyright (c) 2006 Rene Scharfe |
||||
*/ |
||||
#include <time.h> |
||||
#include "cache.h" |
||||
#include "builtin.h" |
||||
#include "archive.h" |
||||
#include "commit.h" |
||||
#include "tree-walk.h" |
||||
#include "exec_cmd.h" |
||||
#include "pkt-line.h" |
||||
#include "sideband.h" |
||||
|
||||
static const char archive_usage[] = \ |
||||
"git-archive --format=<fmt> [--prefix=<prefix>/] [--verbose] [<extra>] <tree-ish> [path...]"; |
||||
|
||||
struct archiver archivers[] = { |
||||
{ |
||||
.name = "tar", |
||||
.write_archive = write_tar_archive, |
||||
}, |
||||
{ |
||||
.name = "zip", |
||||
.write_archive = write_zip_archive, |
||||
.parse_extra = parse_extra_zip_args, |
||||
}, |
||||
}; |
||||
|
||||
static int run_remote_archiver(const char *remote, int argc, |
||||
const char **argv) |
||||
{ |
||||
char *url, buf[LARGE_PACKET_MAX]; |
||||
int fd[2], i, len, rv; |
||||
pid_t pid; |
||||
const char *exec = "git-upload-archive"; |
||||
int exec_at = 0; |
||||
|
||||
for (i = 1; i < argc; i++) { |
||||
const char *arg = argv[i]; |
||||
if (!strncmp("--exec=", arg, 7)) { |
||||
if (exec_at) |
||||
die("multiple --exec specified"); |
||||
exec = arg + 7; |
||||
exec_at = i; |
||||
break; |
||||
} |
||||
} |
||||
|
||||
url = xstrdup(remote); |
||||
pid = git_connect(fd, url, exec); |
||||
if (pid < 0) |
||||
return pid; |
||||
|
||||
for (i = 1; i < argc; i++) { |
||||
if (i == exec_at) |
||||
continue; |
||||
packet_write(fd[1], "argument %s\n", argv[i]); |
||||
} |
||||
packet_flush(fd[1]); |
||||
|
||||
len = packet_read_line(fd[0], buf, sizeof(buf)); |
||||
if (!len) |
||||
die("git-archive: expected ACK/NAK, got EOF"); |
||||
if (buf[len-1] == '\n') |
||||
buf[--len] = 0; |
||||
if (strcmp(buf, "ACK")) { |
||||
if (len > 5 && !strncmp(buf, "NACK ", 5)) |
||||
die("git-archive: NACK %s", buf + 5); |
||||
die("git-archive: protocol error"); |
||||
} |
||||
|
||||
len = packet_read_line(fd[0], buf, sizeof(buf)); |
||||
if (len) |
||||
die("git-archive: expected a flush"); |
||||
|
||||
/* Now, start reading from fd[0] and spit it out to stdout */ |
||||
rv = recv_sideband("archive", fd[0], 1, 2, buf, sizeof(buf)); |
||||
close(fd[0]); |
||||
rv |= finish_connect(pid); |
||||
|
||||
return !!rv; |
||||
} |
||||
|
||||
static int init_archiver(const char *name, struct archiver *ar) |
||||
{ |
||||
int rv = -1, i; |
||||
|
||||
for (i = 0; i < ARRAY_SIZE(archivers); i++) { |
||||
if (!strcmp(name, archivers[i].name)) { |
||||
memcpy(ar, &archivers[i], sizeof(struct archiver)); |
||||
rv = 0; |
||||
break; |
||||
} |
||||
} |
||||
return rv; |
||||
} |
||||
|
||||
void parse_pathspec_arg(const char **pathspec, struct archiver_args *ar_args) |
||||
{ |
||||
ar_args->pathspec = get_pathspec(ar_args->base, pathspec); |
||||
} |
||||
|
||||
void parse_treeish_arg(const char **argv, struct archiver_args *ar_args, |
||||
const char *prefix) |
||||
{ |
||||
const char *name = argv[0]; |
||||
const unsigned char *commit_sha1; |
||||
time_t archive_time; |
||||
struct tree *tree; |
||||
struct commit *commit; |
||||
unsigned char sha1[20]; |
||||
|
||||
if (get_sha1(name, sha1)) |
||||
die("Not a valid object name"); |
||||
|
||||
commit = lookup_commit_reference_gently(sha1, 1); |
||||
if (commit) { |
||||
commit_sha1 = commit->object.sha1; |
||||
archive_time = commit->date; |
||||
} else { |
||||
commit_sha1 = NULL; |
||||
archive_time = time(NULL); |
||||
} |
||||
|
||||
tree = parse_tree_indirect(sha1); |
||||
if (tree == NULL) |
||||
die("not a tree object"); |
||||
|
||||
if (prefix) { |
||||
unsigned char tree_sha1[20]; |
||||
unsigned int mode; |
||||
int err; |
||||
|
||||
err = get_tree_entry(tree->object.sha1, prefix, |
||||
tree_sha1, &mode); |
||||
if (err || !S_ISDIR(mode)) |
||||
die("current working directory is untracked"); |
||||
|
||||
free(tree); |
||||
tree = parse_tree_indirect(tree_sha1); |
||||
} |
||||
ar_args->tree = tree; |
||||
ar_args->commit_sha1 = commit_sha1; |
||||
ar_args->time = archive_time; |
||||
} |
||||
|
||||
int parse_archive_args(int argc, const char **argv, struct archiver *ar) |
||||
{ |
||||
const char *extra_argv[MAX_EXTRA_ARGS]; |
||||
int extra_argc = 0; |
||||
const char *format = NULL; /* might want to default to "tar" */ |
||||
const char *base = ""; |
||||
int verbose = 0; |
||||
int i; |
||||
|
||||
for (i = 1; i < argc; i++) { |
||||
const char *arg = argv[i]; |
||||
|
||||
if (!strcmp(arg, "--list") || !strcmp(arg, "-l")) { |
||||
for (i = 0; i < ARRAY_SIZE(archivers); i++) |
||||
printf("%s\n", archivers[i].name); |
||||
exit(0); |
||||
} |
||||
if (!strcmp(arg, "--verbose") || !strcmp(arg, "-v")) { |
||||
verbose = 1; |
||||
continue; |
||||
} |
||||
if (!strncmp(arg, "--format=", 9)) { |
||||
format = arg + 9; |
||||
continue; |
||||
} |
||||
if (!strncmp(arg, "--prefix=", 9)) { |
||||
base = arg + 9; |
||||
continue; |
||||
} |
||||
if (!strcmp(arg, "--")) { |
||||
i++; |
||||
break; |
||||
} |
||||
if (arg[0] == '-') { |
||||
if (extra_argc > MAX_EXTRA_ARGS - 1) |
||||
die("Too many extra options"); |
||||
extra_argv[extra_argc++] = arg; |
||||
continue; |
||||
} |
||||
break; |
||||
} |
||||
|
||||
/* We need at least one parameter -- tree-ish */ |
||||
if (argc - 1 < i) |
||||
usage(archive_usage); |
||||
if (!format) |
||||
die("You must specify an archive format"); |
||||
if (init_archiver(format, ar) < 0) |
||||
die("Unknown archive format '%s'", format); |
||||
|
||||
if (extra_argc) { |
||||
if (!ar->parse_extra) |
||||
die("'%s' format does not handle %s", |
||||
ar->name, extra_argv[0]); |
||||
ar->args.extra = ar->parse_extra(extra_argc, extra_argv); |
||||
} |
||||
ar->args.verbose = verbose; |
||||
ar->args.base = base; |
||||
|
||||
return i; |
||||
} |
||||
|
||||
static const char *extract_remote_arg(int *ac, const char **av) |
||||
{ |
||||
int ix, iy, cnt = *ac; |
||||
int no_more_options = 0; |
||||
const char *remote = NULL; |
||||
|
||||
for (ix = iy = 1; ix < cnt; ix++) { |
||||
const char *arg = av[ix]; |
||||
if (!strcmp(arg, "--")) |
||||
no_more_options = 1; |
||||
if (!no_more_options) { |
||||
if (!strncmp(arg, "--remote=", 9)) { |
||||
if (remote) |
||||
die("Multiple --remote specified"); |
||||
remote = arg + 9; |
||||
continue; |
||||
} |
||||
if (arg[0] != '-') |
||||
no_more_options = 1; |
||||
} |
||||
if (ix != iy) |
||||
av[iy] = arg; |
||||
iy++; |
||||
} |
||||
if (remote) { |
||||
av[--cnt] = NULL; |
||||
*ac = cnt; |
||||
} |
||||
return remote; |
||||
} |
||||
|
||||
int cmd_archive(int argc, const char **argv, const char *prefix) |
||||
{ |
||||
struct archiver ar; |
||||
int tree_idx; |
||||
const char *remote = NULL; |
||||
|
||||
remote = extract_remote_arg(&argc, argv); |
||||
if (remote) |
||||
return run_remote_archiver(remote, argc, argv); |
||||
|
||||
setlinebuf(stderr); |
||||
|
||||
memset(&ar, 0, sizeof(ar)); |
||||
tree_idx = parse_archive_args(argc, argv, &ar); |
||||
if (prefix == NULL) |
||||
prefix = setup_git_directory(); |
||||
|
||||
argv += tree_idx; |
||||
parse_treeish_arg(argv, &ar.args, prefix); |
||||
parse_pathspec_arg(argv + 1, &ar.args); |
||||
|
||||
return ar.write_archive(&ar.args); |
||||
} |
@ -0,0 +1,112 @@
@@ -0,0 +1,112 @@
|
||||
#include "cache.h" |
||||
#include "refs.h" |
||||
|
||||
static const char *result_path, *lock_path; |
||||
static const char builtin_pack_refs_usage[] = |
||||
"git-pack-refs [--prune]"; |
||||
|
||||
struct ref_to_prune { |
||||
struct ref_to_prune *next; |
||||
unsigned char sha1[20]; |
||||
char name[FLEX_ARRAY]; |
||||
}; |
||||
|
||||
struct pack_refs_cb_data { |
||||
int prune; |
||||
struct ref_to_prune *ref_to_prune; |
||||
FILE *refs_file; |
||||
}; |
||||
|
||||
static void remove_lock_file(void) |
||||
{ |
||||
if (lock_path) |
||||
unlink(lock_path); |
||||
} |
||||
|
||||
static int do_not_prune(int flags) |
||||
{ |
||||
/* If it is already packed or if it is a symref, |
||||
* do not prune it. |
||||
*/ |
||||
return (flags & (REF_ISSYMREF|REF_ISPACKED)); |
||||
} |
||||
|
||||
static int handle_one_ref(const char *path, const unsigned char *sha1, |
||||
int flags, void *cb_data) |
||||
{ |
||||
struct pack_refs_cb_data *cb = cb_data; |
||||
|
||||
/* Do not pack the symbolic refs */ |
||||
if (!(flags & REF_ISSYMREF)) |
||||
fprintf(cb->refs_file, "%s %s\n", sha1_to_hex(sha1), path); |
||||
if (cb->prune && !do_not_prune(flags)) { |
||||
int namelen = strlen(path) + 1; |
||||
struct ref_to_prune *n = xcalloc(1, sizeof(*n) + namelen); |
||||
hashcpy(n->sha1, sha1); |
||||
strcpy(n->name, path); |
||||
n->next = cb->ref_to_prune; |
||||
cb->ref_to_prune = n; |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
/* make sure nobody touched the ref, and unlink */ |
||||
static void prune_ref(struct ref_to_prune *r) |
||||
{ |
||||
struct ref_lock *lock = lock_ref_sha1(r->name + 5, r->sha1, 1); |
||||
|
||||
if (lock) { |
||||
unlink(git_path(r->name)); |
||||
unlock_ref(lock); |
||||
} |
||||
} |
||||
|
||||
static void prune_refs(struct ref_to_prune *r) |
||||
{ |
||||
while (r) { |
||||
prune_ref(r); |
||||
r = r->next; |
||||
} |
||||
} |
||||
|
||||
int cmd_pack_refs(int argc, const char **argv, const char *prefix) |
||||
{ |
||||
int fd, i; |
||||
struct pack_refs_cb_data cbdata; |
||||
|
||||
memset(&cbdata, 0, sizeof(cbdata)); |
||||
|
||||
for (i = 1; i < argc; i++) { |
||||
const char *arg = argv[i]; |
||||
if (!strcmp(arg, "--prune")) { |
||||
cbdata.prune = 1; |
||||
continue; |
||||
} |
||||
/* perhaps other parameters later... */ |
||||
break; |
||||
} |
||||
if (i != argc) |
||||
usage(builtin_pack_refs_usage); |
||||
|
||||
result_path = xstrdup(git_path("packed-refs")); |
||||
lock_path = xstrdup(mkpath("%s.lock", result_path)); |
||||
|
||||
fd = open(lock_path, O_CREAT | O_EXCL | O_WRONLY, 0666); |
||||
if (fd < 0) |
||||
die("unable to create new ref-pack file (%s)", strerror(errno)); |
||||
atexit(remove_lock_file); |
||||
|
||||
cbdata.refs_file = fdopen(fd, "w"); |
||||
if (!cbdata.refs_file) |
||||
die("unable to create ref-pack file structure (%s)", |
||||
strerror(errno)); |
||||
for_each_ref(handle_one_ref, &cbdata); |
||||
fsync(fd); |
||||
fclose(cbdata.refs_file); |
||||
if (rename(lock_path, result_path) < 0) |
||||
die("unable to overwrite old ref-pack file (%s)", strerror(errno)); |
||||
lock_path = NULL; |
||||
if (cbdata.prune) |
||||
prune_refs(cbdata.ref_to_prune); |
||||
return 0; |
||||
} |
@ -0,0 +1,36 @@
@@ -0,0 +1,36 @@
|
||||
#include "wt-status.h" |
||||
#include "cache.h" |
||||
|
||||
extern int wt_status_use_color; |
||||
|
||||
static const char runstatus_usage[] = |
||||
"git-runstatus [--color|--nocolor] [--amend] [--verbose]"; |
||||
|
||||
int cmd_runstatus(int argc, const char **argv, const char *prefix) |
||||
{ |
||||
struct wt_status s; |
||||
int i; |
||||
|
||||
git_config(git_status_config); |
||||
wt_status_prepare(&s); |
||||
|
||||
for (i = 1; i < argc; i++) { |
||||
if (!strcmp(argv[i], "--color")) |
||||
wt_status_use_color = 1; |
||||
else if (!strcmp(argv[i], "--nocolor")) |
||||
wt_status_use_color = 0; |
||||
else if (!strcmp(argv[i], "--amend")) { |
||||
s.amend = 1; |
||||
s.reference = "HEAD^1"; |
||||
} |
||||
else if (!strcmp(argv[i], "--verbose")) |
||||
s.verbose = 1; |
||||
else if (!strcmp(argv[i], "--untracked")) |
||||
s.untracked = 1; |
||||
else |
||||
usage(runstatus_usage); |
||||
} |
||||
|
||||
wt_status_print(&s); |
||||
return s.commitable ? 0 : 1; |
||||
} |
@ -0,0 +1,175 @@
@@ -0,0 +1,175 @@
|
||||
/* |
||||
* Copyright (c) 2006 Franck Bui-Huu |
||||
*/ |
||||
#include <time.h> |
||||
#include "cache.h" |
||||
#include "builtin.h" |
||||
#include "archive.h" |
||||
#include "pkt-line.h" |
||||
#include "sideband.h" |
||||
#include <sys/wait.h> |
||||
#include <sys/poll.h> |
||||
|
||||
static const char upload_archive_usage[] = |
||||
"git-upload-archive <repo>"; |
||||
|
||||
static const char deadchild[] = |
||||
"git-upload-archive: archiver died with error"; |
||||
|
||||
static const char lostchild[] = |
||||
"git-upload-archive: archiver process was lost"; |
||||
|
||||
|
||||
static int run_upload_archive(int argc, const char **argv, const char *prefix) |
||||
{ |
||||
struct archiver ar; |
||||
const char *sent_argv[MAX_ARGS]; |
||||
const char *arg_cmd = "argument "; |
||||
char *p, buf[4096]; |
||||
int treeish_idx; |
||||
int sent_argc; |
||||
int len; |
||||
|
||||
if (argc != 2) |
||||
usage(upload_archive_usage); |
||||
|
||||
if (strlen(argv[1]) > sizeof(buf)) |
||||
die("insanely long repository name"); |
||||
|
||||
strcpy(buf, argv[1]); /* enter-repo smudges its argument */ |
||||
|
||||
if (!enter_repo(buf, 0)) |
||||
die("not a git archive"); |
||||
|
||||
/* put received options in sent_argv[] */ |
||||
sent_argc = 1; |
||||
sent_argv[0] = "git-upload-archive"; |
||||
for (p = buf;;) { |
||||
/* This will die if not enough free space in buf */ |
||||
len = packet_read_line(0, p, (buf + sizeof buf) - p); |
||||
if (len == 0) |
||||
break; /* got a flush */ |
||||
if (sent_argc > MAX_ARGS - 2) |
||||
die("Too many options (>29)"); |
||||
|
||||
if (p[len-1] == '\n') { |
||||
p[--len] = 0; |
||||
} |
||||
if (len < strlen(arg_cmd) || |
||||
strncmp(arg_cmd, p, strlen(arg_cmd))) |
||||
die("'argument' token or flush expected"); |
||||
|
||||
len -= strlen(arg_cmd); |
||||
memmove(p, p + strlen(arg_cmd), len); |
||||
sent_argv[sent_argc++] = p; |
||||
p += len; |
||||
*p++ = 0; |
||||
} |
||||
sent_argv[sent_argc] = NULL; |
||||
|
||||
/* parse all options sent by the client */ |
||||
treeish_idx = parse_archive_args(sent_argc, sent_argv, &ar); |
||||
|
||||
parse_treeish_arg(sent_argv + treeish_idx, &ar.args, prefix); |
||||
parse_pathspec_arg(sent_argv + treeish_idx + 1, &ar.args); |
||||
|
||||
return ar.write_archive(&ar.args); |
||||
} |
||||
|
||||
static void error_clnt(const char *fmt, ...) |
||||
{ |
||||
char buf[1024]; |
||||
va_list params; |
||||
int len; |
||||
|
||||
va_start(params, fmt); |
||||
len = vsprintf(buf, fmt, params); |
||||
va_end(params); |
||||
send_sideband(1, 3, buf, len, LARGE_PACKET_MAX); |
||||
die("sent error to the client: %s", buf); |
||||
} |
||||
|
||||
static void process_input(int child_fd, int band) |
||||
{ |
||||
char buf[16384]; |
||||
ssize_t sz = read(child_fd, buf, sizeof(buf)); |
||||
if (sz < 0) { |
||||
if (errno != EINTR) |
||||
error_clnt("read error: %s\n", strerror(errno)); |
||||
return; |
||||
} |
||||
send_sideband(1, band, buf, sz, LARGE_PACKET_MAX); |
||||
} |
||||
|
||||
int cmd_upload_archive(int argc, const char **argv, const char *prefix) |
||||
{ |
||||
pid_t writer; |
||||
int fd1[2], fd2[2]; |
||||
/* |
||||
* Set up sideband subprocess. |
||||
* |
||||
* We (parent) monitor and read from child, sending its fd#1 and fd#2 |
||||
* multiplexed out to our fd#1. If the child dies, we tell the other |
||||
* end over channel #3. |
||||
*/ |
||||
if (pipe(fd1) < 0 || pipe(fd2) < 0) { |
||||
int err = errno; |
||||
packet_write(1, "NACK pipe failed on the remote side\n"); |
||||
die("upload-archive: %s", strerror(err)); |
||||
} |
||||
writer = fork(); |
||||
if (writer < 0) { |
||||
int err = errno; |
||||
packet_write(1, "NACK fork failed on the remote side\n"); |
||||
die("upload-archive: %s", strerror(err)); |
||||
} |
||||
if (!writer) { |
||||
/* child - connect fd#1 and fd#2 to the pipe */ |
||||
dup2(fd1[1], 1); |
||||
dup2(fd2[1], 2); |
||||
close(fd1[1]); close(fd2[1]); |
||||
close(fd1[0]); close(fd2[0]); /* we do not read from pipe */ |
||||
|
||||
exit(run_upload_archive(argc, argv, prefix)); |
||||
} |
||||
|
||||
/* parent - read from child, multiplex and send out to fd#1 */ |
||||
close(fd1[1]); close(fd2[1]); /* we do not write to pipe */ |
||||
packet_write(1, "ACK\n"); |
||||
packet_flush(1); |
||||
|
||||
while (1) { |
||||
struct pollfd pfd[2]; |
||||
int status; |
||||
|
||||
pfd[0].fd = fd1[0]; |
||||
pfd[0].events = POLLIN; |
||||
pfd[1].fd = fd2[0]; |
||||
pfd[1].events = POLLIN; |
||||
if (poll(pfd, 2, -1) < 0) { |
||||
if (errno != EINTR) { |
||||
error("poll failed resuming: %s", |
||||
strerror(errno)); |
||||
sleep(1); |
||||
} |
||||
continue; |
||||
} |
||||
if (pfd[0].revents & POLLIN) |
||||
/* Data stream ready */ |
||||
process_input(pfd[0].fd, 1); |
||||
if (pfd[1].revents & POLLIN) |
||||
/* Status stream ready */ |
||||
process_input(pfd[1].fd, 2); |
||||
/* Always finish to read data when available */ |
||||
if ((pfd[0].revents | pfd[1].revents) & POLLIN) |
||||
continue; |
||||
|
||||
if (waitpid(writer, &status, 0) < 0) |
||||
error_clnt("%s", lostchild); |
||||
else if (!WIFEXITED(status) || WEXITSTATUS(status) > 0) |
||||
error_clnt("%s", deadchild); |
||||
packet_flush(1); |
||||
break; |
||||
} |
||||
return 0; |
||||
} |
@ -0,0 +1,176 @@
@@ -0,0 +1,176 @@
|
||||
#include "color.h" |
||||
#include "cache.h" |
||||
#include "git-compat-util.h" |
||||
|
||||
#include <stdarg.h> |
||||
|
||||
#define COLOR_RESET "\033[m" |
||||
|
||||
static int parse_color(const char *name, int len) |
||||
{ |
||||
static const char * const color_names[] = { |
||||
"normal", "black", "red", "green", "yellow", |
||||
"blue", "magenta", "cyan", "white" |
||||
}; |
||||
char *end; |
||||
int i; |
||||
for (i = 0; i < ARRAY_SIZE(color_names); i++) { |
||||
const char *str = color_names[i]; |
||||
if (!strncasecmp(name, str, len) && !str[len]) |
||||
return i - 1; |
||||
} |
||||
i = strtol(name, &end, 10); |
||||
if (*name && !*end && i >= -1 && i <= 255) |
||||
return i; |
||||
return -2; |
||||
} |
||||
|
||||
static int parse_attr(const char *name, int len) |
||||
{ |
||||
static const int attr_values[] = { 1, 2, 4, 5, 7 }; |
||||
static const char * const attr_names[] = { |
||||
"bold", "dim", "ul", "blink", "reverse" |
||||
}; |
||||
int i; |
||||
for (i = 0; i < ARRAY_SIZE(attr_names); i++) { |
||||
const char *str = attr_names[i]; |
||||
if (!strncasecmp(name, str, len) && !str[len]) |
||||
return attr_values[i]; |
||||
} |
||||
return -1; |
||||
} |
||||
|
||||
void color_parse(const char *value, const char *var, char *dst) |
||||
{ |
||||
const char *ptr = value; |
||||
int attr = -1; |
||||
int fg = -2; |
||||
int bg = -2; |
||||
|
||||
if (!strcasecmp(value, "reset")) { |
||||
strcpy(dst, "\033[m"); |
||||
return; |
||||
} |
||||
|
||||
/* [fg [bg]] [attr] */ |
||||
while (*ptr) { |
||||
const char *word = ptr; |
||||
int val, len = 0; |
||||
|
||||
while (word[len] && !isspace(word[len])) |
||||
len++; |
||||
|
||||
ptr = word + len; |
||||
while (*ptr && isspace(*ptr)) |
||||
ptr++; |
||||
|
||||
val = parse_color(word, len); |
||||
if (val >= -1) { |
||||
if (fg == -2) { |
||||
fg = val; |
||||
continue; |
||||
} |
||||
if (bg == -2) { |
||||
bg = val; |
||||
continue; |
||||
} |
||||
goto bad; |
||||
} |
||||
val = parse_attr(word, len); |
||||
if (val < 0 || attr != -1) |
||||
goto bad; |
||||
attr = val; |
||||
} |
||||
|
||||
if (attr >= 0 || fg >= 0 || bg >= 0) { |
||||
int sep = 0; |
||||
|
||||
*dst++ = '\033'; |
||||
*dst++ = '['; |
||||
if (attr >= 0) { |
||||
*dst++ = '0' + attr; |
||||
sep++; |
||||
} |
||||
if (fg >= 0) { |
||||
if (sep++) |
||||
*dst++ = ';'; |
||||
if (fg < 8) { |
||||
*dst++ = '3'; |
||||
*dst++ = '0' + fg; |
||||
} else { |
||||
dst += sprintf(dst, "38;5;%d", fg); |
||||
} |
||||
} |
||||
if (bg >= 0) { |
||||
if (sep++) |
||||
*dst++ = ';'; |
||||
if (bg < 8) { |
||||
*dst++ = '4'; |
||||
*dst++ = '0' + bg; |
||||
} else { |
||||
dst += sprintf(dst, "48;5;%d", bg); |
||||
} |
||||
} |
||||
*dst++ = 'm'; |
||||
} |
||||
*dst = 0; |
||||
return; |
||||
bad: |
||||
die("bad config value '%s' for variable '%s'", value, var); |
||||
} |
||||
|
||||
int git_config_colorbool(const char *var, const char *value) |
||||
{ |
||||
if (!value) |
||||
return 1; |
||||
if (!strcasecmp(value, "auto")) { |
||||
if (isatty(1) || (pager_in_use && pager_use_color)) { |
||||
char *term = getenv("TERM"); |
||||
if (term && strcmp(term, "dumb")) |
||||
return 1; |
||||
} |
||||
return 0; |
||||
} |
||||
if (!strcasecmp(value, "never")) |
||||
return 0; |
||||
if (!strcasecmp(value, "always")) |
||||
return 1; |
||||
return git_config_bool(var, value); |
||||
} |
||||
|
||||
static int color_vprintf(const char *color, const char *fmt, |
||||
va_list args, const char *trail) |
||||
{ |
||||
int r = 0; |
||||
|
||||
if (*color) |
||||
r += printf("%s", color); |
||||
r += vprintf(fmt, args); |
||||
if (*color) |
||||
r += printf("%s", COLOR_RESET); |
||||
if (trail) |
||||
r += printf("%s", trail); |
||||
return r; |
||||
} |
||||
|
||||
|
||||
|
||||
int color_printf(const char *color, const char *fmt, ...) |
||||
{ |
||||
va_list args; |
||||
int r; |
||||
va_start(args, fmt); |
||||
r = color_vprintf(color, fmt, args, NULL); |
||||
va_end(args); |
||||
return r; |
||||
} |
||||
|
||||
int color_printf_ln(const char *color, const char *fmt, ...) |
||||
{ |
||||
va_list args; |
||||
int r; |
||||
va_start(args, fmt); |
||||
r = color_vprintf(color, fmt, args, "\n"); |
||||
va_end(args); |
||||
return r; |
||||
} |
@ -0,0 +1,12 @@
@@ -0,0 +1,12 @@
|
||||
#ifndef COLOR_H |
||||
#define COLOR_H |
||||
|
||||
/* "\033[1;38;5;2xx;48;5;2xxm\0" is 23 bytes */ |
||||
#define COLOR_MAXLEN 24 |
||||
|
||||
int git_config_colorbool(const char *var, const char *value); |
||||
void color_parse(const char *var, const char *value, char *dst); |
||||
int color_printf(const char *color, const char *fmt, ...); |
||||
int color_printf_ln(const char *color, const char *fmt, ...); |
||||
|
||||
#endif /* COLOR_H */ |
@ -0,0 +1,140 @@
@@ -0,0 +1,140 @@
|
||||
#include "cache.h" |
||||
#include "tag.h" |
||||
#include "commit.h" |
||||
#include "tree.h" |
||||
#include "blob.h" |
||||
#include "diff.h" |
||||
#include "tree-walk.h" |
||||
#include "revision.h" |
||||
#include "list-objects.h" |
||||
|
||||
static void process_blob(struct rev_info *revs, |
||||
struct blob *blob, |
||||
struct object_array *p, |
||||
struct name_path *path, |
||||
const char *name) |
||||
{ |
||||
struct object *obj = &blob->object; |
||||
|
||||
if (!revs->blob_objects) |
||||
return; |
||||
if (obj->flags & (UNINTERESTING | SEEN)) |
||||
return; |
||||
obj->flags |= SEEN; |
||||
name = xstrdup(name); |
||||
add_object(obj, p, path, name); |
||||
} |
||||
|
||||
static void process_tree(struct rev_info *revs, |
||||
struct tree *tree, |
||||
struct object_array *p, |
||||
struct name_path *path, |
||||
const char *name) |
||||
{ |
||||
struct object *obj = &tree->object; |
||||
struct tree_desc desc; |
||||
struct name_entry entry; |
||||
struct name_path me; |
||||
|
||||
if (!revs->tree_objects) |
||||
return; |
||||
if (obj->flags & (UNINTERESTING | SEEN)) |
||||
return; |
||||
if (parse_tree(tree) < 0) |
||||
die("bad tree object %s", sha1_to_hex(obj->sha1)); |
||||
obj->flags |= SEEN; |
||||
name = xstrdup(name); |
||||
add_object(obj, p, path, name); |
||||
me.up = path; |
||||
me.elem = name; |
||||
me.elem_len = strlen(name); |
||||
|
||||
desc.buf = tree->buffer; |
||||
desc.size = tree->size; |
||||
|
||||
while (tree_entry(&desc, &entry)) { |
||||
if (S_ISDIR(entry.mode)) |
||||
process_tree(revs, |
||||
lookup_tree(entry.sha1), |
||||
p, &me, entry.path); |
||||
else |
||||
process_blob(revs, |
||||
lookup_blob(entry.sha1), |
||||
p, &me, entry.path); |
||||
} |
||||
free(tree->buffer); |
||||
tree->buffer = NULL; |
||||
} |
||||
|
||||
static void mark_edge_parents_uninteresting(struct commit *commit, |
||||
struct rev_info *revs, |
||||
show_edge_fn show_edge) |
||||
{ |
||||
struct commit_list *parents; |
||||
|
||||
for (parents = commit->parents; parents; parents = parents->next) { |
||||
struct commit *parent = parents->item; |
||||
if (!(parent->object.flags & UNINTERESTING)) |
||||
continue; |
||||
mark_tree_uninteresting(parent->tree); |
||||
if (revs->edge_hint && !(parent->object.flags & SHOWN)) { |
||||
parent->object.flags |= SHOWN; |
||||
show_edge(parent); |
||||
} |
||||
} |
||||
} |
||||
|
||||
void mark_edges_uninteresting(struct commit_list *list, |
||||
struct rev_info *revs, |
||||
show_edge_fn show_edge) |
||||
{ |
||||
for ( ; list; list = list->next) { |
||||
struct commit *commit = list->item; |
||||
|
||||
if (commit->object.flags & UNINTERESTING) { |
||||
mark_tree_uninteresting(commit->tree); |
||||
continue; |
||||
} |
||||
mark_edge_parents_uninteresting(commit, revs, show_edge); |
||||
} |
||||
} |
||||
|
||||
void traverse_commit_list(struct rev_info *revs, |
||||
void (*show_commit)(struct commit *), |
||||
void (*show_object)(struct object_array_entry *)) |
||||
{ |
||||
int i; |
||||
struct commit *commit; |
||||
struct object_array objects = { 0, 0, NULL }; |
||||
|
||||
while ((commit = get_revision(revs)) != NULL) { |
||||
process_tree(revs, commit->tree, &objects, NULL, ""); |
||||
show_commit(commit); |
||||
} |
||||
for (i = 0; i < revs->pending.nr; i++) { |
||||
struct object_array_entry *pending = revs->pending.objects + i; |
||||
struct object *obj = pending->item; |
||||
const char *name = pending->name; |
||||
if (obj->flags & (UNINTERESTING | SEEN)) |
||||
continue; |
||||
if (obj->type == OBJ_TAG) { |
||||
obj->flags |= SEEN; |
||||
add_object_array(obj, name, &objects); |
||||
continue; |
||||
} |
||||
if (obj->type == OBJ_TREE) { |
||||
process_tree(revs, (struct tree *)obj, &objects, |
||||
NULL, name); |
||||
continue; |
||||
} |
||||
if (obj->type == OBJ_BLOB) { |
||||
process_blob(revs, (struct blob *)obj, &objects, |
||||
NULL, name); |
||||
continue; |
||||
} |
||||
die("unknown pending object %s (%s)", |
||||
sha1_to_hex(obj->sha1), name); |
||||
} |
||||
for (i = 0; i < objects.nr; i++) |
||||
show_object(&objects.objects[i]); |
||||
} |
@ -0,0 +1,12 @@
@@ -0,0 +1,12 @@
|
||||
#ifndef LIST_OBJECTS_H |
||||
#define LIST_OBJECTS_H |
||||
|
||||
typedef void (*show_commit_fn)(struct commit *); |
||||
typedef void (*show_object_fn)(struct object_array_entry *); |
||||
typedef void (*show_edge_fn)(struct commit *); |
||||
|
||||
void traverse_commit_list(struct rev_info *revs, show_commit_fn, show_object_fn); |
||||
|
||||
void mark_edges_uninteresting(struct commit_list *, struct rev_info *, show_edge_fn); |
||||
|
||||
#endif |
@ -0,0 +1,74 @@
@@ -0,0 +1,74 @@
|
||||
#include "pkt-line.h" |
||||
#include "sideband.h" |
||||
|
||||
/* |
||||
* Receive multiplexed output stream over git native protocol. |
||||
* in_stream is the input stream from the remote, which carries data |
||||
* in pkt_line format with band designator. Demultiplex it into out |
||||
* and err and return error appropriately. Band #1 carries the |
||||
* primary payload. Things coming over band #2 is not necessarily |
||||
* error; they are usually informative message on the standard error |
||||
* stream, aka "verbose"). A message over band #3 is a signal that |
||||
* the remote died unexpectedly. A flush() concludes the stream. |
||||
*/ |
||||
int recv_sideband(const char *me, int in_stream, int out, int err, char *buf, int bufsz) |
||||
{ |
||||
while (1) { |
||||
int len = packet_read_line(in_stream, buf, bufsz); |
||||
if (len == 0) |
||||
break; |
||||
if (len < 1) { |
||||
len = sprintf(buf, "%s: protocol error: no band designator\n", me); |
||||
safe_write(err, buf, len); |
||||
return SIDEBAND_PROTOCOL_ERROR; |
||||
} |
||||
len--; |
||||
switch (buf[0] & 0xFF) { |
||||
case 3: |
||||
safe_write(err, "remote: ", 8); |
||||
safe_write(err, buf+1, len); |
||||
safe_write(err, "\n", 1); |
||||
return SIDEBAND_REMOTE_ERROR; |
||||
case 2: |
||||
safe_write(err, "remote: ", 8); |
||||
safe_write(err, buf+1, len); |
||||
continue; |
||||
case 1: |
||||
safe_write(out, buf+1, len); |
||||
continue; |
||||
default: |
||||
len = sprintf(buf + 1, |
||||
"%s: protocol error: bad band #%d\n", |
||||
me, buf[0] & 0xFF); |
||||
safe_write(err, buf+1, len); |
||||
return SIDEBAND_PROTOCOL_ERROR; |
||||
} |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
/* |
||||
* fd is connected to the remote side; send the sideband data |
||||
* over multiplexed packet stream. |
||||
*/ |
||||
ssize_t send_sideband(int fd, int band, const char *data, ssize_t sz, int packet_max) |
||||
{ |
||||
ssize_t ssz = sz; |
||||
const char *p = data; |
||||
|
||||
while (sz) { |
||||
unsigned n; |
||||
char hdr[5]; |
||||
|
||||
n = sz; |
||||
if (packet_max - 5 < n) |
||||
n = packet_max - 5; |
||||
sprintf(hdr, "%04x", n + 5); |
||||
hdr[4] = band; |
||||
safe_write(fd, hdr, 5); |
||||
safe_write(fd, p, n); |
||||
p += n; |
||||
sz -= n; |
||||
} |
||||
return ssz; |
||||
} |
@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
|
||||
#ifndef SIDEBAND_H |
||||
#define SIDEBAND_H |
||||
|
||||
#define SIDEBAND_PROTOCOL_ERROR -2 |
||||
#define SIDEBAND_REMOTE_ERROR -1 |
||||
|
||||
#define DEFAULT_PACKET_MAX 1000 |
||||
#define LARGE_PACKET_MAX 65520 |
||||
|
||||
int recv_sideband(const char *me, int in_stream, int out, int err, char *, int); |
||||
ssize_t send_sideband(int fd, int band, const char *data, ssize_t sz, int packet_max); |
||||
|
||||
#endif |
@ -0,0 +1,115 @@
@@ -0,0 +1,115 @@
|
||||
#!/bin/sh |
||||
# |
||||
# Copyright (c) 2005 Junio C Hamano |
||||
# |
||||
|
||||
test_description='git-apply boundary tests |
||||
|
||||
' |
||||
. ./test-lib.sh |
||||
|
||||
L="c d e f g h i j k l m n o p q r s t u v w x" |
||||
|
||||
test_expect_success setup ' |
||||
for i in b '"$L"' y |
||||
do |
||||
echo $i |
||||
done >victim && |
||||
cat victim >original && |
||||
git update-index --add victim && |
||||
|
||||
: add to the head |
||||
for i in a b '"$L"' y |
||||
do |
||||
echo $i |
||||
done >victim && |
||||
cat victim >add-a-expect && |
||||
git diff victim >add-a-patch.with && |
||||
git diff --unified=0 >add-a-patch.without && |
||||
|
||||
: modify at the head |
||||
for i in a '"$L"' y |
||||
do |
||||
echo $i |
||||
done >victim && |
||||
cat victim >mod-a-expect && |
||||
git diff victim >mod-a-patch.with && |
||||
git diff --unified=0 >mod-a-patch.without && |
||||
|
||||
: remove from the head |
||||
for i in '"$L"' y |
||||
do |
||||
echo $i |
||||
done >victim && |
||||
cat victim >del-a-expect && |
||||
git diff victim >del-a-patch.with |
||||
git diff --unified=0 >del-a-patch.without && |
||||
|
||||
: add to the tail |
||||
for i in b '"$L"' y z |
||||
do |
||||
echo $i |
||||
done >victim && |
||||
cat victim >add-z-expect && |
||||
git diff victim >add-z-patch.with && |
||||
git diff --unified=0 >add-z-patch.without && |
||||
|
||||
: modify at the tail |
||||
for i in a '"$L"' y |
||||
do |
||||
echo $i |
||||
done >victim && |
||||
cat victim >mod-z-expect && |
||||
git diff victim >mod-z-patch.with && |
||||
git diff --unified=0 >mod-z-patch.without && |
||||
|
||||
: remove from the tail |
||||
for i in b '"$L"' |
||||
do |
||||
echo $i |
||||
done >victim && |
||||
cat victim >del-z-expect && |
||||
git diff victim >del-z-patch.with |
||||
git diff --unified=0 >del-z-patch.without && |
||||
|
||||
: done |
||||
' |
||||
|
||||
for with in with without |
||||
do |
||||
case "$with" in |
||||
with) u= ;; |
||||
without) u='--unidiff-zero ' ;; |
||||
esac |
||||
for kind in add-a add-z mod-a mod-z del-a del-z |
||||
do |
||||
test_expect_success "apply $kind-patch $with context" ' |
||||
cat original >victim && |
||||
git update-index victim && |
||||
git apply --index '"$u$kind-patch.$with"' || { |
||||
cat '"$kind-patch.$with"' |
||||
(exit 1) |
||||
} && |
||||
diff -u '"$kind"'-expect victim |
||||
' |
||||
done |
||||
done |
||||
|
||||
for kind in add-a add-z mod-a mod-z del-a del-z |
||||
do |
||||
rm -f $kind-ng.without |
||||
sed -e "s/^diff --git /diff /" \ |
||||
-e '/^index /d' \ |
||||
<$kind-patch.without >$kind-ng.without |
||||
test_expect_success "apply non-git $kind-patch without context" ' |
||||
cat original >victim && |
||||
git update-index victim && |
||||
git apply --unidiff-zero --index '"$kind-ng.without"' || { |
||||
cat '"$kind-ng.without"' |
||||
(exit 1) |
||||
} && |
||||
diff -u '"$kind"'-expect victim |
||||
' |
||||
done |
||||
|
||||
test_done |
@ -0,0 +1,274 @@
@@ -0,0 +1,274 @@
|
||||
#include "wt-status.h" |
||||
#include "color.h" |
||||
#include "cache.h" |
||||
#include "object.h" |
||||
#include "dir.h" |
||||
#include "commit.h" |
||||
#include "diff.h" |
||||
#include "revision.h" |
||||
#include "diffcore.h" |
||||
|
||||
int wt_status_use_color = 0; |
||||
static char wt_status_colors[][COLOR_MAXLEN] = { |
||||
"", /* WT_STATUS_HEADER: normal */ |
||||
"\033[32m", /* WT_STATUS_UPDATED: green */ |
||||
"\033[31m", /* WT_STATUS_CHANGED: red */ |
||||
"\033[31m", /* WT_STATUS_UNTRACKED: red */ |
||||
}; |
||||
|
||||
static int parse_status_slot(const char *var, int offset) |
||||
{ |
||||
if (!strcasecmp(var+offset, "header")) |
||||
return WT_STATUS_HEADER; |
||||
if (!strcasecmp(var+offset, "updated")) |
||||
return WT_STATUS_UPDATED; |
||||
if (!strcasecmp(var+offset, "changed")) |
||||
return WT_STATUS_CHANGED; |
||||
if (!strcasecmp(var+offset, "untracked")) |
||||
return WT_STATUS_UNTRACKED; |
||||
die("bad config variable '%s'", var); |
||||
} |
||||
|
||||
static const char* color(int slot) |
||||
{ |
||||
return wt_status_use_color ? wt_status_colors[slot] : ""; |
||||
} |
||||
|
||||
void wt_status_prepare(struct wt_status *s) |
||||
{ |
||||
unsigned char sha1[20]; |
||||
const char *head; |
||||
|
||||
s->is_initial = get_sha1("HEAD", sha1) ? 1 : 0; |
||||
|
||||
head = resolve_ref("HEAD", sha1, 0, NULL); |
||||
s->branch = head ? xstrdup(head) : NULL; |
||||
|
||||
s->reference = "HEAD"; |
||||
s->amend = 0; |
||||
s->verbose = 0; |
||||
s->commitable = 0; |
||||
s->untracked = 0; |
||||
} |
||||
|
||||
static void wt_status_print_header(const char *main, const char *sub) |
||||
{ |
||||
const char *c = color(WT_STATUS_HEADER); |
||||
color_printf_ln(c, "# %s:", main); |
||||
color_printf_ln(c, "# (%s)", sub); |
||||
color_printf_ln(c, "#"); |
||||
} |
||||
|
||||
static void wt_status_print_trailer(void) |
||||
{ |
||||
color_printf_ln(color(WT_STATUS_HEADER), "#"); |
||||
} |
||||
|
||||
static void wt_status_print_filepair(int t, struct diff_filepair *p) |
||||
{ |
||||
const char *c = color(t); |
||||
color_printf(color(WT_STATUS_HEADER), "#\t"); |
||||
switch (p->status) { |
||||
case DIFF_STATUS_ADDED: |
||||
color_printf(c, "new file: %s", p->one->path); break; |
||||
case DIFF_STATUS_COPIED: |
||||
color_printf(c, "copied: %s -> %s", |
||||
p->one->path, p->two->path); |
||||
break; |
||||
case DIFF_STATUS_DELETED: |
||||
color_printf(c, "deleted: %s", p->one->path); break; |
||||
case DIFF_STATUS_MODIFIED: |
||||
color_printf(c, "modified: %s", p->one->path); break; |
||||
case DIFF_STATUS_RENAMED: |
||||
color_printf(c, "renamed: %s -> %s", |
||||
p->one->path, p->two->path); |
||||
break; |
||||
case DIFF_STATUS_TYPE_CHANGED: |
||||
color_printf(c, "typechange: %s", p->one->path); break; |
||||
case DIFF_STATUS_UNKNOWN: |
||||
color_printf(c, "unknown: %s", p->one->path); break; |
||||
case DIFF_STATUS_UNMERGED: |
||||
color_printf(c, "unmerged: %s", p->one->path); break; |
||||
default: |
||||
die("bug: unhandled diff status %c", p->status); |
||||
} |
||||
printf("\n"); |
||||
} |
||||
|
||||
static void wt_status_print_updated_cb(struct diff_queue_struct *q, |
||||
struct diff_options *options, |
||||
void *data) |
||||
{ |
||||
struct wt_status *s = data; |
||||
int shown_header = 0; |
||||
int i; |
||||
if (q->nr) { |
||||
} |
||||
for (i = 0; i < q->nr; i++) { |
||||
if (q->queue[i]->status == 'U') |
||||
continue; |
||||
if (!shown_header) { |
||||
wt_status_print_header("Updated but not checked in", |
||||
"will commit"); |
||||
s->commitable = 1; |
||||
shown_header = 1; |
||||
} |
||||
wt_status_print_filepair(WT_STATUS_UPDATED, q->queue[i]); |
||||
} |
||||
if (shown_header) |
||||
wt_status_print_trailer(); |
||||
} |
||||
|
||||
static void wt_status_print_changed_cb(struct diff_queue_struct *q, |
||||
struct diff_options *options, |
||||
void *data) |
||||
{ |
||||
int i; |
||||
if (q->nr) |
||||
wt_status_print_header("Changed but not updated", |
||||
"use git-update-index to mark for commit"); |
||||
for (i = 0; i < q->nr; i++) |
||||
wt_status_print_filepair(WT_STATUS_CHANGED, q->queue[i]); |
||||
if (q->nr) |
||||
wt_status_print_trailer(); |
||||
} |
||||
|
||||
void wt_status_print_initial(struct wt_status *s) |
||||
{ |
||||
int i; |
||||
read_cache(); |
||||
if (active_nr) { |
||||
s->commitable = 1; |
||||
wt_status_print_header("Updated but not checked in", |
||||
"will commit"); |
||||
} |
||||
for (i = 0; i < active_nr; i++) { |
||||
color_printf(color(WT_STATUS_HEADER), "#\t"); |
||||
color_printf_ln(color(WT_STATUS_UPDATED), "new file: %s", |
||||
active_cache[i]->name); |
||||
} |
||||
if (active_nr) |
||||
wt_status_print_trailer(); |
||||
} |
||||
|
||||
static void wt_status_print_updated(struct wt_status *s) |
||||
{ |
||||
struct rev_info rev; |
||||
const char *argv[] = { NULL, NULL, NULL }; |
||||
argv[1] = s->reference; |
||||
init_revisions(&rev, NULL); |
||||
setup_revisions(2, argv, &rev, NULL); |
||||
rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK; |
||||
rev.diffopt.format_callback = wt_status_print_updated_cb; |
||||
rev.diffopt.format_callback_data = s; |
||||
rev.diffopt.detect_rename = 1; |
||||
run_diff_index(&rev, 1); |
||||
} |
||||
|
||||
static void wt_status_print_changed(struct wt_status *s) |
||||
{ |
||||
struct rev_info rev; |
||||
const char *argv[] = { NULL, NULL }; |
||||
init_revisions(&rev, ""); |
||||
setup_revisions(1, argv, &rev, NULL); |
||||
rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK; |
||||
rev.diffopt.format_callback = wt_status_print_changed_cb; |
||||
rev.diffopt.format_callback_data = s; |
||||
run_diff_files(&rev, 0); |
||||
} |
||||
|
||||
static void wt_status_print_untracked(const struct wt_status *s) |
||||
{ |
||||
struct dir_struct dir; |
||||
const char *x; |
||||
int i; |
||||
int shown_header = 0; |
||||
|
||||
memset(&dir, 0, sizeof(dir)); |
||||
|
||||
dir.exclude_per_dir = ".gitignore"; |
||||
if (!s->untracked) { |
||||
dir.show_other_directories = 1; |
||||
dir.hide_empty_directories = 1; |
||||
} |
||||
x = git_path("info/exclude"); |
||||
if (file_exists(x)) |
||||
add_excludes_from_file(&dir, x); |
||||
|
||||
read_directory(&dir, ".", "", 0); |
||||
for(i = 0; i < dir.nr; i++) { |
||||
/* check for matching entry, which is unmerged; lifted from |
||||
* builtin-ls-files:show_other_files */ |
||||
struct dir_entry *ent = dir.entries[i]; |
||||
int pos = cache_name_pos(ent->name, ent->len); |
||||
struct cache_entry *ce; |
||||
if (0 <= pos) |
||||
die("bug in wt_status_print_untracked"); |
||||
pos = -pos - 1; |
||||
if (pos < active_nr) { |
||||
ce = active_cache[pos]; |
||||
if (ce_namelen(ce) == ent->len && |
||||
!memcmp(ce->name, ent->name, ent->len)) |
||||
continue; |
||||
} |
||||
if (!shown_header) { |
||||
wt_status_print_header("Untracked files", |
||||
"use \"git add\" to add to commit"); |
||||
shown_header = 1; |
||||
} |
||||
color_printf(color(WT_STATUS_HEADER), "#\t"); |
||||
color_printf_ln(color(WT_STATUS_UNTRACKED), "%.*s", |
||||
ent->len, ent->name); |
||||
} |
||||
} |
||||
|
||||
static void wt_status_print_verbose(struct wt_status *s) |
||||
{ |
||||
struct rev_info rev; |
||||
const char *argv[] = { NULL, NULL, NULL }; |
||||
argv[1] = s->reference; |
||||
init_revisions(&rev, NULL); |
||||
setup_revisions(2, argv, &rev, NULL); |
||||
rev.diffopt.output_format |= DIFF_FORMAT_PATCH; |
||||
rev.diffopt.detect_rename = 1; |
||||
run_diff_index(&rev, 1); |
||||
} |
||||
|
||||
void wt_status_print(struct wt_status *s) |
||||
{ |
||||
if (s->branch && strcmp(s->branch, "refs/heads/master")) |
||||
color_printf_ln(color(WT_STATUS_HEADER), |
||||
"# On branch %s", s->branch); |
||||
|
||||
if (s->is_initial) { |
||||
color_printf_ln(color(WT_STATUS_HEADER), "#"); |
||||
color_printf_ln(color(WT_STATUS_HEADER), "# Initial commit"); |
||||
color_printf_ln(color(WT_STATUS_HEADER), "#"); |
||||
wt_status_print_initial(s); |
||||
} |
||||
else { |
||||
wt_status_print_updated(s); |
||||
discard_cache(); |
||||
} |
||||
|
||||
wt_status_print_changed(s); |
||||
wt_status_print_untracked(s); |
||||
|
||||
if (s->verbose && !s->is_initial) |
||||
wt_status_print_verbose(s); |
||||
if (!s->commitable) |
||||
printf("%s\n", s->amend ? "# No changes" : "nothing to commit"); |
||||
} |
||||
|
||||
int git_status_config(const char *k, const char *v) |
||||
{ |
||||
if (!strcmp(k, "status.color")) { |
||||
wt_status_use_color = git_config_colorbool(k, v); |
||||
return 0; |
||||
} |
||||
if (!strncmp(k, "status.color.", 13)) { |
||||
int slot = parse_status_slot(k, 13); |
||||
color_parse(v, k, wt_status_colors[slot]); |
||||
} |
||||
return git_default_config(k, v); |
||||
} |
@ -0,0 +1,25 @@
@@ -0,0 +1,25 @@
|
||||
#ifndef STATUS_H |
||||
#define STATUS_H |
||||
|
||||
enum color_wt_status { |
||||
WT_STATUS_HEADER, |
||||
WT_STATUS_UPDATED, |
||||
WT_STATUS_CHANGED, |
||||
WT_STATUS_UNTRACKED, |
||||
}; |
||||
|
||||
struct wt_status { |
||||
int is_initial; |
||||
char *branch; |
||||
const char *reference; |
||||
int commitable; |
||||
int verbose; |
||||
int amend; |
||||
int untracked; |
||||
}; |
||||
|
||||
int git_status_config(const char *var, const char *value); |
||||
void wt_status_prepare(struct wt_status *s); |
||||
void wt_status_print(struct wt_status *s); |
||||
|
||||
#endif /* STATUS_H */ |
Loading…
Reference in new issue