author | Junio C Hamano <gitster@pobox.com> | |
Thu, 4 Dec 2008 06:44:26 +0000 (22:44 -0800) | ||
committer | Junio C Hamano <gitster@pobox.com> | |
Thu, 4 Dec 2008 06:44:26 +0000 (22:44 -0800) |
* dm/svn-remote:
git-svn: Make branch use correct svn-remote
git-svn: Make branch use correct svn-remote
16 files changed:
.gitignore | patch | blob | history | |
Makefile | patch | blob | history | |
builtin-commit.c | patch | blob | history | |
builtin-rm.c | patch | blob | history | |
builtin-write-tree.c | patch | blob | history | |
cache-tree.c | patch | blob | history | |
cache.h | patch | blob | history | |
git-filter-branch.sh | patch | blob | history | |
git-lost-found.sh | patch | blob | history | |
git-pull.sh | patch | blob | history | |
git-rebase.sh | patch | blob | history | |
git-submodule.sh | patch | blob | history | |
gitweb/gitweb.perl | patch | blob | history | |
read-cache.c | patch | blob | history | |
t/t2203-add-intent.sh | patch | blob | history | |
t/t3600-rm.sh | patch | blob | history |
diff --git a/.gitignore b/.gitignore
index bbaf9de0bca797508b9c2a011d0d18eac8907157..d9adce585af99e617a2906a89029a92045a79538 100644 (file)
--- a/.gitignore
+++ b/.gitignore
git-show-branch
git-show-index
git-show-ref
+git-stage
git-stash
git-status
git-stripspace
diff --git a/Makefile b/Makefile
index 9577d6fc734de9912c4099758f2f68b2ae86c57b..51581973ab1c75e847474948bbe444db261b0e53 100644 (file)
--- a/Makefile
+++ b/Makefile
BUILT_INS += git-peek-remote$X
BUILT_INS += git-repo-config$X
BUILT_INS += git-show$X
+BUILT_INS += git-stage$X
BUILT_INS += git-status$X
BUILT_INS += git-whatchanged$X
diff --git a/builtin-commit.c b/builtin-commit.c
index 1677e6b45f112857ecae1c5ddccf9eb6a3d11f2a..2b499fa543e4fd969ee0dcf1e878cbe19f4ba02f 100644 (file)
--- a/builtin-commit.c
+++ b/builtin-commit.c
active_cache_tree = cache_tree();
if (cache_tree_update(active_cache_tree,
active_cache, active_nr, 0, 0) < 0) {
- error("Error building trees; the index is unmerged?");
+ error("Error building trees");
return 0;
}
diff --git a/builtin-rm.c b/builtin-rm.c
index b7126e3e25d4f52ae8c4a120d524a24c11129785..c11f45585825962e61bdee4bdd6df206f0a141c6 100644 (file)
--- a/builtin-rm.c
+++ b/builtin-rm.c
static int check_local_mod(unsigned char *head, int index_only)
{
- /* items in list are already sorted in the cache order,
+ /*
+ * Items in list are already sorted in the cache order,
* so we could do this a lot more efficiently by using
* tree_desc based traversal if we wanted to, but I am
* lazy, and who cares if removal of files is a tad
*/
continue;
}
+
+ /*
+ * "rm" of a path that has changes need to be treated
+ * carefully not to allow losing local changes
+ * accidentally. A local change could be (1) file in
+ * work tree is different since the index; and/or (2)
+ * the user staged a content that is different from
+ * the current commit in the index.
+ *
+ * In such a case, you would need to --force the
+ * removal. However, "rm --cached" (remove only from
+ * the index) is safe if the index matches the file in
+ * the work tree or the HEAD commit, as it means that
+ * the content being removed is available elsewhere.
+ */
+
+ /*
+ * Is the index different from the file in the work tree?
+ */
if (ce_match_stat(ce, &st, 0))
local_changes = 1;
+
+ /*
+ * Is the index different from the HEAD commit? By
+ * definition, before the very initial commit,
+ * anything staged in the index is treated by the same
+ * way as changed from the HEAD.
+ */
if (no_head
|| get_tree_entry(head, name, sha1, &mode)
|| ce->ce_mode != create_ce_mode(mode)
|| hashcmp(ce->sha1, sha1))
staged_changes = 1;
- if (local_changes && staged_changes &&
- !(index_only && is_empty_blob_sha1(ce->sha1)))
- errs = error("'%s' has staged content different "
- "from both the file and the HEAD\n"
- "(use -f to force removal)", name);
+ /*
+ * If the index does not match the file in the work
+ * tree and if it does not match the HEAD commit
+ * either, (1) "git rm" without --cached definitely
+ * will lose information; (2) "git rm --cached" will
+ * lose information unless it is about removing an
+ * "intent to add" entry.
+ */
+ if (local_changes && staged_changes) {
+ if (!index_only || !(ce->ce_flags & CE_INTENT_TO_ADD))
+ errs = error("'%s' has staged content different "
+ "from both the file and the HEAD\n"
+ "(use -f to force removal)", name);
+ }
else if (!index_only) {
- /* It's not dangerous to "git rm --cached" a
- * file if the index matches the file or the
- * HEAD, since it means the deleted content is
- * still available somewhere.
- */
if (staged_changes)
errs = error("'%s' has changes staged in the index\n"
"(use --cached to keep the file, "
diff --git a/builtin-write-tree.c b/builtin-write-tree.c
index 52a3c015ff8e4611522bd41078bdb2934d288d35..9d640508dd8eb62201b286490b7f83486470d611 100644 (file)
--- a/builtin-write-tree.c
+++ b/builtin-write-tree.c
die("%s: error reading the index", me);
break;
case WRITE_TREE_UNMERGED_INDEX:
- die("%s: error building trees; the index is unmerged?", me);
+ die("%s: error building trees", me);
break;
case WRITE_TREE_PREFIX_ERROR:
die("%s: prefix %s not found", me, prefix);
diff --git a/cache-tree.c b/cache-tree.c
index 5f8ee87bb1c446341b640c2f978a658d6bfcfcd0..3d8f218a5f9e838b15e1d56113a4dd56904ee544 100644 (file)
--- a/cache-tree.c
+++ b/cache-tree.c
funny = 0;
for (i = 0; i < entries; i++) {
struct cache_entry *ce = cache[i];
- if (ce_stage(ce)) {
+ if (ce_stage(ce) || (ce->ce_flags & CE_INTENT_TO_ADD)) {
if (10 < ++funny) {
fprintf(stderr, "...\n");
break;
}
- fprintf(stderr, "%s: unmerged (%s)\n",
- ce->name, sha1_to_hex(ce->sha1));
+ if (ce_stage(ce))
+ fprintf(stderr, "%s: unmerged (%s)\n",
+ ce->name, sha1_to_hex(ce->sha1));
+ else
+ fprintf(stderr, "%s: not added yet\n",
+ ce->name);
}
}
if (funny)
index 487e5e1b1bf66007a2d2d753daaf97ddb879b85a..f15b3fc906b8a3525e3d81b2c2fb826b5fec44b8 100644 (file)
--- a/cache.h
+++ b/cache.h
char name[FLEX_ARRAY]; /* more */
};
+/*
+ * This struct is used when CE_EXTENDED bit is 1
+ * The struct must match ondisk_cache_entry exactly from
+ * ctime till flags
+ */
+struct ondisk_cache_entry_extended {
+ struct cache_time ctime;
+ struct cache_time mtime;
+ unsigned int dev;
+ unsigned int ino;
+ unsigned int mode;
+ unsigned int uid;
+ unsigned int gid;
+ unsigned int size;
+ unsigned char sha1[20];
+ unsigned short flags;
+ unsigned short flags2;
+ char name[FLEX_ARRAY]; /* more */
+};
+
struct cache_entry {
unsigned int ce_ctime;
unsigned int ce_mtime;
#define CE_VALID (0x8000)
#define CE_STAGESHIFT 12
-/* In-memory only */
+/*
+ * Range 0xFFFF0000 in ce_flags is divided into
+ * two parts: in-memory flags and on-disk ones.
+ * Flags in CE_EXTENDED_FLAGS will get saved on-disk
+ * if you want to save a new flag, add it in
+ * CE_EXTENDED_FLAGS
+ *
+ * In-memory only flags
+ */
#define CE_UPDATE (0x10000)
#define CE_REMOVE (0x20000)
#define CE_UPTODATE (0x40000)
#define CE_HASHED (0x100000)
#define CE_UNHASHED (0x200000)
+/*
+ * Extended on-disk flags
+ */
+#define CE_INTENT_TO_ADD 0x20000000
+/* CE_EXTENDED2 is for future extension */
+#define CE_EXTENDED2 0x80000000
+
+#define CE_EXTENDED_FLAGS (CE_INTENT_TO_ADD)
+
+/*
+ * Safeguard to avoid saving wrong flags:
+ * - CE_EXTENDED2 won't get saved until its semantic is known
+ * - Bits in 0x0000FFFF have been saved in ce_flags already
+ * - Bits in 0x003F0000 are currently in-memory flags
+ */
+#if CE_EXTENDED_FLAGS & 0x803FFFFF
+#error "CE_EXTENDED_FLAGS out of range"
+#endif
+
/*
* Copy the sha1 and stat state of a cache entry from one to
* another. But we never change the name, or the hash state!
}
#define ce_size(ce) cache_entry_size(ce_namelen(ce))
-#define ondisk_ce_size(ce) ondisk_cache_entry_size(ce_namelen(ce))
+#define ondisk_ce_size(ce) (((ce)->ce_flags & CE_EXTENDED) ? \
+ ondisk_cache_entry_extended_size(ce_namelen(ce)) : \
+ ondisk_cache_entry_size(ce_namelen(ce)))
#define ce_stage(ce) ((CE_STAGEMASK & (ce)->ce_flags) >> CE_STAGESHIFT)
#define ce_uptodate(ce) ((ce)->ce_flags & CE_UPTODATE)
#define ce_mark_uptodate(ce) ((ce)->ce_flags |= CE_UPTODATE)
(S_ISREG(mode) ? (S_IFREG | ce_permissions(mode)) : \
S_ISLNK(mode) ? S_IFLNK : S_ISDIR(mode) ? S_IFDIR : S_IFGITLINK)
-#define cache_entry_size(len) ((offsetof(struct cache_entry,name) + (len) + 8) & ~7)
-#define ondisk_cache_entry_size(len) ((offsetof(struct ondisk_cache_entry,name) + (len) + 8) & ~7)
+#define flexible_size(STRUCT,len) ((offsetof(struct STRUCT,name) + (len) + 8) & ~7)
+#define cache_entry_size(len) flexible_size(cache_entry,len)
+#define ondisk_cache_entry_size(len) flexible_size(ondisk_cache_entry,len)
+#define ondisk_cache_entry_extended_size(len) flexible_size(ondisk_cache_entry_extended,len)
struct index_state {
struct cache_entry **cache;
diff --git a/git-filter-branch.sh b/git-filter-branch.sh
index 81392add0b852f51f63a470727c33e0c306260d8..c106f45af73446d26630030f77107efa520aa296 100755 (executable)
--- a/git-filter-branch.sh
+++ b/git-filter-branch.sh
*)
# The commit may not have the subdirectory at all
err=$(git read-tree -i -m $commit:"$filter_subdir" 2>&1) || {
- if ! git rev-parse --verify $commit:"$filter_subdir" 2>/dev/null
+ if ! git rev-parse -q --verify $commit:"$filter_subdir"
then
rm -f "$GIT_INDEX_FILE"
else
diff --git a/git-lost-found.sh b/git-lost-found.sh
index 9cedaf80ceac1d4100adf3cfb152c76c7f945e4d..0b3e8c7a8652478a7dd3d629fde9240cac6d6e2b 100755 (executable)
--- a/git-lost-found.sh
+++ b/git-lost-found.sh
do
case "$dangling" in
dangling)
- if git rev-parse --verify "$sha1^0" >/dev/null 2>/dev/null
+ if git rev-parse -q --verify "$sha1^0" >/dev/null
then
dir="$laf/commit"
git show-branch "$sha1"
diff --git a/git-pull.sh b/git-pull.sh
index 1cac898a2443538144d45006b9adb3c0aaf2d1fa..2c7f432dc04254dcb9906f4d078eb16d195848ca 100755 (executable)
--- a/git-pull.sh
+++ b/git-pull.sh
test -z "$origin" && origin=$(get_default_remote)
reflist="$(get_remote_refs_for_fetch "$@" 2>/dev/null |
sed "s|refs/heads/\(.*\):|\1|")" &&
- oldremoteref="$(git rev-parse --verify \
- "refs/remotes/$origin/$reflist" 2>/dev/null)"
+ oldremoteref="$(git rev-parse -q --verify \
+ "refs/remotes/$origin/$reflist")"
}
-orig_head=$(git rev-parse --verify HEAD 2>/dev/null)
+orig_head=$(git rev-parse -q --verify HEAD)
git fetch $verbosity --update-head-ok "$@" || exit 1
-curr_head=$(git rev-parse --verify HEAD 2>/dev/null)
+curr_head=$(git rev-parse -q --verify HEAD)
if test -n "$orig_head" && test "$curr_head" != "$orig_head"
then
# The fetch involved updating the current branch.
diff --git a/git-rebase.sh b/git-rebase.sh
index 023a6dc94a48f7abf2801359ad68d40909e9b6aa..ea7720d3e29650b09a2805410a08f19d73f287f5 100755 (executable)
--- a/git-rebase.sh
+++ b/git-rebase.sh
switch_to="$2"
if git show-ref --verify --quiet -- "refs/heads/$2" &&
- branch=$(git rev-parse --verify "refs/heads/$2" 2>/dev/null)
+ branch=$(git rev-parse -q --verify "refs/heads/$2")
then
head_name="refs/heads/$2"
- elif branch=$(git rev-parse --verify "$2" 2>/dev/null)
+ elif branch=$(git rev-parse -q --verify "$2")
then
head_name="detached HEAD"
else
diff --git a/git-submodule.sh b/git-submodule.sh
index 220d94ec0c53c9d6c4535d1e50f5d1cb9dc99689..2f47e065fe8b7ca856f4527d6a507a28f1b2a06b 100755 (executable)
--- a/git-submodule.sh
+++ b/git-submodule.sh
test $summary_limit = 0 && return
- if rev=$(git rev-parse --verify "$1^0" 2>/dev/null)
+ if rev=$(git rev-parse -q --verify "$1^0")
then
head=$rev
shift
missing_dst=
test $mod_src = 160000 &&
- ! GIT_DIR="$name/.git" git-rev-parse --verify $sha1_src^0 >/dev/null 2>&1 &&
+ ! GIT_DIR="$name/.git" git-rev-parse -q --verify $sha1_src^0 >/dev/null &&
missing_src=t
test $mod_dst = 160000 &&
- ! GIT_DIR="$name/.git" git-rev-parse --verify $sha1_dst^0 >/dev/null 2>&1 &&
+ ! GIT_DIR="$name/.git" git-rev-parse -q --verify $sha1_dst^0 >/dev/null &&
missing_dst=t
total_commits=
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 2738643950d6f2c0aa15875014c7364b2ee0286d..95988fba4ab743e26b246aa2c22c6e36ef50efaf 100755 (executable)
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
return to_utf8($owner);
}
+# assume that file exists
+sub insert_file {
+ my $filename = shift;
+
+ open my $fd, '<', $filename;
+ print map(to_utf8, <$fd>);
+ close $fd;
+}
+
## ......................................................................
## mimetype related functions
"<body>\n";
if (-f $site_header) {
- open (my $fd, $site_header);
- print <$fd>;
- close $fd;
+ insert_file($site_header);
}
print "<div class=\"page_header\">\n" .
print "</div>\n"; # class="page_footer"
if (-f $site_footer) {
- open (my $fd, $site_footer);
- print <$fd>;
- close $fd;
+ insert_file($site_footer);
}
print "</body>\n" .
git_header_html();
if (-f $home_text) {
print "<div class=\"index_include\">\n";
- open (my $fd, $home_text);
- print <$fd>;
- close $fd;
+ insert_file($home_text);
print "</div>\n";
}
print $cgi->startform(-method => "get") .
print "</table>\n";
if (-s "$projectroot/$project/README.html") {
- if (open my $fd, "$projectroot/$project/README.html") {
- print "<div class=\"title\">readme</div>\n" .
- "<div class=\"readme\">\n";
- print $_ while (<$fd>);
- print "\n</div>\n"; # class="readme"
- close $fd;
- }
+ print "<div class=\"title\">readme</div>\n" .
+ "<div class=\"readme\">\n";
+ insert_file("$projectroot/$project/README.html");
+ print "\n</div>\n"; # class="readme"
}
# we need to request one more than 16 (0..15) to check if
diff --git a/read-cache.c b/read-cache.c
index 22a814311d2cfc032129b43f6da56706c7026b9c..8579663ee09b1e01cf5228bd8be7c5eb371c5d31 100644 (file)
--- a/read-cache.c
+++ b/read-cache.c
if (!ignore_valid && (ce->ce_flags & CE_VALID))
return 0;
+ /*
+ * Intent-to-add entries have not been added, so the index entry
+ * by definition never matches what is in the work tree until it
+ * actually gets added.
+ */
+ if (ce->ce_flags & CE_INTENT_TO_ADD)
+ return DATA_CHANGED | TYPE_CHANGED | MODE_CHANGED;
+
changed = ce_match_stat_basic(ce, st);
/*
ce->ce_flags = namelen;
if (!intent_only)
fill_stat_cache_info(ce, st);
+ else
+ ce->ce_flags |= CE_INTENT_TO_ADD;
if (trust_executable_bit && has_symlinks)
ce->ce_mode = create_ce_mode(st_mode);
if (hdr->hdr_signature != htonl(CACHE_SIGNATURE))
return error("bad signature");
- if (hdr->hdr_version != htonl(2))
+ if (hdr->hdr_version != htonl(2) && hdr->hdr_version != htonl(3))
return error("bad index version");
git_SHA1_Init(&c);
git_SHA1_Update(&c, hdr, size - 20);
static void convert_from_disk(struct ondisk_cache_entry *ondisk, struct cache_entry *ce)
{
size_t len;
+ const char *name;
ce->ce_ctime = ntohl(ondisk->ctime.sec);
ce->ce_mtime = ntohl(ondisk->mtime.sec);
@@ -1145,19 +1156,31 @@ static void convert_from_disk(struct ondisk_cache_entry *ondisk, struct cache_en
/* On-disk flags are just 16 bits */
ce->ce_flags = ntohs(ondisk->flags);
- /* For future extension: we do not understand this entry yet */
- if (ce->ce_flags & CE_EXTENDED)
- die("Unknown index entry format");
hashcpy(ce->sha1, ondisk->sha1);
len = ce->ce_flags & CE_NAMEMASK;
+
+ if (ce->ce_flags & CE_EXTENDED) {
+ struct ondisk_cache_entry_extended *ondisk2;
+ int extended_flags;
+ ondisk2 = (struct ondisk_cache_entry_extended *)ondisk;
+ extended_flags = ntohs(ondisk2->flags2) << 16;
+ /* We do not yet understand any bit out of CE_EXTENDED_FLAGS */
+ if (extended_flags & ~CE_EXTENDED_FLAGS)
+ die("Unknown index entry format %08x", extended_flags);
+ ce->ce_flags |= extended_flags;
+ name = ondisk2->name;
+ }
+ else
+ name = ondisk->name;
+
if (len == CE_NAMEMASK)
- len = strlen(ondisk->name);
+ len = strlen(name);
/*
* NEEDSWORK: If the original index is crafted, this copy could
* go unchecked.
*/
- memcpy(ce->name, ondisk->name, len + 1);
+ memcpy(ce->name, name, len + 1);
}
static inline size_t estimate_cache_size(size_t ondisk_size, unsigned int entries)
{
int size = ondisk_ce_size(ce);
struct ondisk_cache_entry *ondisk = xcalloc(1, size);
+ char *name;
ondisk->ctime.sec = htonl(ce->ce_ctime);
ondisk->ctime.nsec = 0;
ondisk->size = htonl(ce->ce_size);
hashcpy(ondisk->sha1, ce->sha1);
ondisk->flags = htons(ce->ce_flags);
- memcpy(ondisk->name, ce->name, ce_namelen(ce));
+ if (ce->ce_flags & CE_EXTENDED) {
+ struct ondisk_cache_entry_extended *ondisk2;
+ ondisk2 = (struct ondisk_cache_entry_extended *)ondisk;
+ ondisk2->flags2 = htons((ce->ce_flags & CE_EXTENDED_FLAGS) >> 16);
+ name = ondisk2->name;
+ }
+ else
+ name = ondisk->name;
+ memcpy(name, ce->name, ce_namelen(ce));
return ce_write(c, fd, ondisk, size);
}
{
git_SHA_CTX c;
struct cache_header hdr;
- int i, err, removed;
+ int i, err, removed, extended;
struct cache_entry **cache = istate->cache;
int entries = istate->cache_nr;
- for (i = removed = 0; i < entries; i++)
+ for (i = removed = extended = 0; i < entries; i++) {
if (cache[i]->ce_flags & CE_REMOVE)
removed++;
+ /* reduce extended entries if possible */
+ cache[i]->ce_flags &= ~CE_EXTENDED;
+ if (cache[i]->ce_flags & CE_EXTENDED_FLAGS) {
+ extended++;
+ cache[i]->ce_flags |= CE_EXTENDED;
+ }
+ }
+
hdr.hdr_signature = htonl(CACHE_SIGNATURE);
- hdr.hdr_version = htonl(2);
+ /* for extended format, increase version so older git won't try to read it */
+ hdr.hdr_version = htonl(extended ? 3 : 2);
hdr.hdr_entries = htonl(entries - removed);
git_SHA1_Init(&c);
diff --git a/t/t2203-add-intent.sh b/t/t2203-add-intent.sh
index d4de35ea067c4a7c3a9a46ceac57f4cf1086a19a..58a329961e5c269e1b035558db890c9e30375147 100755 (executable)
--- a/t/t2203-add-intent.sh
+++ b/t/t2203-add-intent.sh
! grep "$empty" actual
'
+test_expect_success 'cannot commit with i-t-a entry' '
+ test_tick &&
+ git commit -a -m initial &&
+ git reset --hard &&
+
+ echo xyzzy >rezrov &&
+ echo frotz >nitfol &&
+ git add rezrov &&
+ git add -N nitfol &&
+ test_must_fail git commit
+'
+
+test_expect_success 'can commit with an unrelated i-t-a entry in index' '
+ git reset --hard &&
+ echo xyzzy >rezrov &&
+ echo frotz >nitfol &&
+ git add rezrov &&
+ git add -N nitfol &&
+ git commit -m partial rezrov
+'
+
+test_expect_success 'can "commit -a" with an i-t-a entry' '
+ git reset --hard &&
+ : >nitfol &&
+ git add -N nitfol &&
+ git commit -a -m all
+'
+
test_done
diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh
index 5b4d6f71387ce7f30cb7ee6b596357aa1942ab6e..b7d46e50a87f2609e67f595470021450b1f838a6 100755 (executable)
--- a/t/t3600-rm.sh
+++ b/t/t3600-rm.sh
test_must_fail git ls-files --error-unmatch baz
'
-test_expect_failure 'refuse to remove cached empty file with modifications' '
- touch empty &&
+test_expect_success 'refuse to remove cached empty file with modifications' '
+ >empty &&
git add empty &&
echo content >empty &&
test_must_fail git rm --cached empty