Browse Source

Merge refs/heads/master from .

maint
Junio C Hamano 20 years ago
parent
commit
89305da8a2
  1. 107
      Documentation/git-show-branch.txt
  2. 71
      Documentation/git-show-branches-script.txt
  3. 2
      Documentation/git.txt
  4. 3
      Makefile
  5. 21
      commit.c
  6. 2
      commit.h
  7. 10
      git-diff-script
  8. 61
      git-show-branches-script
  9. 108
      ls-files.c
  10. 41
      sha1_name.c
  11. 339
      show-branch.c

107
Documentation/git-show-branch.txt

@ -0,0 +1,107 @@
git-show-branch(1)
==================
v0.99.4, Aug 2005

NAME
----
git-show-branch - Show branches and their commits.

SYNOPSIS
--------
'git show-branch [--all] [--heads] [--tags] [--more=<n>] [--merge-base] <reference>...'

DESCRIPTION
-----------
Shows the head commits from the named <reference> (or all refs under
$GIT_DIR/refs/heads), and displays concise list of commit logs
to show their relationship semi-visually.

OPTIONS
-------
<reference>::
Name of the reference under $GIT_DIR/refs/.

--all --heads --tags::
Show all refs under $GIT_DIR/refs, $GIT_DIR/refs/heads,
and $GIT_DIR/refs/tags, respectively.

--more=<n>::
Usually the command stops output upon showing the commit
that is the common ancestor of all the branches. This
flag tells the command to go <n> commits beyond that.

--merge-base::
Instead of showing the commit list, just act like the
'git-merge-base' command except that it can accept more
than two heads.


OUTPUT
------
Given N <references>, the first N lines are the one-line
description from their commit message. The branch head that is
pointed at by $GIT_DIR/HEAD is prefixed with an asterisk '*'
character while other heads are prefixed with a '!' character.

Following these N lines, one-line log for each commit is
displayed, indented N places. If a commit is on the I-th
branch, the I-th indentation character shows a '+' sign;
otherwise it shows a space. Each commit shows a short name that
can be used as an exended SHA1 to name that commit.

The following example shows three branches, "master", "fixes"
and "mhf":

------------------------------------------------
$ git show-branch master fixes mhf
! [master] Add 'git show-branch'.
! [fixes] Introduce "reset type" flag to "git reset"
! [mhf] Allow "+remote:local" refspec to cause --force when fetching.
---
+ [mhf] Allow "+remote:local" refspec to cause --force when fetching.
+ [mhf~1] Use git-octopus when pulling more than one heads.
+ [fixes] Introduce "reset type" flag to "git reset"
+ [mhf~2] "git fetch --force".
+ [mhf~3] Use .git/remote/origin, not .git/branches/origin.
+ [mhf~4] Make "git pull" and "git fetch" default to origin
+ [mhf~5] Infamous 'octopus merge'
+ [mhf~6] Retire git-parse-remote.
+ [mhf~7] Multi-head fetch.
+ [mhf~8] Start adding the $GIT_DIR/remotes/ support.
+++ [master] Add 'git show-branch'.
------------------------------------------------

These three branches all forked from a common commit, [master],
whose commit message is "Add 'git show-branch'. "fixes" branch
adds one commit 'Introduce "reset type"'. "mhf" branch has many
other commits.

When only one head is given, the output format changes slightly
to conserve space. The '+' sign to show which commit is
reachable from which head and the first N lines to show the list
of heads being displayed are both meaningless so they are
omitted. Also the label given to each commit does not repeat
the name of the branch because it is obvious.

------------------------------------------------
$ git show-branch --more=4 master
[master] Add 'git show-branch'.
[~1] Add a new extended SHA1 syntax <name>~<num>
[~2] Fix "git-diff-script A B"
[~3] git-ls-files: generalized pathspecs
[~4] Make "git-ls-files" work in subdirectories
------------------------------------------------

Author
------
Written by Junio C Hamano <junkio@cox.net>


Documentation
--------------
Documentation by Junio C Hamano.


GIT
---
Part of the link:git.html[git] suite

71
Documentation/git-show-branches-script.txt

@ -1,71 +0,0 @@
git-show-branches-script(1)
===========================
v0.99.4, Aug 2005

