* [PATCH] cg-clean shouldn't clean untracked directories without -d
From: Pavel Roskin @ 2006-01-24 17:17 UTC (permalink / raw)
To: Petr Baudis, git
Use the new squashdirs argument in list_untracked_files() to treat
untracked directories and their contents as a whole. Remove a separate
pass to find untracked directories. Adjust the testsuite accordingly.
Don't change IFS - it's no longer needed.
Signed-off-by: Pavel Roskin <proski@gnu.org>
---
cg-clean | 103 +++++++++++++-----------------------------------------
t/t9400-clean.sh | 3 +-
2 files changed, 25 insertions(+), 81 deletions(-)
diff --git a/cg-clean b/cg-clean
index b40b41b..fc6767d 100755
--- a/cg-clean
+++ b/cg-clean
@@ -53,88 +53,33 @@ done
[ ${#ARGS[*]} = 0 ] || usage
+list_untracked_files "$noexclude" squashdirs | tr '\0' '\n' |
+(cd "${_git_relpath-.}" &&
+while read -r file; do
+ if [ -z "$_git_relpath" ] || [ x"${file#$_git_relpath}" != x"$file" ]; then
+ file="${file#$_git_relpath}"
+ else
+ continue
+ fi
-clean_dirs()
-{
- dirlist="$(mktemp -t gitlsfiles.XXXXXX)"
- git-ls-files --cached |
- sed -n 's|/[^/]*$||p' |
- while IFS=$'\n' read -r dir; do
- while true; do
- echo "$dir"
- updir="${dir%/*}"
- [ "$dir" = "$updir" ] && break
- dir="$updir"
- done
- done |
- sort -u >"$dirlist"
-
- save_IFS="$IFS"
- IFS=$'\n'
-
- find ./ -type d -print |
- sed 's/^\.\///;/^$/d;/^\.git$/d;/^\.git\//d' |
- cat - "$dirlist" | sort -u |
- diff - "$dirlist" |
- sed -n 's/< //p' |
- while read -r dir; do
- if [ ! -d "$dir" ]; then
- # Perhaps directory was removed with its parent
- continue
- fi
+ if [ -d "$file" -a ! -L "$file" ]; then
if [ -z "$cleandir" ]; then
- echo "Not removing $dir/"
+ echo "Not removing $file/"
continue
fi
- [ "$quiet" ] || echo "Removing $dir/"
+ [ "$quiet" ] || echo "Removing $file/"
if [ "$cleandirhard" ]; then
- chmod -R 700 "$dir"
- fi
- $rm -rf "$dir"
- if [ -e "$dir" -o -L "$dir" ]; then
- echo "Cannot remove $dir/"
- fi
- done
-
- IFS="$save_IFS"
- rm "$dirlist"
-}
-
-clean_files()
-{
- save_IFS="$IFS"
- IFS=$'\n'
-
- list_untracked_files "$noexclude" nosquashdirs | tr '\0' '\n' |
- (cd "${_git_relpath-.}" &&
- while read -r file; do
- if [ -z "$_git_relpath" ] || [ x"${file#$_git_relpath}" != x"$file" ]; then
- file="${file#$_git_relpath}"
- else
- continue
- fi
-
- if [ -d "$file" -a ! -L "$file" ]; then
- # Sanity check, shouldn't happen
- echo "FATAL: cg-status reports directories (internal error)" >&2
- exit 1
- elif [ -e "$file" -o -L "$file" ]; then
- [ "$quiet" ] || echo "Removing $file"
- "$rm" -f "$file"
- # rm would complain itself on failure
- else
- echo "File $file has disappeared!"
+ chmod -R 700 "$file"
fi
- done)
-
- IFS="$save_IFS"
-}
-
-
-# Even if -d or -D is not specified, we want to tell user about
-# directories that are not removed
-if [ -z "$quiet" -o "$cleandir" ]; then
- ( cd "${_git_relpath-.}" && clean_dirs )
-fi
-
-clean_files
+ $rm -rf "$file"
+ if [ -e "$file" -o -L "$file" ]; then
+ echo "Cannot remove $file/"
+ fi
+ elif [ -e "$file" -o -L "$file" ]; then
+ [ "$quiet" ] || echo "Removing $file"
+ "$rm" -f "$file"
+ # rm would complain itself on failure
+ else
+ echo "File $file has disappeared!"
+ fi
+done)
diff --git a/t/t9400-clean.sh b/t/t9400-clean.sh
index 47ae0dc..98801c5 100755
--- a/t/t9400-clean.sh
+++ b/t/t9400-clean.sh
@@ -84,10 +84,8 @@ echo "ign file 3" >"repo dir/ign file 3.
echo "ign file 4" >"repo dir/ign file 4.ign1"
mklist init
-# FIXME: cg-clean shouldn't clean unknown directories without "-d"
loss='extra file 1
ign file 2.ign1
-extra dir 1/extra file 3
repo dir/extra file 2'
test_expect_success 'cg-clean in top-level dir' \
"(cg-clean && check_loss)"
@@ -99,6 +97,7 @@ test_expect_success 'cg-clean -x in top-
"(cg-clean -x && check_loss)"
loss='extra dir 1
+extra dir 1/extra file 3
repo dir/extra dir 2'
test_expect_success 'cg-clean -d in top-level dir' \
"(cg-clean -d && check_loss)"
--
Regards,
Pavel Roskin
^ permalink raw reply related
* Re: git pull on a branch semantics
From: Luben Tuikov @ 2006-01-24 15:37 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <7v7j8sc6wy.fsf@assigned-by-dhcp.cox.net>
--- Junio C Hamano <junkio@cox.net> wrote:
> You wanted to "git fetch", not "git pull" which is "fetch
> followed by merge into the current branch".
Ah, ok, thanks Junio.
Luben
^ permalink raw reply
* [PATCH] use "git <command>" instead of "git-<command>"
From: Uwe Zeisberger @ 2006-01-24 10:52 UTC (permalink / raw)
To: git
Hello,
Otherwise installations with gitexecdir != bindir are not able to determine the
right version.
---
GIT-VERSION-GEN | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)
9d9d610164ec140f3fee21475b976d3e4b998991
diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
index e3cd9fa..2085ad3 100755
--- a/GIT-VERSION-GEN
+++ b/GIT-VERSION-GEN
@@ -5,13 +5,13 @@ DEF_VER=v1.1.GIT
# First try git-describe, then see if there is a version file
# (included in release tarballs), then default
-VN=$(git-describe --abbrev=4 HEAD 2>/dev/null | sed -e 's/-/./g') ||
+VN=$(git describe --abbrev=4 HEAD 2>/dev/null | sed -e 's/-/./g') ||
VN=$(cat version) ||
VN="$DEF_VER"
VN=$(expr "$VN" : v*'\(.*\)')
-dirty=$(sh -c 'git-diff-index --name-only HEAD' 2>/dev/null) || dirty=
+dirty=$(sh -c 'git diff-index --name-only HEAD' 2>/dev/null) || dirty=
case "$dirty" in
'')
;;
--
1.1.4.g2eaa
Best regards
Uwe
--
Uwe Zeisberger
http://www.google.com/search?q=1+year+divided+by+3+in+seconds
^ permalink raw reply related
* [PATCH] diff-tree -c: show a merge commit a bit more sensibly.
From: Junio C Hamano @ 2006-01-24 9:34 UTC (permalink / raw)
To: git; +Cc: torvalds
A new option '-c' to diff-tree changes the way a merge commit is
displayed when generating a patch output. It shows a "combined
diff" (hence the option letter 'c'), which looks like this:
$ git-diff-tree --pretty -c -p fec9ebf1 | head -n 18
diff-tree fec9ebf... (from parents)
Merge: 0620db3... 8a263ae...
Author: Junio C Hamano <junkio@cox.net>
Date: Sun Jan 15 22:25:35 2006 -0800
Merge fixes up to GIT 1.1.3
diff --combined describe.c
@@ +99,18 @@
}
- static void describe(char *arg)
- static void describe(struct commit *cmit, int last_one)
++ static void describe(char *arg, int last_one)
{
+ unsigned char sha1[20];
+ struct commit *cmit;
struct commit_list *list;
Unlike "gitk", this is monochrome output. A '-' character in
the nth column means the line is from the nth parent and does
not appear in the merge result. A '+' character in the nth
column means the line appears in the merge result, and the nth
parent does not have that line. The above example shows that the
function signature was changed from either parents (hence two
'-' lines and a '++' line), and "unsigned char sha1[20]",
prefixed by a " +", was inherited from the first parent.
The code probably is still buggy with rough edges, but with my
limited tests, it did not seem to dump core ;-).
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
Makefile | 2
combine-diff.c | 436 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
diff-tree.c | 34 ++++
diff.h | 2
4 files changed, 467 insertions(+), 7 deletions(-)
create mode 100644 combine-diff.c
b80f9de155bfc183839551e19e767859d689b593
diff --git a/Makefile b/Makefile
index 3046056..552b20f 100644
--- a/Makefile
+++ b/Makefile
@@ -182,7 +182,7 @@ LIB_H = \
DIFF_OBJS = \
diff.o diffcore-break.o diffcore-order.o diffcore-pathspec.o \
- diffcore-pickaxe.o diffcore-rename.o tree-diff.o
+ diffcore-pickaxe.o diffcore-rename.o tree-diff.o combine-diff.o
LIB_OBJS = \
blob.o commit.o connect.o count-delta.o csum-file.o \
diff --git a/combine-diff.c b/combine-diff.c
new file mode 100644
index 0000000..b47eda5
--- /dev/null
+++ b/combine-diff.c
@@ -0,0 +1,436 @@
+#include "cache.h"
+#include "commit.h"
+#include "diff.h"
+#include "diffcore.h"
+#include "quote.h"
+
+struct path_list {
+ struct path_list *next;
+ int len;
+ char *path;
+ unsigned char sha1[20];
+ unsigned char parent_sha1[FLEX_ARRAY][20];
+};
+
+static int uninteresting(struct diff_filepair *p)
+{
+ if (diff_unmodified_pair(p))
+ return 1;
+ if (!S_ISREG(p->one->mode) || !S_ISREG(p->two->mode))
+ return 1;
+ return 0;
+}
+
+static struct path_list *intersect_paths(struct path_list *curr,
+ int n, int num_parent)
+{
+ struct diff_queue_struct *q = &diff_queued_diff;
+ struct path_list *p;
+ int i;
+
+ if (!curr) {
+ struct path_list *list = NULL, *tail = NULL;
+ for (i = 0; i < q->nr; i++) {
+ int len;
+ const char *path;
+ if (uninteresting(q->queue[i]))
+ continue;
+ path = q->queue[i]->two->path;
+ len = strlen(path);
+
+ p = xmalloc(sizeof(*p) + len + 1 + num_parent * 20);
+ p->path = (char*) &(p->parent_sha1[num_parent][0]);
+ memcpy(p->path, path, len);
+ p->path[len] = 0;
+ p->len = len;
+ p->next = NULL;
+ memcpy(p->sha1, q->queue[i]->two->sha1, 20);
+ memcpy(p->parent_sha1[n], q->queue[i]->one->sha1, 20);
+ if (!tail)
+ list = tail = p;
+ else {
+ tail->next = p;
+ p = tail;
+ }
+ }
+ return list;
+ }
+
+ for (p = curr; p; p = p->next) {
+ int found = 0;
+ if (!p->len)
+ continue;
+ for (i = 0; i < q->nr; i++) {
+ const char *path;
+ int len;
+
+ if (uninteresting(q->queue[i]))
+ continue;
+ path = q->queue[i]->two->path;
+ len = strlen(path);
+ if (len == p->len && !memcmp(path, p->path, len)) {
+ found = 1;
+ memcpy(p->parent_sha1[n],
+ q->queue[i]->one->sha1, 20);
+ break;
+ }
+ }
+ if (!found)
+ p->len = 0;
+ }
+ return curr;
+}
+
+struct lline {
+ struct lline *next;
+ int nth;
+ char line[FLEX_ARRAY];
+};
+
+struct sline {
+ struct lline *lost_head, **lost_tail;
+ char *bol;
+ int len;
+ unsigned long flag;
+};
+
+static char *grab_blob(const unsigned char *sha1, unsigned long *size)
+{
+ char *blob;
+ char type[20];
+ if (!memcmp(sha1, null_sha1, 20)) {
+ /* deleted blob */
+ *size = 0;
+ return xcalloc(1, 1);
+ }
+ blob = read_sha1_file(sha1, type, size);
+ if (strcmp(type, "blob"))
+ die("object '%s' is not a blob!", sha1_to_hex(sha1));
+ return blob;
+}
+
+#define TMPPATHLEN 50
+#define MAXLINELEN 10240
+
+static void write_to_temp_file(char *tmpfile, void *blob, unsigned long size)
+{
+ int fd = git_mkstemp(tmpfile, TMPPATHLEN, ".diff_XXXXXX");
+ if (fd < 0)
+ die("unable to create temp-file");
+ if (write(fd, blob, size) != size)
+ die("unable to write temp-file");
+ close(fd);
+}
+
+static void write_temp_blob(char *tmpfile, const unsigned char *sha1)
+{
+ unsigned long size;
+ void *blob;
+ blob = grab_blob(sha1, &size);
+ write_to_temp_file(tmpfile, blob, size);
+ free(blob);
+}
+
+static int parse_num(char **cp_p, unsigned int *num_p)
+{
+ char *cp = *cp_p;
+ unsigned int num = 0;
+ int read_some;
+
+ while ('0' <= *cp && *cp <= '9')
+ num = num * 10 + *cp++ - '0';
+ if (!(read_some = cp - *cp_p))
+ return -1;
+ *cp_p = cp;
+ *num_p = num;
+ return 0;
+}
+
+static int parse_hunk_header(char *line, int len,
+ unsigned int *ob, unsigned int *on,
+ unsigned int *nb, unsigned int *nn)
+{
+ char *cp;
+ cp = line + 4;
+ if (parse_num(&cp, ob)) {
+ bad_line:
+ return error("malformed diff output: %s", line);
+ }
+ if (*cp == ',') {
+ cp++;
+ if (parse_num(&cp, on))
+ goto bad_line;
+ }
+ else
+ *on = 1;
+ if (*cp++ != ' ' || *cp++ != '+')
+ goto bad_line;
+ if (parse_num(&cp, nb))
+ goto bad_line;
+ if (*cp == ',') {
+ cp++;
+ if (parse_num(&cp, nn))
+ goto bad_line;
+ }
+ else
+ *nn = 1;
+ return -!!memcmp(cp, " @@", 3);
+}
+
+static void append_lost(struct sline *sline, int n, const char *line)
+{
+ struct lline *lline;
+ int len = strlen(line);
+ if (line[len-1] == '\n')
+ len--;
+ lline = xmalloc(sizeof(*lline) + len + 1);
+ lline->next = NULL;
+ lline->nth = n;
+ if (sline->lost_head)
+ *(sline->lost_tail) = lline;
+ else
+ sline->lost_head = lline;
+ sline->lost_tail = &lline->next;
+ memcpy(lline->line, line, len);
+ lline->line[len] = 0;
+}
+
+static void combine_diff(const unsigned char *parent, const char *ourtmp,
+ struct sline *sline, int cnt, int n)
+{
+ FILE *in;
+ char parent_tmp[TMPPATHLEN];
+ char cmd[TMPPATHLEN * 2 + 1024];
+ char line[MAXLINELEN];
+ unsigned int lno, ob, on, nb, nn;
+ unsigned long pmask = ~(1UL << n);
+ struct sline *lost_bucket = NULL;
+
+ write_temp_blob(parent_tmp, parent);
+ sprintf(cmd, "diff --unified=0 -La/x -Lb/x '%s' '%s'",
+ parent_tmp, ourtmp);
+ in = popen(cmd, "r");
+ if (!in)
+ return;
+
+ lno = 1;
+ while (fgets(line, sizeof(line), in) != NULL) {
+ int len = strlen(line);
+ if (5 < len && !memcmp("@@ -", line, 4)) {
+ if (parse_hunk_header(line, len,
+ &ob, &on, &nb, &nn))
+ break;
+ lno = nb;
+ lost_bucket = &sline[nb-1]; /* sline is 0 based */
+ continue;
+ }
+ if (!lost_bucket)
+ continue;
+ switch (line[0]) {
+ case '-':
+ append_lost(lost_bucket, n, line+1);
+ break;
+ case '+':
+ sline[lno-1].flag &= pmask;
+ lno++;
+ break;
+ }
+ }
+ fclose(in);
+ unlink(parent_tmp);
+}
+
+static unsigned long context = 2;
+
+static int interesting(struct sline *sline, unsigned long all_mask)
+{
+ return ((sline->flag & all_mask) != all_mask || sline->lost_head);
+}
+
+static void make_hunks(struct sline *sline, unsigned long cnt, int num_parent)
+{
+ unsigned long all_mask = (1UL<<num_parent) - 1;
+ unsigned long mark = (1UL<<num_parent);
+ unsigned long i;
+
+ i = 0;
+ while (i < cnt) {
+ if (interesting(&sline[i], all_mask)) {
+ unsigned long j = (context < i) ? i - context : 0;
+ while (j <= i)
+ sline[j++].flag |= mark;
+ while (++i < cnt) {
+ if (!interesting(&sline[i], all_mask))
+ break;
+ sline[i].flag |= mark;
+ }
+ j = (i + context < cnt) ? i + context : cnt;
+ while (i < j)
+ sline[i++].flag |= mark;
+ continue;
+ }
+ i++;
+ }
+}
+
+static void dump_sline(struct sline *sline, int cnt, int num_parent)
+{
+ unsigned long mark = (1UL<<num_parent);
+ int i;
+ int lno = 0;
+
+ while (1) {
+ struct sline *sl = &sline[lno];
+ int hunk_end;
+ while (lno < cnt && !(sline[lno].flag & mark))
+ lno++;
+ if (cnt <= lno)
+ break;
+ for (hunk_end = lno + 1; hunk_end < cnt; hunk_end++)
+ if (!(sline[hunk_end].flag & mark))
+ break;
+ for (i = 0; i < num_parent; i++) putchar('@');
+ printf(" +%d,%d ", lno+1, hunk_end-lno);
+ for (i = 0; i < num_parent; i++) putchar('@');
+ putchar('\n');
+ while (lno < hunk_end) {
+ struct lline *ll;
+ sl = &sline[lno++];
+ ll = sl->lost_head;
+ int j;
+ while (ll) {
+ for (j = 0; j < ll->nth; j++)
+ putchar(' ');
+ putchar('-');
+ for (j = ll->nth + 1; j < num_parent; j++)
+ putchar(' ');
+ putchar(' ');
+ puts(ll->line);
+ ll = ll->next;
+ }
+ for (j = 0; j < num_parent; j++) {
+ if ((1UL<<j) & sl->flag)
+ putchar(' ');
+ else
+ putchar('+');
+ }
+ printf(" %.*s\n", sl->len, sl->bol);
+ }
+ }
+}
+
+static void show_combined_diff(struct path_list *elem, int num_parent)
+{
+ unsigned long size, cnt, lno;
+ char *result, *cp, *ep;
+ struct sline *sline; /* survived lines */
+ int i;
+ char ourtmp[TMPPATHLEN];
+
+ /* Read the result of merge first */
+ result = grab_blob(elem->sha1, &size);
+ write_to_temp_file(ourtmp, result, size);
+
+ for (cnt = 0, cp = result; cp - result < size; cp++) {
+ if (*cp == '\n')
+ cnt++;
+ }
+ if (result[size-1] != '\n')
+ cnt++; /* incomplete line */
+
+ sline = xcalloc(cnt, sizeof(*sline));
+ ep = result;
+ sline[0].bol = result;
+ for (lno = 0, cp = result; cp - result < size; cp++) {
+ if (*cp == '\n') {
+ sline[lno].len = cp - sline[lno].bol;
+ sline[lno].flag = (1UL<<num_parent) - 1;
+ lno++;
+ if (lno < cnt)
+ sline[lno].bol = cp + 1;
+ }
+ }
+ if (result[size-1] != '\n') {
+ sline[cnt-1].len = size - (sline[cnt-1].bol - result);
+ sline[cnt-1].flag = (1UL<<num_parent) - 1;
+ }
+
+ for (i = 0; i < num_parent; i++)
+ combine_diff(elem->parent_sha1[i], ourtmp, sline, cnt, i);
+
+ make_hunks(sline, cnt, num_parent);
+
+ dump_sline(sline, cnt, num_parent);
+ unlink(ourtmp);
+ free(result);
+
+ for (i = 0; i < cnt; i++) {
+ if (sline[i].lost_head) {
+ struct lline *ll = sline[i].lost_head;
+ while (ll) {
+ struct lline *tmp = ll;
+ ll = ll->next;
+ free(tmp);
+ }
+ }
+ }
+ free(sline);
+}
+
+int diff_tree_combined_merge(const unsigned char *sha1, const char *header)
+{
+ struct commit *commit = lookup_commit(sha1);
+ struct diff_options diffopts;
+ struct commit_list *parents;
+ struct path_list *p, *paths = NULL;
+ int num_parent, i, num_paths;
+
+ diff_setup(&diffopts);
+ diffopts.output_format = DIFF_FORMAT_NO_OUTPUT;
+ diffopts.recursive = 1;
+
+ /* count parents */
+ for (parents = commit->parents, num_parent = 0;
+ parents;
+ parents = parents->next, num_parent++)
+ ; /* nothing */
+
+ /* find set of paths that everybody touches */
+ for (parents = commit->parents, i = 0;
+ parents;
+ parents = parents->next, i++) {
+ struct commit *parent = parents->item;
+ diff_tree_sha1(parent->object.sha1, commit->object.sha1, "",
+ &diffopts);
+ paths = intersect_paths(paths, i, num_parent);
+ diff_flush(&diffopts);
+ }
+
+ /* find out surviving paths */
+ for (num_paths = 0, p = paths; p; p = p->next) {
+ if (p->len)
+ num_paths++;
+ }
+ if (num_paths) {
+ puts(header);
+ for (p = paths; p; p = p->next) {
+ if (!p->len)
+ continue;
+ printf("diff --combined ");
+ if (quote_c_style(p->path, NULL, NULL, 0))
+ quote_c_style(p->path, NULL, stdout, 0);
+ else
+ printf("%s", p->path);
+ putchar('\n');
+ show_combined_diff(p, num_parent);
+ }
+ }
+
+ /* Clean things up */
+ while (paths) {
+ struct path_list *tmp = paths;
+ paths = paths->next;
+ free(tmp);
+ }
+ return 0;
+}
diff --git a/diff-tree.c b/diff-tree.c
index efa2b94..79f2497 100644
--- a/diff-tree.c
+++ b/diff-tree.c
@@ -6,6 +6,7 @@ static int show_root_diff = 0;
static int no_commit_id = 0;
static int verbose_header = 0;
static int ignore_merges = 1;
+static int combine_merges = 0;
static int read_stdin = 0;
static const char *header = NULL;
@@ -79,9 +80,13 @@ static const char *generate_header(const
offset = sprintf(this_header, "%s%s ",
header_prefix,
diff_unique_abbrev(commit_sha1, abbrev));
- offset += sprintf(this_header + offset, "(from %s)\n",
- parent_sha1 ?
- diff_unique_abbrev(parent_sha1, abbrev) : "root");
+ if (commit_sha1 != parent_sha1)
+ offset += sprintf(this_header + offset, "(from %s)\n",
+ parent_sha1
+ ? diff_unique_abbrev(parent_sha1, abbrev)
+ : "root");
+ else
+ offset += sprintf(this_header + offset, "(from parents)\n");
offset += pretty_print_commit(commit_format, msg, len,
this_header + offset,
sizeof(this_header) - offset);
@@ -108,8 +113,15 @@ static int diff_tree_commit(const unsign
}
/* More than one parent? */
- if (ignore_merges && commit->parents && commit->parents->next)
- return 0;
+ if (commit->parents && commit->parents->next) {
+ if (ignore_merges)
+ return 0;
+ else if (combine_merges) {
+ header = generate_header(sha1, sha1,
+ commit->buffer);
+ return diff_tree_combined_merge(sha1, header);
+ }
+ }
for (parents = commit->parents; parents; parents = parents->next) {
struct commit *parent = parents->item;
@@ -154,7 +166,7 @@ static int diff_tree_stdin(char *line)
}
static const char diff_tree_usage[] =
-"git-diff-tree [--stdin] [-m] [-s] [-v] [--pretty] [-t] [-r] [--root] "
+"git-diff-tree [--stdin] [-m] [-c] [-s] [-v] [--pretty] [-t] [-r] [--root] "
"[<common diff options>] <tree-ish> [<tree-ish>] [<path>...]\n"
" -r diff recursively\n"
" --root include the initial commit as diff against /dev/null\n"
@@ -217,6 +229,11 @@ int main(int argc, const char **argv)
ignore_merges = 0;
continue;
}
+ if (!strcmp(arg, "-c")) {
+ ignore_merges = 0;
+ combine_merges = 1;
+ continue;
+ }
if (!strcmp(arg, "-v")) {
verbose_header = 1;
header_prefix = "diff-tree ";
@@ -245,6 +262,11 @@ int main(int argc, const char **argv)
if (diff_options.output_format == DIFF_FORMAT_PATCH)
diff_options.recursive = 1;
+ if (combine_merges) {
+ if (diff_options.output_format != DIFF_FORMAT_PATCH)
+ die("-c cannot be used without -p");
+ }
+
diff_tree_setup_paths(get_pathspec(prefix, argv));
diff_setup_done(&diff_options);
diff --git a/diff.h b/diff.h
index 5696f2a..c0e8e0e 100644
--- a/diff.h
+++ b/diff.h
@@ -56,6 +56,8 @@ extern int diff_tree(struct tree_desc *t
extern int diff_tree_sha1(const unsigned char *old, const unsigned char *new,
const char *base, struct diff_options *opt);
+extern int diff_tree_combined_merge(const unsigned char *sha1, const char *);
+
extern void diff_addremove(struct diff_options *,
int addremove,
unsigned mode,
--
1.1.4.g2bb7f
^ permalink raw reply related
* Re: [PATCH] rev-{list,parse}: allow -<n> as shorthand for --max-count=<n>
From: Junio C Hamano @ 2006-01-24 8:02 UTC (permalink / raw)
To: Eric Wong; +Cc: git list
In-Reply-To: <20060124072946.GA9468@Muzzle>
Eric Wong <normalperson@yhbt.net> writes:
> Some versions of head(1) and tail(1) allow their line limits to be
> parsed this way. I find --max-count to be a commonly used option,
> and also similar in spirit to head/tail, so I decided to make life
> easier on my worn out (and lazy :) fingers with this patch.
As an old timer, I personally am very used to "head -4", but
also have been training my fingers to say "head -n 4" for the
past few years, because the former is not POSIXly correct.
At the same time, I agree that --max-count *was* a mistake. We
should maybe say "-n <n>" perhaps?
^ permalink raw reply
* [PATCH] rev-{list,parse}: allow -<n> as shorthand for --max-count=<n>
From: Eric Wong @ 2006-01-24 7:29 UTC (permalink / raw)
To: git list
Some versions of head(1) and tail(1) allow their line limits to be
parsed this way. I find --max-count to be a commonly used option,
and also similar in spirit to head/tail, so I decided to make life
easier on my worn out (and lazy :) fingers with this patch.
Signed-off-by: Eric Wong <normalperson@yhbt.net>
---
rev-list.c | 5 +++++
rev-parse.c | 4 ++++
2 files changed, 9 insertions(+), 0 deletions(-)
68df4b28986a4642119373e18a63751be0e26366
diff --git a/rev-list.c b/rev-list.c
index d060966..2bbd699 100644
--- a/rev-list.c
+++ b/rev-list.c
@@ -732,6 +732,11 @@ int main(int argc, const char **argv)
struct commit *commit;
unsigned char sha1[20];
+ /* accept, -<digit>, like some versions of head/tail */
+ if (*arg == '-' && isdigit(arg[1])) {
+ max_count = atoi(arg + 1);
+ continue;
+ }
if (!strncmp(arg, "--max-count=", 12)) {
max_count = atoi(arg + 12);
continue;
diff --git a/rev-parse.c b/rev-parse.c
index 0c951af..4dfc1a9 100644
--- a/rev-parse.c
+++ b/rev-parse.c
@@ -52,6 +52,10 @@ static int is_rev_argument(const char *a
};
const char **p = rev_args;
+ /* accept, -<digit>, like some versions of head/tail */
+ if (*arg == '-' && isdigit(arg[1]))
+ return 1;
+
for (;;) {
const char *str = *p++;
int len;
--
1.1.4.g68df
^ permalink raw reply related
* Re: StGIT: "stg new" vs "stg new --force"
From: Pavel Roskin @ 2006-01-24 5:30 UTC (permalink / raw)
To: J. Bruce Fields; +Cc: Yann Dirson, Catalin Marinas, git, Charles Lever
In-Reply-To: <20060120182255.GD32177@fieldses.org>
On Fri, 2006-01-20 at 13:22 -0500, J. Bruce Fields wrote:
> I tend to use stg refresh -es as a quick (well, not quite as quick as
> I'd like) way to look at the current patch. Often I leave it up while
> I'm working (editing the patched files). So if exiting from stg refresh
> -es suddenly started overwriting my working files, I'd be very
> unhappy....
If I understand correctly, "stg refresh" only modifies the repository,
not the files in the local directory. This shouldn't change.
It's only having two instances of "stg refresh -es" that would be a
problem if conflicting edits are made in both editors. The answer to
that is "don't do it".
--
Regards,
Pavel Roskin
^ permalink raw reply
* Re: [RFC] Reverting "git push logic change"?
From: Greg KH @ 2006-01-24 5:05 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <7vfynivx9s.fsf_-_@assigned-by-dhcp.cox.net>
On Fri, Jan 20, 2006 at 11:46:23PM -0800, Junio C Hamano wrote:
> The change introduced by 9e9b267 commit broke "correct" usage of
> git push to push matching refs, to work around a problem
> observed in a usage pattern on a shared repository.
<snip>
Thanks, I think this makes a bit more sense.
If nothing else, the documentation should be updated to reflect the
change in 9e9b267 :)
thanks,
greg k-h
^ permalink raw reply
* Re: Notes on Subproject Support
From: Daniel Barkalow @ 2006-01-24 4:22 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <7vek2yxukm.fsf@assigned-by-dhcp.cox.net>
On Mon, 23 Jan 2006, Junio C Hamano wrote:
> Daniel Barkalow <barkalow@iabervon.org> writes:
>
> > I think it would be a lot more fragile if switching branches requires
> > multiple programs interacting with the index file. If things get
> > interrupted after the tree is read but before the bindings are changed,
> > the user will probably generate an inconsistant commit or have to deal
> > with figuring out what's going on. It is a nice property of the current
> > system that the index file never exists under the usual filename without
> > being consistant.
>
> That is certainly an issue, which we have had already for quite
> some time, I am afraid. We can get interrupted during "switch
> branches" flow after read-tree -u -m but before updating HEAD.
> We can also get interrupted during "commit" flow after writing
> the commit object out before updating the ref pointed at by
> HEAD. No?
The switch branches one is accurate, but I think that, if we get
interrupted before updating the ref, the index will still be the same, and
we'll just have a dangling object (which, if we commit the same thing
again, will be the same object we generate).
I suppose the existing branch switching isn't much less bad than the new
one would be, though. I sort of worry that rewriting the index file is
more likely to be interrupted than updating a ref, but that's probably not
really a significant difference.
> If we are truly serious about solving the issue of getting
> interrupted in the middle, I suspect we have to take the "index
> is a staging area for the next commit" approach I digressed into
> last night. It would involve introducing a git-atomic-checkout
> command to replace the current "git-rev-parse, git-read-tree,
> then git-symbolic-ref" sequence in the checkout flow.
Well, if the value of HEAD were in the index file, that would be
sufficient to prevent anything actually bad from happening in the checkout
path; if it gets interrupted, the index file's "current commit" field
would then not match the ref and it would be clear that the system was in
an intermediate state. (It would appear like if you'd fetched into the
current branch without it doing the fast-forward.)
> In the commit flow, we would need git-commit-index command to replace
> the current "git-write-tree, git-commit-tree, then
> git-update-ref" sequence.
I don't think there's an issue here, anyway.
> I am not particularly opposed to that, but I suspect it might be
> a moderate amount of work for very little gain. Continuing with
> the digression, the updated index file may contain:
>
> 1. list of <blob path, object name>
> 2. list of parent commit object names for the next commit
> 3. the name of the local branch to create the next commit on
> 4. for each bound path:
> list of parent commit object names for that path.
>
> 1. is what we have in the current (version 2) index file.
>
> 2. contains:
> - 0 commit in an index file before the initial commit
> - 1 commit in an index file after a fresh checkout and records
> the commit object name we checked out (replaces HEAD+heads/$branch)
> - 2 commits in an index file after 3-way read-tree, or more
> during an Octopus merge (replaces HEAD+MERGE_HEAD)
>
> 3. may not be needed, but if we did so, it would replace HEAD.
I don't think it would be needed; it could certainly be passed in.
Actually not having HEAD would complicate a lot of programs that use HEAD
but don't currently read the index (and don't actually care about whether
you have the branch that you consider current actually checked out).
> 4. is similar to 2 but for bound subprojects. Usually we have
> one commit per bound path to record the "bind" line commit we
> read from the commit object after a fresh checkout. During a
> subproject merge, we would:
> - start out with 1 commit read from the "bind" line;
> - merging in another subproject commit would add that commit;
> - when making a new subproject commit, the recorded commits
> are used as its parents.
Right.
-Daniel
*This .sig left intentionally blank*
^ permalink raw reply
* Caching directories
From: Pavel Roskin @ 2006-01-24 4:20 UTC (permalink / raw)
To: git
Hello!
I'm thinking of moving cg-clean functionality to git. After having
switched to StGIT, it's the last cogito command I'm still using. I
think git can go it much better, since it's a recursive command
traversing the whole repository.
To be safe and useful, the new command should distinguish between
tracked and untracked directories. Untracked files in tracked
directories are usually the first target for cleaning, as they are
mostly automatic backups and temporary files. Untracked directories are
more likely candidates to be preserved, as they can hold external
sources, build output, extensive test data etc.
cg-clean considers a directory untracked if it has no cached files in
it. This carries a significant speed penalty, even if not coded in
bash.
Maybe it's time to start caching directories in git? I mean,
directories corresponding to tree objects could have their stats
recorded in the cache. This would allow to distinguish between tracked
and untracked directories without scanning them recursively.
--
Regards,
Pavel Roskin
^ permalink raw reply
* Re: What is in git.git
From: Josef Weidendorfer @ 2006-01-24 1:52 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <7v64odj821.fsf@assigned-by-dhcp.cox.net>
On Sunday 22 January 2006 03:44, you wrote:
> In commit 1:
> bind xxxxx... X/
Ah, OK, sorry.
This was a misunderstanding from my side; please forget the
other suggestion about change reference files, too ;-)
Josef
^ permalink raw reply
* Re: Notes on Subproject Support
From: Junio C Hamano @ 2006-01-24 1:50 UTC (permalink / raw)
To: Daniel Barkalow; +Cc: git
In-Reply-To: <Pine.LNX.4.64.0601231200380.25300@iabervon.org>
Daniel Barkalow <barkalow@iabervon.org> writes:
> ... Do you see an advantage to having the index only record the
> information used for making a tree, and keeping the information for making
> a commit in other files?
If somebody else already did the work and presented me two git
implementations, one with the index file capable of generating a
tree and uses separate files to keep track of other information
for commits, and the other with the index file with everything
needed for a commit, I'd certainly take the latter. In that
sense, I do not see such an advantage at all. The practical
advantage of keeping them separate is to keep things simple,
minimizing the changes. I see the subproject support as a
secondary issue, and so far I haven't found a reason convincing
enough to tell me that it is better to put HEAD+heads/$branch
information in the index itself when used in a subproject-less
setup. It perhaps would make us more robust when interrupted in
the middle of switching branches or making a commit, but that is
about it (I do not particularly see that a serious problem).
>> *1* One good property of "gitlink" approach is that we *could*
>> extend this blob-like object to store arbitrary human readable
>> information to represent a point-in-time from an arbitrary
>> foreign SCM system. IOW, we do not necessarily have to require
>> `commit` line that name a git commit to be there. It could say
>> "Please slurp http://www.kernel.org/pub/software/.../git.tar.gz
>> and extract it in git/ directory".
>> ...
> I don't think this would really be useful. The reason to have the included
> revision stored in a way that's explicitly marked for git to find is that
> git can do useful things with the information ...
> but more importantly, making sure that changes to what revision
> you're working with propagate to changes in what revision you specify
> should be there)...
My example was taking things to the extreme to be illustrative.
To be more practical, it could have pointed at "git-1.0.tar.gz"
or an "svn://" URL with explicit revision name, which ought to
be enough to recreate the exact state.
^ permalink raw reply
* Re: Notes on Subproject Support
From: Junio C Hamano @ 2006-01-24 1:50 UTC (permalink / raw)
To: Daniel Barkalow; +Cc: git
In-Reply-To: <Pine.LNX.4.64.0601231116550.25300@iabervon.org>
Daniel Barkalow <barkalow@iabervon.org> writes:
> I think it would be a lot more fragile if switching branches requires
> multiple programs interacting with the index file. If things get
> interrupted after the tree is read but before the bindings are changed,
> the user will probably generate an inconsistant commit or have to deal
> with figuring out what's going on. It is a nice property of the current
> system that the index file never exists under the usual filename without
> being consistant.
That is certainly an issue, which we have had already for quite
some time, I am afraid. We can get interrupted during "switch
branches" flow after read-tree -u -m but before updating HEAD.
We can also get interrupted during "commit" flow after writing
the commit object out before updating the ref pointed at by
HEAD. No?
If we are truly serious about solving the issue of getting
interrupted in the middle, I suspect we have to take the "index
is a staging area for the next commit" approach I digressed into
last night. It would involve introducing a git-atomic-checkout
command to replace the current "git-rev-parse, git-read-tree,
then git-symbolic-ref" sequence in the checkout flow. In the
commit flow, we would need git-commit-index command to replace
the current "git-write-tree, git-commit-tree, then
git-update-ref" sequence.
I am not particularly opposed to that, but I suspect it might be
a moderate amount of work for very little gain. Continuing with
the digression, the updated index file may contain:
1. list of <blob path, object name>
2. list of parent commit object names for the next commit
3. the name of the local branch to create the next commit on
4. for each bound path:
list of parent commit object names for that path.
1. is what we have in the current (version 2) index file.
2. contains:
- 0 commit in an index file before the initial commit
- 1 commit in an index file after a fresh checkout and records
the commit object name we checked out (replaces HEAD+heads/$branch)
- 2 commits in an index file after 3-way read-tree, or more
during an Octopus merge (replaces HEAD+MERGE_HEAD)
3. may not be needed, but if we did so, it would replace HEAD.
4. is similar to 2 but for bound subprojects. Usually we have
one commit per bound path to record the "bind" line commit we
read from the commit object after a fresh checkout. During a
subproject merge, we would:
- start out with 1 commit read from the "bind" line;
- merging in another subproject commit would add that commit;
- when making a new subproject commit, the recorded commits
are used as its parents.
^ permalink raw reply
* Re: Notes on Subproject Support
From: Junio C Hamano @ 2006-01-23 19:30 UTC (permalink / raw)
To: Martin Atukunda; +Cc: git
In-Reply-To: <20060123125013.GA4472@igloo.ds.co.ug>
Martin Atukunda <matlads@dsmagic.com> writes:
> This proposal doesn't seem to cator for the event when a directory is
> renamed or moved to a different location, or am I missing something?
First of all, please do not top post.
Second of all, please do not quote the whole thing.
Third of all, if you quote, please read the parts you quote.
>> Merging
>> -------
>> ...
>> Such renaming can be handled by first moving the bind points in
>> our branch, and redoing the merge (this is a rare operation
>> anyway). It might go like this:
>> ...
This step describes how bind-point might be relocated prior to a
merge.
^ permalink raw reply
* Re: Notes on Subproject Support
From: Daniel Barkalow @ 2006-01-23 17:57 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git, Petr Baudis
In-Reply-To: <7v64ob1omh.fsf@assigned-by-dhcp.cox.net>
On Sun, 22 Jan 2006, Junio C Hamano wrote:
> Junio C Hamano <junkio@cox.net> writes:
>
> BTW, let's digress a bit.
>
> I think recording "commit" in the tree objects is in line with
> the logical organization described in README: "blob" and "tree"
> represent a state, and have *nothing* to do with how we came
> about to that state. The historyh is described in "commit"
> objects. The bound commit approach keeps that property.
>
> The "gitlink" approach, as I understand how Linus outlined in
> his original suggestion, is a bit different. The link objects
> appear in tree objects, and when you "git cat-file link" one of
> them, you would see something like this:
>
> commit 5b2bcc7b2d546c636f79490655b3347acc91d17f
> name kernel
>
> So in that sense, "gitlink" approach departs from the original
> premise of "commit" being the only thing that ties things
> together. Tree objects with "gitlink" know where they are in
> the history [*1*].
>
> By this, I do not mean to say that "gitlink" approach is
> inferior because it breaks that original premise. I am just
> pointing it out as a difference between two approaches.
I think it's hard to say whether the history of a subproject is part of
the state of the superproject or part of its history. They're certainly
not the same history, because the superproject history may record that
the superproject switched from one fork of a subproject to a different
fork, or reverted the subproject to an earlier version, or other such
things. (It's the whole data/metadata issue: when you take a step back,
one level's data and metadata are all data, and there's more stuff that's
metadata.)
I'd say that with either commits in trees or gitlink objects, it's still
only the commits that tie things together; but some of the things that
they tie together are opaquely things tied to other things. Tree objects
with gitlink/commits don't know where *they* are in the history; they
just happen to store things which know about a different history.
> Now, the current way index file is used is as a staging area to
> create a new commit on top of the tip of the current branch.
> However, it is interesting to note that logically, by itself
> *alone*, it cannot be used that way. The information the index
> file records is something that can be used to write out a tree
> object, and not a commit, because it does not know where the
> current state sits in the history. We have two separate files,
> $GIT_DIR/HEAD that records which branch we are on, and the
> branch head ref the HEAD points at, which records where the
> current index came from, for that purpose. The latter tells us
> what commit we should use as the parent commit if we create such
> a commit, and the former tells us which branch head to update
> once we create one. So in that sense, the index file is just a
> staging area to create a new tree, not a new commit.
Of course, we don't strictly need $GIT_DIR/HEAD to create a commit;
that's only needed for what we generally do with the commit once we have
it. We do need the branch head ref (or, more abstractly, the commit that
we read to generate the index we modified) in order to create the commit.
> We could have done things differently. I am not advocating to
> do the following change, but offering a possibility as a thought
> experiment. It just felt interesting enough to point them out.
>
> The index file could have recorded what commit the current state
> recorded in the index came from. By recording the commit the
> index was read from in the index itself, independently from the
> $GIT_DIR/refs/heads/$branch file, we could have been able to
> allow fetching into the current branch. When the $branch file
> for the current branch was updated by a fast-forward fetch, we
> would notice that the commit recorded there no longer match what
> is recorded in the index.
I actually think this would have been a good idea. I think we've had
approximately every possible bug that could come from inconsistancy
between the files that give the parents and the index file. (I think Linus
didn't do it that way initially just because he was thinking of it as a
cache, and there's little point in caching something small, and by the
time we started looking at it as primary information on its own, we'd
stopped thinking about what should go in it.)
> Another interesting consequence is if the development is a
> single repository and linear, we did not even need any file in
> $GIT_DIR/refs/ ("branchless git"). The commit recorded as the
> topmost in the index file itself would have served as the tip of
> the development, and we would have been able to tangle the
> history starting from the commit in the index file.
Well, you wouldn't be able to check out an old version and then return to
the present without dredging the objects database for the dangling commit.
My memory has gotten fuzzy, but I think HEAD may have originally been just
a file, and we effectively had this (except that HEAD and the index were
not the same file as far as the filesystem was concerned).
> While we are doing a thought experiment, let's say we allow to
> record more than one commits the current index is based upon.
> 'git merge' would record all the parent commits there, so that
> writing out the merge result out of the index file as a tree and
> then recording these commits as parents would have been the way
> to create a merge commit. We would not need the auxiliary file
> $GIT_DIR/MERGE_HEAD if we did so.
>
> In other words, if the index file recorded the commits its
> contents were based upon, instead of being a staging area for a
> new tree, it would have been a staging area for a new commit.
>
> Now, the latest proposal, borrowing your idea, records the
> subproject commits bound to subdirectories in the index itself.
> This is halfway to make the index file a staging area for the
> next commit. If we were to do that, we also *could* record the
> commits the current index is based upon, so that it can truly be
> used as a staging area to create a new commit, not just a tree.
>
> On the other hand, this could be a reason *not* to do the
> `update-index --bind` to record the subproject information in
> the index file. An auxiliary file such as $GIT_DIR/bind might
> be sufficient, just like $GIT_DIR/MERGE_HEAD has been good
> enough for us so far. One difference between MERGE_HEAD and
> bind is that the former is very transient -- only exists during
> a merge while the latter is persistent while the top commit is
> checked out and being worked on.
We've been able to make MERGE_HEAD work, but I remember there being
problems even there when people tried to abandon merges by changing
branches. Do you see an advantage to having the index only record the
information used for making a tree, and keeping the information for making
a commit in other files?
> [Footnote]
>
> *1* One good property of "gitlink" approach is that we *could*
> extend this blob-like object to store arbitrary human readable
> information to represent a point-in-time from an arbitrary
> foreign SCM system. IOW, we do not necessarily have to require
> `commit` line that name a git commit to be there. It could say
> "Please slurp http://www.kernel.org/pub/software/.../git.tar.gz
> and extract it in git/ directory".
>
> Of course, for such a toplevel project commit, the tool may not
> be able to do a checkout automatically and require the user to
> cat-file the link, download a tarball and extract the subtree
> there manually.
>
> The bound commit approach requires you to have git commit object
> names on the `bind` lines, and it is fundamentally much harder
> to extend it to allow interfacing with foreign (non-)SCM
> systems.
I don't think this would really be useful. The reason to have the included
revision stored in a way that's explicitly marked for git to find is that
git can do useful things with the information (such as checking it out for
you, but more importantly, making sure that changes to what revision
you're working with propagate to changes in what revision you specify
should be there). If the bound project is foreign, this clearly isn't
going to happen, so there's not much point. For your example above, you
could just have a regular file, "git/README", with the content "Please
download http://.../git.tar.gz, and extract it here", and it would be at
least as good.
-Daniel
*This .sig left intentionally blank*
^ permalink raw reply
* Re: Notes on Subproject Support
From: Daniel Barkalow @ 2006-01-23 16:48 UTC (permalink / raw)
To: Alexander Litvinov; +Cc: Junio C Hamano, git, Petr Baudis
In-Reply-To: <200601231206.53466.lan@ac-sw.com>
On Mon, 23 Jan 2006, Alexander Litvinov wrote:
> In our development we use a little bit other case (it is simplified):
> 1. We have self written C++ library for linked list implementation. Lets call
> it liblist.
> 2. We have project A that use liblist as separate directory and project B that
> use this library too.
>
> Currently we have 3 cvs projects with cvs-modules for linking liblist to A and
> B. During development of A and B we often modify liblist to fix bugs and
> these changes are immidatly visible to all projects who use liblist.
>
> After full implementation of bind functionality I see one restriction: I have
> to use one repo for storing all three projects: A, B and liblist to make
> changes of liblist visible to all projects. The solution is to make separate
> repos and on each change of liblist in prokect A push these changes to
> liblist repo and pull them into project B again - bit hacky solution.
We haven't yet discussed how pushing a repository with subprojects would
work. We could probably have an extra line in the remotes/ file to make
the desired thing happen automatically, so that you can just do "git push"
and have the subproject go to its repository and the main project go to
its repository.
-Daniel
*This .sig left intentionally blank*
^ permalink raw reply
* Re: Notes on Subproject Support
From: Daniel Barkalow @ 2006-01-23 16:31 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <7v7j8r7e7s.fsf@assigned-by-dhcp.cox.net>
On Sun, 22 Jan 2006, Junio C Hamano wrote:
> Daniel Barkalow <barkalow@iabervon.org> writes:
>
> >> Switching branches
> >> ------------------
> >>
> >> Along with the traditional two-way merge by `read-tree -m -u`,
> >> we would need to look at:
> >>
> >> . `bind` lines in the current `HEAD` commit.
> >>
> >> . `bind` lines in the commit we are switching to.
> >>
> >> . subproject binding information in the index file.
> >>
> >> to make sure we do sensible things.
> >
> > This is one place I think storing the bindings in the commit is awkward.
> > read-tree deals in trees (hence the name), but will need information from
> > the commit.
>
> That's why it is 'along with'. Dealing with binding information
> can be done between commits and index without bothering tree
> objects. read-tree would not have to deal with it, and I think
> keeping it that way is probably a good idea.
I think it would be a lot more fragile if switching branches requires
multiple programs interacting with the index file. If things get
interrupted after the tree is read but before the bindings are changed,
the user will probably generate an inconsistant commit or have to deal
with figuring out what's going on. It is a nice property of the current
system that the index file never exists under the usual filename without
being consistant.
> In other words, I think the design so far does not require us to
> touch tree objects at all, and I'd be happy if we do not have to.
>
> One reason I started the bound commit approach was exactly
> because I only needed to muck with commit objects and did not
> have to touch trees and blobs; after trying to implement the
> core level for "gitlink", which I ended up touching quite a lot
> and have abandoned for now.
I think that the same thing that worked with the index file would work for
minimizing the impact of the changes as far as the code sees. If the
struct tree parser reported the tree at a path when it found a commit at a
path, it would work just as if the original tree had used trees (like in
your proposal).
-Daniel
*This .sig left intentionally blank*
^ permalink raw reply
* Re: [PATCH] DT_UNKNOWN: do not fully trust existence of DT_UNKNOWN
From: Alex Riesen @ 2006-01-23 13:07 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <7vpsmmt32x.fsf@assigned-by-dhcp.cox.net>
On 1/21/06, Junio C Hamano <junkio@cox.net> wrote:
> Alex Riesen <raa.lkml@gmail.com> writes:
>
> >> +#undef DT_UNKNOWN
> >> +#undef DT_DIR
> >> +#undef DT_REG
> >> +#undef DT_LNK
> >
> > yes, of course
>
> That is technically correct but I suspect it would not matter in
> practice. The only thing you are avoiding is four compiler
> warnings when compiling for Cygwin of this week (I heard this is
> already fixed in Cygwin CVS). On older Cygwin you simply would
> not use NO_D_TYPE_IN_DIRENT, since d_type worked before. No?
Yes :)
^ permalink raw reply
* Re: Notes on Subproject Support
From: Martin Atukunda @ 2006-01-23 12:50 UTC (permalink / raw)
To: git
In-Reply-To: <7v3bjfafql.fsf@assigned-by-dhcp.cox.net>
This proposal doesn't seem to cator for the event when a directory is
renamed or moved to a different location, or am I missing something?
- Martin -
On Sun, Jan 22, 2006 at 05:35:14PM -0800, Junio C Hamano wrote:
> This is still a draft/WIP, but "release early" is a good
> discipline, so...
>
> -- >8 --
>
> Notes on Subproject Support
> ===========================
> Junio C Hamano <junkio@cox.net>
> v1.0 January 22, 2006
>
> Scenario
> --------
>
> The examples in the following discussion show how this proposal
> plans to help this:
>
> . A project to build an embedded Linux appliance "gadget" is
> maintained with git.
>
> . The project uses linux-2.6 kernel as its subcomponent. It
> starts from a particular version of the mainline kernel, but
> adds its own code and build infrastructure to fit the
> appliance's needs.
>
> . The working tree of the project is laid out this way:
> +
> ------------
> Makefile - Builds the whole thing.
> linux-2.6/ - The kernel, perhaps modified for the project.
> appliance/ - Applications that run on the appliance, and
> other bits.
> ------------
>
> . The project is willing to maintain its own changes out of tree
> of the Linux kernel project, but would want to be able to feed
> the changes upstream, and incorporate upstream changes to its
> own tree, taking advantage of the fact that both itself and
> the Linux kernel project are version controlled with git.
>
> The idea here is to:
>
> . Keep `linux-2.6/` part as an independent project. The work by
> the project on the kernel part can be naturally exchanged with
> the other kernel developers this way. Specifically, a tree
> object contained in commit objects belonging to this project
> does *not* have linux-2.6/ directory at the top.
>
> . Keep the `appliance/` part as another independent project.
> Applications are supposed to be more or less independent from
> the kernel version, but some other bits might be tied to a
> specific kernel version. Again, a tree object contained in
> commit objects belonging to this project does *not* have
> appliance/ directory at the top.
>
> . Have another project that combines the whole thing together,
> so that the project can keep track of which versions of the
> parts are built together.
>
> We will call the project that binds things together the
> 'toplevel project'. Other projects that hold `linux-2.6/` part
> and `appliance/` part are called 'subprojects'.
>
> Notice that `Makefile` at the top is part of the toplevel
> project in this example, but it is not necessary. We could
> instead have the appliance subproject include this file. In
> such a setup, the appliance subproject would have had `Makefile`
> and `appliance/` directory at the toplevel.
>
>
> Setting up
> ----------
>
> Let's say we have been working on the appliance software,
> independently version controlled with git. Also the kernel part
> has been version controlled separately, like this:
> ------------
> $ ls -dF current/*/.git current/*
> current/Makefile current/appliance/.git/ current/linux-2.6/.git/
> current/appliance/ current/linux-2.6/
> ------------
>
> Now we would want to get a combined project. First we would
> clone from these repositories (which is not strictly needed --
> we could use `$GIT_ALTERNATE_OBJECT_DIRECTORIES` instead):
>
> ------------
> $ mkdir combined && cd combined
> $ cp ../current/Makefile .
> $ git init-db
> $ mkdir -p .git/refs/subs/{kernel,gadget}/{heads,tags}
> $ git clone-pack ../current/linux-2.6/ master | read kernel_commit junk
> $ git clone-pack ../current/appliance/ master | read gadget_commit junk
> ------------
>
> We will introduce a new command to set up a combined project:
>
> ------------
> $ git bind-projects \
> $kernel_commit linux-2.6/ \
> $gadget_commit appliance/
> ------------
>
> This would do an equivalent of:
>
> ------------
> $ git read-tree --prefix=linux-2.6/ $kernel_commit
> $ git read-tree --prefix=appliance/ $gadget_commit
> ------------
> [NOTE]
> ============
> Earlier outlines sent to the git mailing list talked
> about `$GIT_DIR/bind` to record what subproject are bound to
> which subtree in the curent working tree and index. This
> proposal instead records that information in the index file
> when `--prefix=linux-2.6/` is given to `read-tree`.
>
> Also note that in this round of proposal, there is no separate
> branches that keep track of heads of subprojects.
> ============
>
> Let's not forget to add the `Makefile`, and check the whole
> thing out from the index file.
> ------------
> $ git add Makefile
> $ git checkout-index -f -u -q -a
> ------------
>
> Now our directory should be identical with the `current`
> directory. After making sure of that, we should be able to
> commit the whole thing:
>
> ------------
> $ diff -x .git -r ../current ../combined
> $ git commit -m 'Initial toplevel project commit'
> ------------
>
> Which should create a new commit object that records what is in
> the index file as its tree, with `bind` lines to record which
> subproject commit objects are bound at what subdirectory, and
> updates the `$GIT_DIR/refs/heads/master`. Such a commit object
> might look like this:
> ------------
> tree 04803b09c300c8325258ccf2744115acc4c57067
> bind 5b2bcc7b2d546c636f79490655b3347acc91d17f linux-2.6/
> bind 0bdd79af62e8621359af08f0afca0ce977348ac7 appliance/
> author Junio C Hamano <junio@kernel.org> 1137965565 -0800
> committer Junio C Hamano <junio@kernel.org> 1137965565 -0800
>
> Initial toplevel project commit
> ------------
>
>
> Making further commits
> ----------------------
>
> The easiest case is when you updated the Makefile without
> changing anything in the subprojects. In such a case, we just
> need to create a new commmit object that records the new tree
> with the current `HEAD` as its parent, and with the same set of
> `bind` lines.
>
> When we have changes to the subproject part, we would make a
> separate commit to the subproject part and then record the whole
> thing by making a commit to the toplevel project. The user
> interaction might go this way:
> ------------
> $ git commit
> error: you have changes to the subproject bound at linux-2.6/.
> $ git commit --subproject linux-2.6/
> $ git commit
> ------------
>
> With the new `--subproject` option, the directory structure
> rooted at `linux-2.6/` part is written out as a tree, and a new
> commit object that records that tree object with the commit
> bound to that portion of the tree (`5b2bcc7b` in the above
> example) as its parent is created. Then the final `git commit`
> would record the whole tree with updated `bind` line for the
> `linux-2.6/` part.
>
>
> Checking out
> ------------
>
> After cloning such a toplevel project, `git clone` without `-n`
> option would check out the working tree. This is done by
> reading the tree object recorded in the commit object (which
> records the whole thing), and adding the information from the
> "bind" line to the index file.
>
> ------------
> $ cd ..
> $ git clone -n combined cloned ;# clone the one we created earlier
> $ cd cloned
> $ git checkout
> ------------
>
> This round of proposal does not maintain separate branch heads
> for subprojects. The bound commits and their subdirectories
> are recorded in the index file from the commit object, so there
> is no need to do anything other than updating the index and the
> working tree.
>
>
> Switching branches
> ------------------
>
> Along with the traditional two-way merge by `read-tree -m -u`,
> we would need to look at:
>
> . `bind` lines in the current `HEAD` commit.
>
> . `bind` lines in the commit we are switching to.
>
> . subproject binding information in the index file.
>
> to make sure we do sensible things.
>
> Just like until very recently we did not allow switching
> branches when two-way merge would lose local changes, we can
> start by refusing to switch branches when the subprojects bound
> in the index do not match what is recorded in the `HEAD` commit.
>
> Because in this round of the proposal we do not use the
> `$GIT_DIR/bind` file nor separate branches to keep track of
> heads of the subprojects, there is nothing else other than the
> working tree and the index file that needs to be updated when
> switching branches.
>
>
> Merging
> -------
>
> Merging two branches of the toplevel projects can use the
> traditional merging mechanism mostly unchanged. The merge base
> computation can be done using the `parent` ancestry information
> taken from the two toplevel project branch heads being merged,
> and merging of the whole tree can be done with a three-way merge
> of the whole tree using the merge base and two head commits.
> For reasons described later, we would not merge the subproject
> parts of the trees during this step, though.
>
> When the two branch heads use different versions of subproject,
> things get a bit tricky. First, let's forget for a moment about
> the case where they bind the same project at different location.
> We would refuse if they do not have the same number of `bind`
> lines that bind something at the same subdirectories.
>
> ------------
> $ git merge 'Merge in a side branch' HEAD side
> error: the merged heads have subprojects bound at different places.
> ours:
> linux-2.6/
> appliance/
> theirs:
> kernel/
> gadget/
> manual/
> ------------
>
> Such renaming can be handled by first moving the bind points in
> our branch, and redoing the merge (this is a rare operation
> anyway). It might go like this:
>
> ------------
> $ git bind-projects \
> $kernel_commit kernel/ \
> $gadget_commit gadget/
> $ git commit -m 'Prepare for merge with side branch'
> $ git merge 'Merge in a side branch' HEAD side
> error: the merged heads have subprojects bound at different places.
> ours:
> kernel/
> gadget/
> theirs:
> kernel/
> gadget/
> manual/
> ------------
>
> Their branch added another subproject, so this did not work (or
> it could be the other way around -- we might have been the one
> with `manual/` subproject while they didn't). This suggests
> that we may want an option to `git merge` to allow taking a
> union of subprojects. Again, this is a rare operation, and
> always taking a union would have created a toplevel project that
> had both `kernel/` and `linux-2.6/` bound to the same Linux
> kernel project from possibly different vintage, so it would be
> prudent to require the set of bound subprojects to exactly match
> and give the user an option to take a union.
>
> ------------
> $ git merge --union-subprojects 'Merge in a side branch HEAD side
> error: the subproject at `kernel/` needs to be merged first.
> ------------
>
> Here, the version of the Linux kernel project in the `side`
> branch was different from what our branch had on our `bind`
> line. On what kind of difference should we give this error?
> Initially, I think we could require one is the fast forward of
> the other (ours might be ahead of theirs, or the other way
> around), and take the descendant.
>
> Or we could do an independent merge of subprojects heads, using
> the `parent` ancestry of the bound subproject heads to find
> their merge-base and doing a three-way merge. This would leave
> the merge result in the subproject part of the working tree and
> the index.
>
> [NOTE]
> This is the reason we did not do the whole-tree three way merge
> earlier. The subproject commit bound to the merge base commit
> used for the toplevel project may not be the merge base between
> the subproject commits bound to the two toplevel project
> commits.
>
> So let's deal with the case to merge only a subproject part into
> our tree first.
>
>
> Merging subprojects
> -------------------
>
> An operation of more practical importance is to be able to merge
> in changes done outside to the projects bound to our toplevel
> project.
>
> ------------
> $ git pull --subproject=kernel/ git://git.kernel.org/.../linux-2.6/
> ------------
>
> might do:
>
> . fetch the current `HEAD` commit from Linus.
> . find the subproject commit bound at kernel/ subtree.
> . perform the usual three-way merge of these two commits, in
> `kernel/` part of the working tree.
>
> After that, `git commit \--subproject` option would be needed to
> make a commit.
>
> [NOTE]
> This suggests that we would need to have something similar to
> `MERGE_HEAD` for merging the subproject part.
>
>
> -
> To unsubscribe from this list: send the line "unsubscribe git" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
--
Due to a shortage of devoted followers, the production of great leaders has been discontinued.
^ permalink raw reply
* Re: Notes on Subproject Support
From: Junio C Hamano @ 2006-01-23 8:38 UTC (permalink / raw)
To: git
In-Reply-To: <7v64ob1omh.fsf@assigned-by-dhcp.cox.net>
Junio C Hamano <junkio@cox.net> writes:
> BTW, let's digress a bit.
Ugh. Serious typo.
> I think recording "commit" in the tree objects is in line with
> the logical organization described in README: "blob" and "tree"
> represent a state, and have *nothing* to do with how we came
> about to that state. The historyh is described in "commit"
> objects. The bound commit approach keeps that property.
Obviously, I think "*NOT* recording commit in tree objects" is
in line with "blobs and trees are about states, commits give
them their points in history".
^ permalink raw reply
* Re: [RFC] 1st pass at git-annotate (not-quite functional, however)
From: Junio C Hamano @ 2006-01-23 8:24 UTC (permalink / raw)
To: Ryan Anderson; +Cc: git
In-Reply-To: <20060123080532.GD19212@mythryan2.michonline.com>
Ryan Anderson <ryan@michonline.com> writes:
> I haven't figured out exactly how I want to handle annotating across
> merges,...
You might want to take a look at the "passing blames around"
algorithm I did for "git blame" loooooooooooooooong time ago.
http://article.gmane.org/gmane.comp.version-control.git/5483
^ permalink raw reply
* [RFC] 1st pass at git-annotate (not-quite functional, however)
From: Ryan Anderson @ 2006-01-23 8:05 UTC (permalink / raw)
To: git; +Cc: Linus Torvalds
Here's my stab at getting git-annotate working. This requires the
--remove-empty addition to git-rev-list Linus sent out a few days ago.
This code is not complete, but on my test case (git-format-mbox.sh) it
does track through renames, and does figure out authorship of about half
the lines (rough guess).
I have learned that parsing an arbritrary length series of diffs is
hard, mostly because trying to infer line numbers from them is tricky
(especially when you contemplate tracking state down multiple merges
at once.)
I currently have a very naive approach of tracking offsets in here,
built as the diff is parsed. I know it's wrong, but it's not too wrong,
if you ignore merges.
I haven't figured out exactly how I want to handle annotating across
merges, but I think the general idea I have here will work, it just
needs to be ripped out of global variables and stuck into some
structures that get passed around/split/merged up/etc.
I'm posting this tonight because I won't have time to look at this again
for a few days (probably the weekend), and someone else might see a way
to make this work a bit easier.
Without further ado, git-annotate:
Makefile | 2
git-annotate.perl | 309 +++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 310 insertions(+), 1 deletions(-)
create mode 100755 git-annotate.perl
df7b8b7bf682411485aefa31c6dc24a4b8adf316
diff --git a/Makefile b/Makefile
index 6517204..505b353 100644
--- a/Makefile
+++ b/Makefile
@@ -118,7 +118,7 @@ SCRIPT_PERL = \
git-archimport.perl git-cvsimport.perl git-relink.perl \
git-shortlog.perl git-fmt-merge-msg.perl \
git-svnimport.perl git-mv.perl git-cvsexportcommit.perl \
- git-graft-ripple.perl
+ git-graft-ripple.perl git-annotate.perl
SCRIPT_PYTHON = \
git-merge-recursive.py
diff --git a/git-annotate.perl b/git-annotate.perl
new file mode 100755
index 0000000..044828f
--- /dev/null
+++ b/git-annotate.perl
@@ -0,0 +1,309 @@
+#!/usr/bin/perl
+# Copyright 2006, Ryan Anderson <ryan@michonline.com>
+#
+# GPL v2 (See COPYING)
+#
+# This file is licensed under the GPL v2, or a later version
+# at the discretion of Linus Torvalds.
+
+use warnings;
+use strict;
+
+use Data::Dumper;
+
+my $filename = shift @ARGV;
+
+
+my @stack = (
+ {
+ 'rev' => "HEAD",
+ 'filename' => $filename,
+ },
+);
+
+our (@lineoffsets, @pendinglineoffsets);
+my @filelines = ();
+open(F,"<",$filename)
+ or die "Failed to open filename: $!";
+
+while(<F>) {
+ chomp;
+ push @filelines, $_;
+}
+close(F);
+
+my $revsprocessed = 0;
+W: while (my $bound = pop @stack) {
+ my @revisions = git_rev_list($bound->{'rev'}, $bound->{'filename'});
+ foreach my $revinst (@revisions) {
+ my ($rev, @parents) = @$revinst;
+ #last W if $revsprocessed++ > 3;
+
+ if (scalar @parents > 0) {
+ git_diff_parse($parents[0], $rev, $bound->{'filename'});
+ next;
+ }
+
+ printf("Boundary = %s\n", $rev);
+ my $newbound = find_parent_renames($rev, $bound->{'filename'});
+ if ( exists $newbound->{'filename'} && $newbound->{'filename'} ne $bound->{'filename'}) {
+ push @stack, $newbound;
+ }
+ }
+}
+
+my $i = 0;
+foreach my $l (@filelines) {
+ my ($output, $rev, $committer, $date);
+ if (ref $l eq 'ARRAY') {
+ ($output, $rev, $committer, $date) = @$l;
+ if (length($rev) > 8) {
+ $rev = substr($rev,0,8);
+ }
+ } else {
+ $output = $l;
+ ($rev, $committer, $date) = ('unknonw', 'unknown', 'unknown');
+ }
+
+ printf("(%8s %10s %10s %d)%s\n", $rev, $committer, $date, $i++, $output);
+}
+
+#print Data::Dumper->Dump([\@lineoffsets],[qw(*lineoffsets)]);
+
+
+sub git_rev_list {
+ my ($rev, $file) = @_;
+ #printf("grl = %s, %s\n", $rev, $file);
+
+ open(P,"-|","git-rev-list","--parents","--remove-empty",$rev,"--",$file)
+ or die "Failed to exec git-rev-list: $!";
+
+ my @revs;
+ while(my $line = <P>) {
+ chomp $line;
+ my ($rev, @parents) = split /\s+/, $line;
+ push @revs, [ $rev, @parents ];
+ }
+ close(P);
+
+ return @revs;
+}
+
+sub find_parent_renames {
+ my ($rev, $file) = @_;
+
+ open(P,"-|","git-diff", "-r","--name-status", "-z","$rev^1..$rev")
+ or die "Failed to exec git-diff: $!";
+
+ local $/ = "\0";
+ my %bound;
+ while (my $change = <P>) {
+ chomp $change;
+ my $filename = <P>;
+ chomp $filename;
+
+ if ($change =~ m/^[AMD]$/ ) {
+ next;
+ } elsif ($change =~ m/^R/ ) {
+ my $oldfilename = $filename;
+ $filename = <P>;
+ chomp $filename;
+ if ( $file eq $filename ) {
+ my $parent = git_find_parent($rev);
+ #printf("Found rename at boundary: %s-%s, %s\n", $rev, $parent, $oldfilename);
+ @bound{'rev','filename'} = ($parent, $oldfilename);
+
+ last;
+ } else {
+ #printf("Found unknown rename of %s => %s\n", $oldfilename, $filename);
+ }
+ } else {
+ #printf("Unknown name-status type of '%s'\n", $change);
+ }
+
+
+
+ }
+ close(P);
+
+ return \%bound;
+}
+
+
+sub git_find_parent {
+ my ($rev) = @_;
+
+ open(REVPARENT,"-|","git-rev-list","--parents","$rev^1..$rev")
+ or die "Failed to open git-rev-list to find a single parent: $!";
+
+ my $parentline = <REVPARENT>;
+ chomp $parentline;
+ my ($revfound,$parent) = split m/\s+/, $parentline;
+
+ close(REVPARENT);
+
+ return $parent;
+}
+
+
+# Get a diff between the current revision and a parent.
+# Record the commit information that results.
+sub git_diff_parse {
+ my ($parent, $rev, $filename) = @_;
+
+ my %revinfo = git_commit_info($rev);
+
+ # To make this slightly simpler, we're going to do things backwards.
+ # git-diff $rev..$parent gets us line numbers that pertain to $rev, not
+ # $parent, and we can work with those easier than the reverse.
+ open(DIFF,"-|","git-diff","$rev..$parent",$filename)
+ or die "Failed to call git-diff for annotation: $!";
+
+ my ($gotheader) = (0);
+ my ($remstart, $remlength, $addstart, $addlength);
+ my ($hunk_actual_start, $hunk_index, $hunk_adds);
+ while(<DIFF>) {
+ print;
+ if (m/^@@ -(\d+),(\d+) \+(\d+),(\d+)/) {
+ if (0 && $gotheader) {
+ print "Filelines had:\n";
+ for (my $i = $remstart ; $i < $remstart + $remlength; $i++) {
+ print $filelines[$i];
+
+ }
+ print "===================================\n";
+ }
+ ($remstart, $remlength, $addstart, $addlength) = ($1, $2, $3, $4);
+ # Adjust for 0-based arrays
+ $remstart--;
+ $addstart--;
+
+ # Record (but don't put into use right away) these offsets.
+ insert_into_lineoffsets( $remstart + $remlength, $remlength - $addlength );
+ $hunk_index = 0;
+ $hunk_adds = 0;
+ $hunk_actual_start = find_actual_start($remstart);
+ $gotheader = 1;
+ next;
+ } elsif (!$gotheader) {
+ next;
+ }
+
+ if (m/^\+/) {
+ next;
+
+ } elsif (m/^-(.*)$/) {
+ my $line = $1;
+ my $floffset = $hunk_actual_start + $hunk_index;
+
+ printf("\tline = %s\n\tfilelines entry = %s*\thas = %d, hi = %d\n",
+ $line, $filelines[$floffset],
+ $hunk_actual_start, $hunk_index) if 0;
+
+ if (ref $filelines[$floffset] eq '' && $filelines[$floffset] eq $line) {
+ my $oline = $filelines[$floffset];
+ $filelines[$floffset] =
+ [ $oline, $rev, $revinfo{'committer'}, $revinfo{'committer_date'} ];
+ }
+ }
+
+ $hunk_index++;
+ }
+ close(DIFF);
+
+ # Store these offsets for use when processing the next diff.
+ sync_line_offsets();
+}
+
+sub insert_into_lineoffsets {
+ my ($start, $offset) = @_;
+
+ push @pendinglineoffsets, [$start, $offset];
+
+}
+
+sub sync_line_offsets {
+ check_line_offsets(0);
+ #print Data::Dumper->Dump([\@pendinglineoffsets],[qw(*pending)]);
+ foreach my $o (@pendinglineoffsets) {
+ _insert_into_lineoffsets($$o[0],$$o[1]);
+ }
+ @pendinglineoffsets = ();
+
+ #print Data::Dumper->Dump([\@lineoffsets], [qw(*lineoffsets)]);
+ check_line_offsets(1);
+
+ if (defined $lineoffsets[0] && !defined $lineoffsets[0][0]) {
+ shift @lineoffsets;
+ }
+}
+
+sub check_line_offsets {
+ my $v = shift;
+ for(my $i = 0; $i < @lineoffsets; $i++) {
+ warn "lineoffsets[$i] undef at $v" if (!defined $lineoffsets[$i]);
+ warn "lineoffsets[$i][0] undef at $v" if (!defined $lineoffsets[$i][0]);
+ }
+}
+
+my $calls_to_lineoffsets = 1;
+sub _insert_into_lineoffsets {
+ my ($start, $offset) = @_;
+ #warn sprintf("lineoffsets has %d elements (%d)", scalar @lineoffsets, $calls_to_lineoffsets++);
+
+ if (@lineoffsets == 0) {
+ push @lineoffsets, [ $start, $offset ];
+ return;
+ }
+
+ # replace this with a binary search later ## FIXME ##
+ my $i = 0;
+ while ($i < @lineoffsets && $start < $lineoffsets[$i][0]) {
+ $i++;
+ }
+
+ if ($i < @lineoffsets && $start == $lineoffsets[$i][0]) {
+ $lineoffsets[$i][1] += $offset;
+ } else {
+ push @lineoffsets, [ $start, $offset ];
+ @lineoffsets = sort { $a->[0] <=> $b->[0] } @lineoffsets;
+ }
+}
+
+sub find_actual_start {
+ my ($start) = @_;
+ my $offset = 0;
+
+ for(my $i = 0; $i < @lineoffsets && $start < $lineoffsets[$i][0] ; $i++) {
+ $offset += $lineoffsets[$i][1];
+ }
+
+ printf("hunk_actual_start = %d (%d + %d)\n", $start + $offset, $start, $offset);
+ return $start + $offset;
+}
+
+
+sub git_commit_info {
+ my ($rev) = @_;
+ open(COMMIT, "-|","git-cat-file", "commit", $rev)
+ or die "Failed to call git-cat-file: $!";
+
+ my %info;
+ while(<COMMIT>) {
+ chomp;
+ last if (length $_ == 0);
+
+ if (m/^author (.*) <(.*)> (.*)$/) {
+ $info{'author'} = $1;
+ $info{'author_email'} = $2;
+ $info{'author_date'} = $3;
+ } elsif (m/^committer (.*) <(.*)> (.*)$/) {
+ $info{'committer'} = $1;
+ $info{'committer_email'} = $2;
+ $info{'committer_date'} = $3;
+ }
+ }
+ close(COMMIT);
+
+ return %info;
+}
--
1.1.4.g31e9b
--
Ryan Anderson
sometimes Pug Majere
^ permalink raw reply related
* Re: Notes on Subproject Support
From: Junio C Hamano @ 2006-01-23 8:00 UTC (permalink / raw)
To: git
In-Reply-To: <7v3bjfafql.fsf@assigned-by-dhcp.cox.net>
Junio C Hamano <junkio@cox.net> writes:
> This is still a draft/WIP, but "release early" is a good
> discipline, so...
Tentatively I'm placing this document in the 'todo' branch, so
that people interested in the changes can ask gitweb to show
diffs, until I can find a better way and location to manage it.
I do not think it is suitable to be in the Documentation/ area,
due to its being an early draft and its just-one-ofthe-proposals
status.
^ permalink raw reply
* Re: Notes on Subproject Support
From: Alexander Litvinov @ 2006-01-23 6:06 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Daniel Barkalow, git, Petr Baudis
In-Reply-To: <7v64ob1omh.fsf@assigned-by-dhcp.cox.net>
In our development we use a little bit other case (it is simplified):
1. We have self written C++ library for linked list implementation. Lets call
it liblist.
2. We have project A that use liblist as separate directory and project B that
use this library too.
Currently we have 3 cvs projects with cvs-modules for linking liblist to A and
B. During development of A and B we often modify liblist to fix bugs and
these changes are immidatly visible to all projects who use liblist.
After full implementation of bind functionality I see one restriction: I have
to use one repo for storing all three projects: A, B and liblist to make
changes of liblist visible to all projects. The solution is to make separate
repos and on each change of liblist in prokect A push these changes to
liblist repo and pull them into project B again - bit hacky solution.
^ permalink raw reply
* Re: Notes on Subproject Support
From: Junio C Hamano @ 2006-01-23 5:48 UTC (permalink / raw)
To: Daniel Barkalow; +Cc: git, Petr Baudis
In-Reply-To: <7v7j8r7e7s.fsf@assigned-by-dhcp.cox.net>
Junio C Hamano <junkio@cox.net> writes:
> In other words, I think the design so far does not require us to
> touch tree objects at all, and I'd be happy if we do not have to.
>
> One reason I started the bound commit approach was exactly
> because I only needed to muck with commit objects and did not
> have to touch trees and blobs; after trying to implement the
> core level for "gitlink", which I ended up touching quite a lot
> and have abandoned for now.
BTW, let's digress a bit.
I think recording "commit" in the tree objects is in line with
the logical organization described in README: "blob" and "tree"
represent a state, and have *nothing* to do with how we came
about to that state. The historyh is described in "commit"
objects. The bound commit approach keeps that property.
The "gitlink" approach, as I understand how Linus outlined in
his original suggestion, is a bit different. The link objects
appear in tree objects, and when you "git cat-file link" one of
them, you would see something like this:
commit 5b2bcc7b2d546c636f79490655b3347acc91d17f
name kernel
So in that sense, "gitlink" approach departs from the original
premise of "commit" being the only thing that ties things
together. Tree objects with "gitlink" know where they are in
the history [*1*].
By this, I do not mean to say that "gitlink" approach is
inferior because it breaks that original premise. I am just
pointing it out as a difference between two approaches.
Now, the current way index file is used is as a staging area to
create a new commit on top of the tip of the current branch.
However, it is interesting to note that logically, by itself
*alone*, it cannot be used that way. The information the index
file records is something that can be used to write out a tree
object, and not a commit, because it does not know where the
current state sits in the history. We have two separate files,
$GIT_DIR/HEAD that records which branch we are on, and the
branch head ref the HEAD points at, which records where the
current index came from, for that purpose. The latter tells us
what commit we should use as the parent commit if we create such
a commit, and the former tells us which branch head to update
once we create one. So in that sense, the index file is just a
staging area to create a new tree, not a new commit.
We could have done things differently. I am not advocating to
do the following change, but offering a possibility as a thought
experiment. It just felt interesting enough to point them out.
The index file could have recorded what commit the current state
recorded in the index came from. By recording the commit the
index was read from in the index itself, independently from the
$GIT_DIR/refs/heads/$branch file, we could have been able to
allow fetching into the current branch. When the $branch file
for the current branch was updated by a fast-forward fetch, we
would notice that the commit recorded there no longer match what
is recorded in the index.
Another interesting consequence is if the development is a
single repository and linear, we did not even need any file in
$GIT_DIR/refs/ ("branchless git"). The commit recorded as the
topmost in the index file itself would have served as the tip of
the development, and we would have been able to tangle the
history starting from the commit in the index file.
While we are doing a thought experiment, let's say we allow to
record more than one commits the current index is based upon.
'git merge' would record all the parent commits there, so that
writing out the merge result out of the index file as a tree and
then recording these commits as parents would have been the way
to create a merge commit. We would not need the auxiliary file
$GIT_DIR/MERGE_HEAD if we did so.
In other words, if the index file recorded the commits its
contents were based upon, instead of being a staging area for a
new tree, it would have been a staging area for a new commit.
Now, the latest proposal, borrowing your idea, records the
subproject commits bound to subdirectories in the index itself.
This is halfway to make the index file a staging area for the
next commit. If we were to do that, we also *could* record the
commits the current index is based upon, so that it can truly be
used as a staging area to create a new commit, not just a tree.
On the other hand, this could be a reason *not* to do the
`update-index --bind` to record the subproject information in
the index file. An auxiliary file such as $GIT_DIR/bind might
be sufficient, just like $GIT_DIR/MERGE_HEAD has been good
enough for us so far. One difference between MERGE_HEAD and
bind is that the former is very transient -- only exists during
a merge while the latter is persistent while the top commit is
checked out and being worked on.
[Footnote]
*1* One good property of "gitlink" approach is that we *could*
extend this blob-like object to store arbitrary human readable
information to represent a point-in-time from an arbitrary
foreign SCM system. IOW, we do not necessarily have to require
`commit` line that name a git commit to be there. It could say
"Please slurp http://www.kernel.org/pub/software/.../git.tar.gz
and extract it in git/ directory".
Of course, for such a toplevel project commit, the tool may not
be able to do a checkout automatically and require the user to
cat-file the link, download a tarball and extract the subtree
there manually.
The bound commit approach requires you to have git commit object
names on the `bind` lines, and it is fundamentally much harder
to extend it to allow interfacing with foreign (non-)SCM
systems.
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox