{"thread":{"id":"64645","subject":"changes for adding new features --snapshot,","startedAt":"2025-12-18T05:02:46Z","lastAt":"2025-12-19T11:05:35Z","messageCount":3,"participants":["Abdullah","Phillip Wood","Chris Torek"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"532411","messageId":"CAG-pPd-4StfLAsassx-fqzavHJkAmi8P5UkgvqE3Yz-yGJ7GUw@mail.gmail.com","threadId":"64645","inReplyTo":null,"subject":"changes for adding new features --snapshot,","fromName":"Abdullah","fromEmail":"abdullahindo888@gmail.com","sentAt":"2025-12-18T05:02:32Z","receivedAt":"2025-12-18T05:02:46Z","isPatch":false,"sender":{"key":"abdullahindo888@gmail.com","avatar":null},"body":"---\n builtin/commit.c |   2 +\n snapshot.c       | 108 +++++++++++++++++++++++++++++++++++++++++++++++\n snapshot.h       |  13 ++++++\n 3 files changed, 123 insertions(+)\n create mode 100644 snapshot.c\n create mode 100644 snapshot.h\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 0243f17d53..e880409be7 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -43,6 +43,7 @@\n #include \"commit-graph.h\"\n #include \"pretty.h\"\n #include \"trailer.h\"\n+#include \"snapshot.c\"\n\n static const char * const builtin_commit_usage[] = {\n  N_(\"git commit [-a | --interactive | --patch] [-s] [-v] [-u[<mode>]]\n[--amend]\\n\"\n@@ -1545,6 +1546,7 @@ struct repository *repo UNUSED)\n  int fd;\n  struct object_id oid;\n  static struct option builtin_status_options[] = {\n+ OPT_BOOLEAN(0, \"snapshot\", &opts.snapshot, \"Create snapshot of staged files\")\n  OPT__VERBOSE(&verbose, N_(\"be verbose\")),\n  OPT_SET_INT('s', \"short\", &status_format,\n      N_(\"show status concisely\"), STATUS_FORMAT_SHORT),\ndiff --git a/snapshot.c b/snapshot.c\nnew file mode 100644\nindex 0000000000..8c117a7314\n--- /dev/null\n+++ b/snapshot.c\n@@ -0,0 +1,108 @@\n+#include \"snapshot.h\"\n+#include \"run-command.h\"\n+#include \"strbuf.h\"\n+#include \"utf8.h\"\n+#include <time.h>\n+#include <stdio.h>\n+#include <stdlib.h>\n+#include <string.h>\n+#include <sys/stat.h>\n+#include <errno.h>\n+\n+#define DEFAULT_SNAPSHOT_DIR \".git/snapshots\"\n+/*\n+## Idea\n+The `--snapshot` flag would create a copy of all staged files in a\ntimestamped folder before the commit is made. Users could configure\nthe snapshot folder, defaulting to `.git/snapshots`.\n+\n+## Example Usage\n+\n+# Enable snapshot feature\n+git config snapshot.enable true\n+\n+# Optional: configure snapshot folder\n+git config snapshot.folder \"../snapshots\"\n+\n+# Stage files and commit with snapshot\n+git add .\n+git commit -m \"Initial commit\" --snapshot\n+\n+# Example snapshot folder created:\n+# ../snapshots/2025-12-17_14-30-00_Initial_commit/\n+\n+# Notes:\n+these codes are never tested, I send it just to know if it fits to\nyour development\n+*/\n+\n+// Get snapshot folder from config, or fallback to default\n+static char *get_snapshot_dir(void) {\n+    const char *cfg_dir = git_config_get_string(\"snapshot.folder\");\n+    if (cfg_dir && *cfg_dir)\n+        return strdup(cfg_dir);\n+    return strdup(DEFAULT_SNAPSHOT_DIR);\n+}\n+\n+// Check if snapshot is enabled\n+int snapshot_enabled(void) {\n+    const char *val = git_config_get_string(\"snapshot.enable\");\n+    return val && strcmp(val, \"true\") == 0;\n+}\n+\n+// Get current timestamp for folder name\n+static void get_timestamp(char *buffer, size_t size) {\n+    time_t now = time(NULL);\n+    struct tm *tm_info = localtime(&now);\n+    strftime(buffer, size, \"%Y-%m-%d_%H-%M-%S\", tm_info);\n+}\n+\n+// Create snapshot of staged files\n+int create_snapshot(const char *message) {\n+    char timestamp[64];\n+    char snapshot_path[1024];\n+\n+    char *snapshot_dir = get_snapshot_dir();\n+    if (!snapshot_dir) {\n+        fprintf(stderr, \"Failed to get snapshot directory\\n\");\n+        return -1;\n+    }\n+\n+    // Ensure base folder exists\n+    if (mkdir(snapshot_dir, 0755) != 0 && errno != EEXIST) {\n+        perror(\"mkdir snapshot_dir\");\n+        free(snapshot_dir);\n+        return -1;\n+    }\n+\n+    get_timestamp(timestamp, sizeof(timestamp));\n+\n+    // Sanitize commit message\n+    char sanitized_msg[512];\n+    snprintf(sanitized_msg, sizeof(sanitized_msg), \"%s\", message);\n+    for (char *p = sanitized_msg; *p; p++)\n+        if (*p == ' ') *p = '_';\n+\n+    // Create snapshot folder path\n+    snprintf(snapshot_path, sizeof(snapshot_path), \"%s/%s_%s\",\nsnapshot_dir, timestamp, sanitized_msg);\n+\n+    if (mkdir(snapshot_path, 0755) != 0) {\n+        perror(\"mkdir snapshot_path\");\n+        free(snapshot_dir);\n+        return -1;\n+    }\n+\n+    // Copy staged files\n+    char cmd[2048];\n+    snprintf(cmd, sizeof(cmd),\n+             \"git diff --name-only --cached | xargs -I{} cp --parents\n{} \\\"%s\\\"\",\n+             snapshot_path);\n+\n+    int ret = system(cmd);\n+    if (ret != 0) {\n+        fprintf(stderr, \"Failed to copy staged files\\n\");\n+        free(snapshot_dir);\n+        return -1;\n+    }\n+\n+    printf(\"Snapshot created at %s\\n\", snapshot_path);\n+    free(snapshot_dir);\n+    return 0;\n+}\ndiff --git a/snapshot.h b/snapshot.h\nnew file mode 100644\nindex 0000000000..7542538f1f\n--- /dev/null\n+++ b/snapshot.h\n@@ -0,0 +1,13 @@\n+#ifndef SNAPSHOT_H\n+#define SNAPSHOT_H\n+\n+#include \"cache.h\"\n+#include \"dir.h\"\n+\n+// Check if snapshot feature is enabled\n+int snapshot_enabled(void);\n+\n+// Create snapshot of staged files with commit message\n+int create_snapshot(const char *message);\n+\n+#endif // SNAPSHOT_H\n-- \n2.34.1\n"},{"id":"532535","messageId":"03034879-2d8e-4ab1-96ff-ff125e7d059e@gmail.com","threadId":"64645","inReplyTo":"CAG-pPd-4StfLAsassx-fqzavHJkAmi8P5UkgvqE3Yz-yGJ7GUw@mail.gmail.com","subject":"Re: changes for adding new features --snapshot,","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2025-12-19T10:41:46Z","receivedAt":"2025-12-19T10:41:52Z","isPatch":false,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"It would be helpful if there was a commit message explaining what this \nnew feature is and why it is needed. There are some comments in the code \nexplaining what it does but not why it is useful. Given that a commit is \na snapshot of the working copy I'm not sure why you'd want to save an \nadditional copy. As the code comments indicate you have not tested this \ncode I wonder how you know that what you're proposing is useful.\n\nThanks\n\nPhillip\n\nOn 18/12/2025 05:02, Abdullah wrote:\n> ---\n>   builtin/commit.c |   2 +\n>   snapshot.c       | 108 +++++++++++++++++++++++++++++++++++++++++++++++\n>   snapshot.h       |  13 ++++++\n>   3 files changed, 123 insertions(+)\n>   create mode 100644 snapshot.c\n>   create mode 100644 snapshot.h\n> \n> diff --git a/builtin/commit.c b/builtin/commit.c\n> index 0243f17d53..e880409be7 100644\n> --- a/builtin/commit.c\n> +++ b/builtin/commit.c\n> @@ -43,6 +43,7 @@\n>   #include \"commit-graph.h\"\n>   #include \"pretty.h\"\n>   #include \"trailer.h\"\n> +#include \"snapshot.c\"\n> \n>   static const char * const builtin_commit_usage[] = {\n>    N_(\"git commit [-a | --interactive | --patch] [-s] [-v] [-u[<mode>]]\n> [--amend]\\n\"\n> @@ -1545,6 +1546,7 @@ struct repository *repo UNUSED)\n>    int fd;\n>    struct object_id oid;\n>    static struct option builtin_status_options[] = {\n> + OPT_BOOLEAN(0, \"snapshot\", &opts.snapshot, \"Create snapshot of staged files\")\n>    OPT__VERBOSE(&verbose, N_(\"be verbose\")),\n>    OPT_SET_INT('s', \"short\", &status_format,\n>        N_(\"show status concisely\"), STATUS_FORMAT_SHORT),\n> diff --git a/snapshot.c b/snapshot.c\n> new file mode 100644\n> index 0000000000..8c117a7314\n> --- /dev/null\n> +++ b/snapshot.c\n> @@ -0,0 +1,108 @@\n> +#include \"snapshot.h\"\n> +#include \"run-command.h\"\n> +#include \"strbuf.h\"\n> +#include \"utf8.h\"\n> +#include <time.h>\n> +#include <stdio.h>\n> +#include <stdlib.h>\n> +#include <string.h>\n> +#include <sys/stat.h>\n> +#include <errno.h>\n> +\n> +#define DEFAULT_SNAPSHOT_DIR \".git/snapshots\"\n> +/*\n> +## Idea\n> +The `--snapshot` flag would create a copy of all staged files in a\n> timestamped folder before the commit is made. Users could configure\n> the snapshot folder, defaulting to `.git/snapshots`.\n> +\n> +## Example Usage\n> +\n> +# Enable snapshot feature\n> +git config snapshot.enable true\n> +\n> +# Optional: configure snapshot folder\n> +git config snapshot.folder \"../snapshots\"\n> +\n> +# Stage files and commit with snapshot\n> +git add .\n> +git commit -m \"Initial commit\" --snapshot\n> +\n> +# Example snapshot folder created:\n> +# ../snapshots/2025-12-17_14-30-00_Initial_commit/\n> +\n> +# Notes:\n> +these codes are never tested, I send it just to know if it fits to\n> your development\n> +*/\n> +\n> +// Get snapshot folder from config, or fallback to default\n> +static char *get_snapshot_dir(void) {\n> +    const char *cfg_dir = git_config_get_string(\"snapshot.folder\");\n> +    if (cfg_dir && *cfg_dir)\n> +        return strdup(cfg_dir);\n> +    return strdup(DEFAULT_SNAPSHOT_DIR);\n> +}\n> +\n> +// Check if snapshot is enabled\n> +int snapshot_enabled(void) {\n> +    const char *val = git_config_get_string(\"snapshot.enable\");\n> +    return val && strcmp(val, \"true\") == 0;\n> +}\n> +\n> +// Get current timestamp for folder name\n> +static void get_timestamp(char *buffer, size_t size) {\n> +    time_t now = time(NULL);\n> +    struct tm *tm_info = localtime(&now);\n> +    strftime(buffer, size, \"%Y-%m-%d_%H-%M-%S\", tm_info);\n> +}\n> +\n> +// Create snapshot of staged files\n> +int create_snapshot(const char *message) {\n> +    char timestamp[64];\n> +    char snapshot_path[1024];\n> +\n> +    char *snapshot_dir = get_snapshot_dir();\n> +    if (!snapshot_dir) {\n> +        fprintf(stderr, \"Failed to get snapshot directory\\n\");\n> +        return -1;\n> +    }\n> +\n> +    // Ensure base folder exists\n> +    if (mkdir(snapshot_dir, 0755) != 0 && errno != EEXIST) {\n> +        perror(\"mkdir snapshot_dir\");\n> +        free(snapshot_dir);\n> +        return -1;\n> +    }\n> +\n> +    get_timestamp(timestamp, sizeof(timestamp));\n> +\n> +    // Sanitize commit message\n> +    char sanitized_msg[512];\n> +    snprintf(sanitized_msg, sizeof(sanitized_msg), \"%s\", message);\n> +    for (char *p = sanitized_msg; *p; p++)\n> +        if (*p == ' ') *p = '_';\n> +\n> +    // Create snapshot folder path\n> +    snprintf(snapshot_path, sizeof(snapshot_path), \"%s/%s_%s\",\n> snapshot_dir, timestamp, sanitized_msg);\n> +\n> +    if (mkdir(snapshot_path, 0755) != 0) {\n> +        perror(\"mkdir snapshot_path\");\n> +        free(snapshot_dir);\n> +        return -1;\n> +    }\n> +\n> +    // Copy staged files\n> +    char cmd[2048];\n> +    snprintf(cmd, sizeof(cmd),\n> +             \"git diff --name-only --cached | xargs -I{} cp --parents\n> {} \\\"%s\\\"\",\n> +             snapshot_path);\n> +\n> +    int ret = system(cmd);\n> +    if (ret != 0) {\n> +        fprintf(stderr, \"Failed to copy staged files\\n\");\n> +        free(snapshot_dir);\n> +        return -1;\n> +    }\n> +\n> +    printf(\"Snapshot created at %s\\n\", snapshot_path);\n> +    free(snapshot_dir);\n> +    return 0;\n> +}\n> diff --git a/snapshot.h b/snapshot.h\n> new file mode 100644\n> index 0000000000..7542538f1f\n> --- /dev/null\n> +++ b/snapshot.h\n> @@ -0,0 +1,13 @@\n> +#ifndef SNAPSHOT_H\n> +#define SNAPSHOT_H\n> +\n> +#include \"cache.h\"\n> +#include \"dir.h\"\n> +\n> +// Check if snapshot feature is enabled\n> +int snapshot_enabled(void);\n> +\n> +// Create snapshot of staged files with commit message\n> +int create_snapshot(const char *message);\n> +\n> +#endif // SNAPSHOT_H\n\n"},{"id":"532536","messageId":"CAPx1Gvfuacq0rt-6LymiEUUdeKsE0+s8x2x66_zD=zatWui0RQ@mail.gmail.com","threadId":"64645","inReplyTo":"03034879-2d8e-4ab1-96ff-ff125e7d059e@gmail.com","subject":"Re: changes for adding new features --snapshot,","fromName":"Chris Torek","fromEmail":"chris.torek@gmail.com","sentAt":"2025-12-19T11:05:21Z","receivedAt":"2025-12-19T11:05:35Z","isPatch":false,"sender":{"key":"chris.torek@gmail.com","avatar":"https://avatars.githubusercontent.com/u/16826774?v=4"},"body":"On Fri, Dec 19, 2025 at 2:41 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n> It would be helpful if there was a commit message explaining what this\n> new feature is and why it is needed. There are some comments in the code\n> explaining what it does but not why it is useful. Given that a commit is\n> a snapshot of the working copy I'm not sure why you'd want to save an\n> additional copy.\n\nTo be a bit more precise, a commit is just a little bit more than\na snapshot of all staged files: it consists of a *tree object*, which\nis this snapshot, plus a *commit object*, which contains metadata\nto explain who made the commit, when, and why, and what\ncommit(s) come immediately before the new commit.\n\nThe sample code itself makes copies of some (but not all)\nstaged files, rather than a complete snapshot of all staged\nfiles. Abdullah is probably under the impression that Git\nsaves only *changed* files, and that the staging area therefore\ncontains only these changed files, but that's not the case: every\nsnapshot contains *every* file.\n\nThis takes no extra disk space because of Git's clever method\nof storing snapshots. The code in the diff does not make use of\nthis clever method; instead, it makes clumsy actual copies,\nwhich generally do take up extra disk space. That's presumably\nwhy there's a `--name-only` diff here:\n\n>> git diff --name-only --cached | xargs -I{} cp --parents {} \\\"%s\\\"\n\nTo make a Git-style snapshot containing all files but using\nno extra space, we could more simply run `git commit-tree`,\nwhich produces the tree hash ID; we'd then save that somewhere\nthat Git can find it so that Git won't garbage collect the tree\nlater. The obvious place to save it is in a tag-like reference\n(perhaps an actual tag, perhaps some new `refs/snaps/`\nspace or similar). But it's probably superior simply to create\nan actual commit, without putting it on any branch, in the\nsame way that `git stash` makes commits but puts them on\nno branch.\n\nIn any case, you (Phillip) are right that this doesn't explain\nthe use case for these extra \"snapshot\" commits or trees.\nI rather suspect that the intended use is better-served simply\nby making a branch (as often seems to me to be the cases\nfor which people use `git stash`...).\n\nChris\n"}]}