NAME
----
git-show-branches-script - Show branches and their commits.

SYNOPSIS
--------
'git show-branches <reference>...'

DESCRIPTION
-----------
Shows the head commits from the named <reference> (or all refs under
$GIT_DIR/refs/heads), and displays concise list of commit logs
to show their relationship semi-visually.

OPTIONS
-------
<reference>::
Name of the reference under $GIT_DIR/refs/heads/.


OUTPUT
------
Given N <references>, the first N lines are the one-line
description from their commit message. The branch head that is
pointed at by $GIT_DIR/HEAD is prefixed with an asterisk '*'
character while other heads are prefixed with a '!' character.

Following these N lines, one-line log for each commit is
displayed, indented N places. If a commit is on the I-th
branch, the I-th indentation character shows a '+' sign;
otherwise it shows a space.

The following example shows three branches, "pu", "master" and
"rc":

------------------------------------------------
* [pu] Add cheap local clone '-s' flag to git-clone-script
! [master] Documentation updates.
! [rc] Merge master into rc
+ Add cheap local clone '-s' flag to git-clone-script
+ Alternate object pool mechanism updates.
+ Audit rev-parse users.
++ Documentation updates.
+ Merge master into rc
+++ [PATCH] plug memory leak in diff.c::diff_free_filepair()
------------------------------------------------

These three branches all forked from a common commit, "[PATCH]
plug memory leak...", and "rc" has one commit ahead of it. The
"master" branch has one different commit that is also shared by
"pu" branch, and "pu" branch has three more commits on top of
"master" branch.


Author
------
Written by Junio C Hamano <junkio@cox.net>


Documentation
--------------
Documentation by Junio C Hamano.


GIT
---
Part of the link:git.html[git] suite

2
Documentation/git.txt

@ -208,7 +208,7 @@ link:git-pull-script.html[git-pull-script]::
link:git-commit-script.html[git-commit-script]:: link:git-commit-script.html[git-commit-script]::
Record changes to the repository. Record changes to the repository.


link:git-show-branches-script.html[git-show-branches-script]:: link:git-show-branch.html[git-show-branch]::
Show branches and their commits. Show branches and their commits.


link:git-repack-script.html[git-repack-script]:: link:git-repack-script.html[git-repack-script]::

3
Makefile

@ -71,7 +71,6 @@ SCRIPTS=git git-apply-patch-script git-merge-one-file-script git-prune-script \
SCRIPTS += git-count-objects-script SCRIPTS += git-count-objects-script
# SCRIPTS += git-send-email-script # SCRIPTS += git-send-email-script
SCRIPTS += git-revert-script SCRIPTS += git-revert-script
SCRIPTS += git-show-branches-script


PROG= git-update-cache git-diff-files git-init-db git-write-tree \ PROG= git-update-cache git-diff-files git-init-db git-write-tree \
git-read-tree git-commit-tree git-cat-file git-fsck-cache \ git-read-tree git-commit-tree git-cat-file git-fsck-cache \
@ -84,7 +83,7 @@ PROG= git-update-cache git-diff-files git-init-db git-write-tree \
git-diff-stages git-rev-parse git-patch-id git-pack-objects \ git-diff-stages git-rev-parse git-patch-id git-pack-objects \
git-unpack-objects git-verify-pack git-receive-pack git-send-pack \ git-unpack-objects git-verify-pack git-receive-pack git-send-pack \
git-prune-packed git-fetch-pack git-upload-pack git-clone-pack \ git-prune-packed git-fetch-pack git-upload-pack git-clone-pack \
git-show-index git-daemon git-var git-peek-remote \ git-show-index git-daemon git-var git-peek-remote git-show-branch \
git-update-server-info git-show-rev-cache git-build-rev-cache git-update-server-info git-show-rev-cache git-build-rev-cache


ifndef NO_CURL ifndef NO_CURL

21
commit.c

@ -38,23 +38,32 @@ enum cmit_fmt get_commit_format(const char *arg)
die("invalid --pretty format"); die("invalid --pretty format");
} }


