{"thread":{"id":"60813","subject":"Multiple indices / staging areas","startedAt":"2024-01-30T03:47:40Z","lastAt":"2024-02-02T08:36:11Z","messageCount":2,"participants":["Jacek Lipiec","Junio C Hamano"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"487572","messageId":"CAPGWkwcqbkanFP4h1P0umJAFzmiQYTvSm1KeD0=yY0n2K=KS7g@mail.gmail.com","threadId":"60813","inReplyTo":null,"subject":"Multiple indices / staging areas","fromName":"Jacek Lipiec","fromEmail":"jacek.lipiec.bc@gmail.com","sentAt":"2024-01-30T03:47:27Z","receivedAt":"2024-01-30T03:47:40Z","isPatch":false,"sender":{"key":"jacek.lipiec.bc@gmail.com","avatar":null},"body":"Hello,\nI am thinking about extending the current index mechanism, and I wish\nto ask for feedback if this would be something that you would be\ninterested in. Please note, I have no prior git development\nexperience.\n\nThe main idea is to mirror the \"changelist\" functionality found in\nIntelliJ IDE's (https://www.jetbrains.com/help/idea/managing-changelists.html).\nNamely, at each given moment you can create a \"changelist\"/separate\nindex to which you can move hunks to. Such changelists are named, and\noptionally have a (default) commit message.\nThis allows for an easy way to split current changes into separate\ncommits, to have clean and atomic work units committed; while seeing\nthe combined changes in the worktree; as well as having a\nconceptual/logical separation for changes\n\nScenario: I am introducing a new functionality. Changes are\nconceptually separate, but work together. i.e. new version of package\n& using the new functionality from the package.\nScenario 2: I am not yet sure about what needs to be done, so I am\nsketching in code. Some changes will be of linting nature, some will\nintroduce code, some will be an \"ad-hoc fix\".\n\nWhat we can do with the current flow:\n* Commit/amend/fixup. This works best when one has prior knowledge of\nwhat needs to be done, i.e. bumping versions then using the\nfunctionality. Problems arise when the code is \"living\", as such you\nneed to fixup commits in the local branch, which can lead to\nconflicts. Another issue is that if you wish to commit small change\n(i.e. a fix for a linter) you need to juggle the staging area. (This\nis my current flow in the vanilla git. It can become clunky if you,\nlike me, sketch the solution in the code)\n* git worktree. While this functionality is the closest to what I am\nproposing, it does not allow for easy transfer of hunks, nor it allows\nfor working with all the changes in the same worktree at the same\ntime.\n* branches. This again does not allow us to see all the changes at the\nsame time.\n\nThe idea for the implementation would be as follows, and should\nhopefully introduce no change to the existing flow.\n\n1) Design\n\n1.1) New indices file\nWe add a new file in the $GIT_DIR, called `indices`. This is a map\nfile that will MUST have at least one entry. Index entry MUST have a\nname. Index entries COULD have default commit message\nExample:\n```.git/indices\nselected=1 # maps to .git/index.1\n#  translates to .git/index.0\nindex[0].name=Default\n#  translates to .git/index.1\nindex[1].name=Add named index\n#  translates to .git/index.2\nindex[2].name=Wip\nindex[2].message=Add named index\\n\\nThis is a longer description\n```\nThe \"selected\" index is in two places - one is `index` and the other\nis based on the selected entry; so following example, `index.1`. The\nbest way to keep them synced would be to symlink them, though this\nwould introduce FS dependency. Synchronized copy would work as well.\nRenames could work, but there would be IMO high risk of losing changes\nfrom the index due to a mistake.\n\nThis way, the existing index file format remains unchanged; and all\nnew functionality can be handled via the new `indices` file; due to\nthat all existing code should work without an issue.\nI see a potential problem of two indices overlapping (i.e. index\nchanged via older git, or other tool) - in this case I would\ntransparently correct other indices, treating selected index as the\nsource of truth. This would imply that software not aware of the\nindices would see the changes in the not-selected index as unstaged.\nThe second issue is the split index, as I know nothing about it, yet\nit might require changes.\n\n1.2) Commands\nA git index create <name> [-m|--message <message>]\nA git index delete <name>\nA git index switch <name>\nA git index update <name> [-m|--message <message>] [-n|--name <new name>]\nM git add [-I|--index <index name>]\nM git diff [-I|--index <index name>]\n\n2) Example workflows\n2.1) Working with indices/non interactive add\ngit index create \"wip\" --message=\"This is a default commit message\" #\nWe have a new index, but the default one is selected\ntouch {example|example2|example3}\ngit add example # Added to the \"current\"/\"default\" index, by default 0\ngit index switch \"wip\"\ngit add example2 # Added to the \"wip\"/ index, with id of 1\ngit add example3 --index=default # Added to the \"current\"/\"default\" index,\ngit commit # commit message=\"This is a default commit message\"\n# At this point, index.1 (wip) is empty, but selected. We can commit\nanother index\ngit commit --index=default -m \"This is a commit message for the default index\"\n\n2.2) Status\n> git status\nOn branch main\nSelected index: wip\nChanges to be committed (wip):\n  (use \"git restore --staged <file>...\" to unstage)\n        modified:   example\n\nChanges staged for commit (default)\n  (use \"git restore --index=default --staged <file>...\" to unstage)\n        modified:   example\n\nChanges not staged for commit:\n  (use \"git add <file>...\" to update what will be committed)\n  (use \"git restore <file>...\" to discard changes in working directory)\n        modified:   example\n\n2.3) Git add (interactive)\n> git add --interactive # details omitted for brevity\n-Example change\n+Example change 2\n(1/1) Stage this hunk to wip [y,n,q,a,d,e,i,?]?\n> ?\ni - change the current index\n\n2.4) Remove index\n> git index remove # removes current, all staged goes to the \"default\" branch/index 0\n> git index remove wip # removes wip, all staged goes to the default branch/index 0\n> git index switch default\n> git index remove # Does nothing...\n> git index rename default \"WIP\" # index[0].name=WIP\n> git index remove # renames default -> index[0].name=Default\n...\n\nI am open for the feedback/questions, or early go/nogo.\n\nJacek Lipiec\n"},{"id":"487793","messageId":"xmqqr0hvuuts.fsf@gitster.g","threadId":"60813","inReplyTo":"CAPGWkwcqbkanFP4h1P0umJAFzmiQYTvSm1KeD0=yY0n2K=KS7g@mail.gmail.com","subject":"Re: Multiple indices / staging areas","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-02-02T08:35:59Z","receivedAt":"2024-02-02T08:36:11Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jacek Lipiec <jacek.lipiec.bc@gmail.com> writes:\n\n> I am open for the feedback/questions, or early go/nogo.\n\nGiven that even having one index between the committed history and\nthe working tree files seems to add cognitive load to new users, I\nam not very enthused to hear that we may be able to add an arbitrary\nnumber of them, and more importantly, I don't quite see what real\nworld problems the additional complexities (most of them are probably\nnot accounted for in the sketch we saw and yet to be discovered) are\nsolving.\n\nBut I'll wait before sending this response for a few days and keep\nit in my outbox.\n"}]}