static struct commit *check_commit(struct object *obj, const unsigned char *sha1) static struct commit *check_commit(struct object *obj,
const unsigned char *sha1,
int quiet)
{ {
if (obj->type != commit_type) { if (obj->type != commit_type) {
error("Object %s is a %s, not a commit", if (!quiet)
sha1_to_hex(sha1), obj->type); error("Object %s is a %s, not a commit",
sha1_to_hex(sha1), obj->type);
return NULL; return NULL;
} }
return (struct commit *) obj; return (struct commit *) obj;
} }


struct commit *lookup_commit_reference(const unsigned char *sha1) struct commit *lookup_commit_reference_gently(const unsigned char *sha1,
int quiet)
{ {
struct object *obj = deref_tag(parse_object(sha1)); struct object *obj = deref_tag(parse_object(sha1));


if (!obj) if (!obj)
return NULL; return NULL;
return check_commit(obj, sha1); return check_commit(obj, sha1, quiet);
}

struct commit *lookup_commit_reference(const unsigned char *sha1)
{
return lookup_commit_reference_gently(sha1, 0);
} }


struct commit *lookup_commit(const unsigned char *sha1) struct commit *lookup_commit(const unsigned char *sha1)
@ -69,7 +78,7 @@ struct commit *lookup_commit(const unsigned char *sha1)
} }
if (!obj->type) if (!obj->type)
obj->type = commit_type; obj->type = commit_type;
return check_commit(obj, sha1); return check_commit(obj, sha1, 0);
} }


static unsigned long parse_commit_date(const char *buf) static unsigned long parse_commit_date(const char *buf)

2
commit.h

@ -21,6 +21,8 @@ extern const char *commit_type;


struct commit *lookup_commit(const unsigned char *sha1); struct commit *lookup_commit(const unsigned char *sha1);
struct commit *lookup_commit_reference(const unsigned char *sha1); struct commit *lookup_commit_reference(const unsigned char *sha1);
struct commit *lookup_commit_reference_gently(const unsigned char *sha1,
int quiet);


int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size); int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size);



10
git-diff-script

@ -8,8 +8,14 @@ case "${#rev[*]}" in
1) 1)
git-diff-cache -M -p "$@";; git-diff-cache -M -p "$@";;
2) 2)
begin=$(echo "${rev[1]}" | tr -d '^') case "${rev[1]}" in
end="${rev[0]}" ^?*)
begin=$(echo "${rev[1]}" | tr -d '^')
end="${rev[0]}" ;;
*)
begin="${rev[0]}"
end="${rev[1]}" ;;
esac
git-diff-tree -M -p $flags $begin $end $files;; git-diff-tree -M -p $flags $begin $end $files;;
*) *)
echo "I don't understand" echo "I don't understand"

61
git-show-branches-script

@ -1,61 +0,0 @@
#!/bin/sh
#
# Show refs and their recent commits.
#

. git-sh-setup-script || die "Not a git repository"

usage () {
die "usage: $0 <ref>..."
}

headref=`readlink $GIT_DIR/HEAD`

case "$(git-rev-parse --no-revs)" in '') ;; *) usage ;; esac
revs=$(git-rev-parse --revs-only --symbolic --no-flags "$@")
flags=$(git-rev-parse --revs-only --flags "$@")
case "$revs" in
'')
revs=$(git-rev-parse --symbolic --all | sed -ne 's|^refs/heads/||p' |
sort)
shift ;;
esac
set x $revs
shift

hh= in=
for ref
do
case "/$headref" in
*/"$ref") H='*' ;;
*) H='!' ;;
esac
h=`git-rev-parse --verify "$ref^0" 2>/dev/null` || continue
l=`git-log-script --max-count=1 --pretty=oneline "$h" |
sed -e 's/^[^ ]* //'`
hh="$hh $h"
echo "$in$H [$ref] $l"
in="$in "
done
set x $hh
shift

git-rev-list --pretty=oneline $flags $@ |
while read v l
do
in=''
for h
do
b=`git-merge-base $h $v`
case "$b" in
$v) in="$in+" ;;
*) in="$in " ;;
esac
done

echo "$in $l"
case "$in" in
*' '*) ;;
*) break ;;
esac
done

108
ls-files.c

@ -21,7 +21,7 @@ static int line_terminator = '\n';


static int prefix_len = 0, prefix_offset = 0; static int prefix_len = 0, prefix_offset = 0;
static const char *prefix = NULL; static const char *prefix = NULL;
static const char *glob = NULL; static const char **pathspec = NULL;


static const char *tag_cached = ""; static const char *tag_cached = "";
static const char *tag_unmerged = ""; static const char *tag_unmerged = "";
@ -302,6 +302,33 @@ static int cmp_name(const void *p1, const void *p2)
e2->name, e2->len); e2->name, e2->len);
} }


/*
* Match a pathspec against a filename. The first "len" characters
* are the common prefix
*/
static int match(const char **spec, const char *filename, int len)
{
const char *m;

while ((m = *spec++) != NULL) {
int matchlen = strlen(m + len);

if (!matchlen)
return 1;
if (!strncmp(m + len, filename + len, matchlen)) {
if (m[len + matchlen - 1] == '/')
return 1;
switch (filename[len + matchlen]) {
case '/': case '\0':
return 1;
}
}
if (!fnmatch(m + len, filename + len, 0))
return 1;
}
return 0;
}

static void show_dir_entry(const char *tag, struct nond_on_fs *ent) static void show_dir_entry(const char *tag, struct nond_on_fs *ent)
{ {
int len = prefix_len; int len = prefix_len;
@ -310,7 +337,7 @@ static void show_dir_entry(const char *tag, struct nond_on_fs *ent)
if (len >= ent->len) if (len >= ent->len)
die("git-ls-files: internal error - directory entry not superset of prefix"); die("git-ls-files: internal error - directory entry not superset of prefix");


if (glob && fnmatch(glob, ent->name + len, 0)) if (pathspec && !match(pathspec, ent->name, len))
return; return;


printf("%s%s%c", tag, ent->name + offset, line_terminator); printf("%s%s%c", tag, ent->name + offset, line_terminator);
@ -373,7 +400,7 @@ static void show_ce_entry(const char *tag, struct cache_entry *ce)
if (len >= ce_namelen(ce)) if (len >= ce_namelen(ce))
die("git-ls-files: internal error - cache entry not superset of prefix"); die("git-ls-files: internal error - cache entry not superset of prefix");


if (glob && fnmatch(glob, ce->name + len, 0)) if (pathspec && !match(pathspec, ce->name, len))
return; return;


if (!show_stage) if (!show_stage)
@ -455,36 +482,44 @@ static void prune_cache(void)
active_nr = last; active_nr = last;
} }


/* static void verify_pathspec(void)
* If the glob starts with a subdirectory, append it to
* the prefix instead, for more efficient operation.
*
* But we do not update the "prefix_offset", which tells
* how much of the name to ignore at printout.
*/
static void extend_prefix(void)
{ {
const char *p, *slash; const char **p, *n, *prev;
char c; char *real_prefix;

unsigned long max;
p = glob;
slash = NULL; prev = NULL;
while ((c = *p++) != '\0') { max = PATH_MAX;
if (c == '*') for (p = pathspec; (n = *p) != NULL; p++) {
break; int i, len = 0;
if (c == '/') for (i = 0; i < max; i++) {
slash = p; char c = n[i];
if (prev && prev[i] != c)
break;
if (c == '*' || c == '?')
break;
if (c == '/')
len = i+1;
}
prev = n;
if (len < max) {
max = len;
if (!max)
break;
}
} }
if (slash) {
int len = slash - glob; if (prefix_offset > max || memcmp(prev, prefix, prefix_offset))
char *newprefix = xmalloc(len + prefix_len + 1); die("git-ls-files: cannot generate relative filenames containing '..'");
memcpy(newprefix, prefix, prefix_len);
memcpy(newprefix + prefix_len, glob, len); real_prefix = NULL;
prefix_len += len; prefix_len = max;
newprefix[prefix_len] = 0; if (max) {
prefix = newprefix; real_prefix = xmalloc(max + 1);
glob = *slash ? slash : NULL; memcpy(real_prefix, prev, max);
real_prefix[max] = 0;
} }
prefix = real_prefix;
} }


static const char ls_files_usage[] = static const char ls_files_usage[] =
@ -499,7 +534,7 @@ int main(int argc, char **argv)


prefix = setup_git_directory(); prefix = setup_git_directory();
if (prefix) if (prefix)
prefix_offset = prefix_len = strlen(prefix); prefix_offset = strlen(prefix);


for (i = 1; i < argc; i++) { for (i = 1; i < argc; i++) {
char *arg = argv[i]; char *arg = argv[i];
@ -577,13 +612,16 @@ int main(int argc, char **argv)
prefix_offset = 0; prefix_offset = 0;
continue; continue;
} }
if (glob || *arg == '-') if (*arg == '-')
usage(ls_files_usage); usage(ls_files_usage);
glob = arg; break;
} }


if (glob) pathspec = get_pathspec(prefix, argv + i);
extend_prefix();
/* Verify that the pathspec matches the prefix */
if (pathspec)
verify_pathspec();


if (show_ignored && !exc_given) { if (show_ignored && !exc_given) {
fprintf(stderr, "%s: --ignored needs some exclude pattern\n", fprintf(stderr, "%s: --ignored needs some exclude pattern\n",

41
sha1_name.c

@ -191,9 +191,29 @@ static int get_parent(const char *name, int len,
return -1; return -1;
} }


static int get_nth_ancestor(const char *name, int len,
unsigned char *result, int generation)
{
unsigned char sha1[20];
int ret = get_sha1_1(name, len, sha1);
if (ret)
return ret;

while (generation--) {
struct commit *commit = lookup_commit_reference(sha1);

if (!commit || parse_commit(commit) || !commit->parents)
return -1;
memcpy(sha1, commit->parents->item->object.sha1, 20);
}
memcpy(result, sha1, 20);
return 0;
}

static int get_sha1_1(const char *name, int len, unsigned char *sha1) static int get_sha1_1(const char *name, int len, unsigned char *sha1)
{ {
int parent, ret; int parent, ret;
const char *cp;


/* foo^[0-9] or foo^ (== foo^1); we do not do more than 9 parents. */ /* foo^[0-9] or foo^ (== foo^1); we do not do more than 9 parents. */
if (len > 2 && name[len-2] == '^' && if (len > 2 && name[len-2] == '^' &&
@ -210,6 +230,27 @@ static int get_sha1_1(const char *name, int len, unsigned char *sha1)
if (parent >= 0) if (parent >= 0)
return get_parent(name, len, sha1, parent); return get_parent(name, len, sha1, parent);


/* "name~3" is "name^^^",
* "name~12" is "name^^^^^^^^^^^^", and
* "name~" and "name~0" are name -- not "name^0"!
*/
parent = 0;
for (cp = name + len - 1; name <= cp; cp--) {
int ch = *cp;
if ('0' <= ch && ch <= '9')
continue;
if (ch != '~')
parent = -1;
break;
}
if (!parent && *cp == '~') {
int len1 = cp - name;
cp++;
while (cp < name + len)
parent = parent * 10 + *cp++ - '0';
return get_nth_ancestor(name, len1, sha1, parent);
}

ret = get_sha1_basic(name, len, sha1); ret = get_sha1_basic(name, len, sha1);
if (!ret) if (!ret)
return 0; return 0;

339
show-branch.c

@ -0,0 +1,339 @@
#include <stdlib.h>
#include "cache.h"
#include "commit.h"
#include "refs.h"

static const char show_branch_usage[] =
"git-show-branch [--all] [--heads] [--tags] [--more=count] [<refs>...]";

#define UNINTERESTING 01

#define REV_SHIFT 2
#define MAX_REVS 29 /* should not exceed bits_per_int - REV_SHIFT */

static struct commit *interesting(struct commit_list *list)
{
while (list) {
struct commit *commit = list->item;
list = list->next;
if (commit->object.flags & UNINTERESTING)
continue;
return commit;
}
return NULL;
}

static struct commit *pop_one_commit(struct commit_list **list_p)
{
struct commit *commit;
struct commit_list *list;
list = *list_p;
commit = list->item;
*list_p = list->next;
free(list);
return commit;
}

struct commit_name {
int head_rev; /* which head's ancestor? */
int generation; /* how many parents away from head_rev */
};

/* Name the commit as nth generation ancestor of head_rev;
* we count only the first-parent relationship for naming purposes.
*/
static void name_commit(struct commit *commit, int head_rev, int nth)
{
struct commit_name *name;
if (!commit->object.util)
commit->object.util = xmalloc(sizeof(struct commit_name));
name = commit->object.util;
name->head_rev = head_rev;
name->generation = nth;
}

/* Parent is the first parent of the commit. We may name it
* as (n+1)th generation ancestor of the same head_rev as
* commit is nth generation ancestore of, if that generation
* number is better than the name it already has.
*/
static void name_parent(struct commit *commit, struct commit *parent)
{
struct commit_name *commit_name = commit->object.util;
struct commit_name *parent_name = parent->object.util;
if (!commit_name)
return;
if (!parent_name ||
commit_name->generation + 1 < parent_name->generation)
name_commit(parent, commit_name->head_rev,
commit_name->generation + 1);
}

static int mark_seen(struct commit *commit, struct commit_list **seen_p)
{
if (!commit->object.flags) {
insert_by_date(commit, seen_p);
return 1;
}
return 0;
}

static void join_revs(struct commit_list **list_p,
struct commit_list **seen_p,
int num_rev, int extra)
{
int all_mask = ((1u << (REV_SHIFT + num_rev)) - 1);
int all_revs = all_mask & ~((1u << REV_SHIFT) - 1);

while (*list_p) {
struct commit_list *parents;
struct commit *commit = pop_one_commit(list_p);
int flags = commit->object.flags & all_mask;
int nth_parent = 0;
int still_interesting = !!interesting(*list_p);

if (!still_interesting && extra < 0)
break;

mark_seen(commit, seen_p);
if ((flags & all_revs) == all_revs)
flags |= UNINTERESTING;
parents = commit->parents;

while (parents) {
struct commit *p = parents->item;
int this_flag = p->object.flags;
parents = parents->next;
nth_parent++;
if (nth_parent == 1)
name_parent(commit, p);

if ((this_flag & flags) == flags)
continue;
parse_commit(p);
if (mark_seen(p, seen_p) && !still_interesting)
extra--;
p->object.flags |= flags;
insert_by_date(p, list_p);
}
}
}

static void show_one_commit(struct commit *commit, char **head_name)
{
char pretty[128], *cp;
struct commit_name *name = commit->object.util;
pretty_print_commit(CMIT_FMT_ONELINE, commit->buffer, ~0,
pretty, sizeof(pretty));
if (!strncmp(pretty, "[PATCH] ", 8))
cp = pretty + 8;
else
cp = pretty;
if (name && head_name) {
printf("[%s", head_name[name->head_rev]);
if (name->generation)
printf("~%d", name->generation);
printf("] ");
}
puts(cp);
}

static char *ref_name[MAX_REVS + 1];
static int ref_name_cnt;

static int append_ref(const char *refname, const unsigned char *sha1)
{
struct commit *commit = lookup_commit_reference_gently(sha1, 1);
if (!commit)
return 0;
if (MAX_REVS < ref_name_cnt) {
fprintf(stderr, "warning: ignoring %s; "
"cannot handle more than %d refs",
refname, MAX_REVS);
return 0;
}
ref_name[ref_name_cnt++] = strdup(refname);
ref_name[ref_name_cnt] = NULL;
return 0;
}

static int append_head_ref(const char *refname, const unsigned char *sha1)
{
if (strncmp(refname, "refs/heads/", 11))
return 0;
return append_ref(refname + 5, sha1);
}

static int append_tag_ref(const char *refname, const unsigned char *sha1)
{
if (strncmp(refname, "refs/tags/", 10))
return 0;
return append_ref(refname + 5, sha1);
}

static void snarf_refs(int head, int tag)
{
if (head)
for_each_ref(append_head_ref);
if (tag)
for_each_ref(append_tag_ref);
}

static int rev_is_head(char *head_path, int headlen,
char *name,
unsigned char *head_sha1, unsigned char *sha1)
{
int namelen;
if ((!head_path[0]) || memcmp(head_sha1, sha1, 20))
return 0;
namelen = strlen(name);
if ((headlen < namelen) ||
memcmp(head_path + headlen - namelen, name, namelen))
return 0;
if (headlen == namelen ||
head_path[headlen - namelen - 1] == '/')
return 1;
return 0;
}

static int show_merge_base(struct commit_list *seen, int num_rev)
{
int all_mask = ((1u << (REV_SHIFT + num_rev)) - 1);
int all_revs = all_mask & ~((1u << REV_SHIFT) - 1);

while (seen) {
struct commit *commit = pop_one_commit(&seen);
int flags = commit->object.flags & all_mask;
if (!(flags & UNINTERESTING) &&
((flags & all_revs) == all_revs)) {
puts(sha1_to_hex(commit->object.sha1));
return 0;
}
}
return 1;
}

int main(int ac, char **av)
{
struct commit *rev[MAX_REVS], *commit;
struct commit_list *list = NULL, *seen = NULL;
int num_rev, i, extra = 0;
int all_heads = 0, all_tags = 0;
char head_path[128];
int head_path_len;
unsigned char head_sha1[20];
int merge_base = 0;
char **label;

while (1 < ac && av[1][0] == '-') {
char *arg = av[1];
if (!strcmp(arg, "--all"))
all_heads = all_tags = 1;
else if (!strcmp(arg, "--heads"))
all_heads = 1;
else if (!strcmp(arg, "--tags"))
all_tags = 1;
else if (!strcmp(arg, "--more"))
extra = 1;
else if (!strncmp(arg, "--more=", 7)) {
extra = atoi(arg + 7);
if (extra < 0)
usage(show_branch_usage);
}
else if (!strcmp(arg, "--merge-base"))
merge_base = 1;
else
usage(show_branch_usage);
ac--; av++;
}
ac--; av++;

if (all_heads + all_tags)
snarf_refs(all_heads, all_tags);

while (0 < ac) {
unsigned char revkey[20];
if (get_sha1(*av, revkey))
die("bad sha1 reference %s", *av);
append_ref(*av, revkey);
ac--; av++;
}

/* If still no revs, then add heads */
if (!ref_name_cnt)
snarf_refs(1, 0);

for (num_rev = 0; ref_name[num_rev]; num_rev++) {
unsigned char revkey[20];

if (MAX_REVS <= num_rev)
die("cannot handle more than %d revs.", MAX_REVS);
if (get_sha1(ref_name[num_rev], revkey))
usage(show_branch_usage);
commit = lookup_commit_reference(revkey);
if (!commit)
die("cannot find commit %s (%s)",
ref_name[num_rev], revkey);
parse_commit(commit);
if (!commit->object.util)
name_commit(commit, num_rev, 0);
mark_seen(commit, &seen);

/* rev#0 uses bit REV_SHIFT, rev#1 uses bit REV_SHIFT+1,
* and so on. REV_SHIFT bits from bit 0 are used for
* internal bookkeeping.
*/
commit->object.flags |= 1u << (num_rev + REV_SHIFT);
insert_by_date(commit, &list);
rev[num_rev] = commit;
}
join_revs(&list, &seen, num_rev, extra);

head_path_len = readlink(".git/HEAD", head_path, sizeof(head_path)-1);
if ((head_path_len < 0) || get_sha1("HEAD", head_sha1))
head_path[0] = 0;
else
head_path[head_path_len] = 0;

if (merge_base)
return show_merge_base(seen, num_rev);

/* Show list */
if (1 < num_rev) {
for (i = 0; i < num_rev; i++) {
int j;
int is_head = rev_is_head(head_path,
head_path_len,
ref_name[i],
head_sha1,
rev[i]->object.sha1);
for (j = 0; j < i; j++)
putchar(' ');
printf("%c [%s] ", is_head ? '*' : '!', ref_name[i]);
show_one_commit(rev[i], NULL);
}
for (i = 0; i < num_rev; i++)
putchar('-');
putchar('\n');
}

label = ref_name;
while (seen) {
struct commit *commit = pop_one_commit(&seen);
int this_flag = commit->object.flags;
static char *obvious[] = { "" };

if ((this_flag & UNINTERESTING) && (--extra < 0))
break;
if (1 < num_rev) {
for (i = 0; i < num_rev; i++)
putchar((this_flag & (1u << (i + REV_SHIFT)))
? '+' : ' ');
putchar(' ');
}
show_one_commit(commit, label);
if (num_rev == 1)
label = obvious;
}
return 0;
}
Loading…
Cancel
Save