├── .github
└── workflows
│ └── maven-publish.yml
├── .gitignore
├── LICENSE
├── README.md
├── doc
├── DiffTutorial.exe
├── OptionsAndParameters2.png
├── ZIT.pdf
├── diff2.pdf
└── diff3-short.pdf
├── pom.xml
└── src
├── main
├── java
│ └── club
│ │ └── qqtim
│ │ ├── Main.java
│ │ ├── command
│ │ ├── Add.java
│ │ ├── Branch.java
│ │ ├── CatFile.java
│ │ ├── Checkout.java
│ │ ├── Commit.java
│ │ ├── Diff.java
│ │ ├── Fetch.java
│ │ ├── HashObject.java
│ │ ├── Init.java
│ │ ├── Lg.java
│ │ ├── Log.java
│ │ ├── Merge.java
│ │ ├── MergeBase.java
│ │ ├── Push.java
│ │ ├── ReadTree.java
│ │ ├── Reset.java
│ │ ├── Show.java
│ │ ├── Status.java
│ │ ├── Tag.java
│ │ └── WriteTree.java
│ │ ├── common
│ │ ├── ConstantVal.java
│ │ └── RegexConstantVal.java
│ │ ├── context
│ │ ├── Zit.java
│ │ └── ZitContext.java
│ │ ├── converter
│ │ └── IdConverter.java
│ │ ├── data
│ │ ├── CommitObject.java
│ │ ├── RefObjValue.java
│ │ ├── RefObject.java
│ │ ├── RefValue.java
│ │ └── ZitObject.java
│ │ ├── diff
│ │ ├── DiffUtil.java
│ │ ├── LineObject.java
│ │ ├── MergePoint.java
│ │ ├── SimplyChange.java
│ │ └── algorithm
│ │ │ ├── DiffAlgorithm.java
│ │ │ ├── MyersDiff.java
│ │ │ ├── Snake.java
│ │ │ └── SnakePoint.java
│ │ ├── parser
│ │ ├── Parser.java
│ │ └── support
│ │ │ └── CommandLineParser.java
│ │ └── util
│ │ ├── ConvertUtil.java
│ │ ├── FileUtil.java
│ │ └── handler
│ │ └── PosixHandler.java
└── resources
│ └── log4j.properties
└── test
└── java
└── club
└── qqtim
└── data
└── DataTest.java
/.github/workflows/maven-publish.yml:
--------------------------------------------------------------------------------
1 | # This workflow will build a package using Maven and then publish it to GitHub packages when a release is created
2 | # For more information see: https://github.com/actions/setup-java/blob/main/docs/advanced-usage.md#apache-maven-with-a-settings-path
3 |
4 | name: Maven Package
5 |
6 | on:
7 | release:
8 | types: [created]
9 |
10 | jobs:
11 | build:
12 |
13 | runs-on: ubuntu-latest
14 | permissions:
15 | contents: read
16 | packages: write
17 |
18 | steps:
19 | - uses: actions/checkout@v3
20 | - name: Set up JDK 11
21 | uses: actions/setup-java@v3
22 | with:
23 | java-version: '11'
24 | distribution: 'temurin'
25 | server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
26 | settings-path: ${{ github.workspace }} # location for the settings.xml file
27 |
28 | - name: Build with Maven
29 | run: mvn -B package --file pom.xml
30 |
31 | - name: Publish to GitHub Packages Apache Maven
32 | run: mvn deploy -s $GITHUB_WORKSPACE/settings.xml
33 | env:
34 | GITHUB_TOKEN: ${{ github.token }}
35 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # Created by https://www.gitignore.io/api/java,maven,intellij
3 | # Edit at https://www.gitignore.io/?templates=java,maven,intellij
4 |
5 | ### Intellij ###
6 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
7 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
8 |
9 | # User-specific stuff
10 | .idea/
11 |
12 | # CMake
13 | cmake-build-*/
14 |
15 | # Mongo Explorer plugin
16 | .idea/**/mongoSettings.xml
17 |
18 | # File-based project format
19 | *.iws
20 |
21 | # IntelliJ
22 | out/
23 |
24 | # mpeltonen/sbt-idea plugin
25 | .idea_modules/
26 |
27 | # JIRA plugin
28 | atlassian-ide-plugin.xml
29 |
30 | # Crashlytics plugin (for Android Studio and IntelliJ)
31 | com_crashlytics_export_strings.xml
32 | crashlytics.properties
33 | crashlytics-build.properties
34 | fabric.properties
35 |
36 | ### Intellij Patch ###
37 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
38 |
39 | *.iml
40 | modules.xml
41 | *.ipr
42 |
43 | ### Java ###
44 | # Compiled class file
45 | *.class
46 |
47 | # Log file
48 | *.log
49 |
50 | # BlueJ files
51 | *.ctxt
52 |
53 | # Mobile Tools for Java (J2ME)
54 | .mtj.tmp/
55 |
56 | # Package Files #
57 | *.jar
58 | *.war
59 | *.nar
60 | *.ear
61 | *.zip
62 | *.tar.gz
63 | *.rar
64 |
65 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
66 | hs_err_pid*
67 |
68 | ### Maven ###
69 | target/
70 | pom.xml.tag
71 | pom.xml.releaseBackup
72 | pom.xml.versionsBackup
73 | pom.xml.next
74 | release.properties
75 | dependency-reduced-pom.xml
76 | buildNumber.properties
77 | .mvn/timing.properties
78 | .mvn/wrapper/maven-wrapper.jar
79 |
80 | # End of https://www.gitignore.io/api/java,maven,intellij
81 |
82 | # Created by https://www.toptal.com/developers/gitignore/api/visualstudiocode
83 | # Edit at https://www.toptal.com/developers/gitignore?templates=visualstudiocode
84 |
85 | ### VisualStudioCode ###
86 | .vscode/*
87 | .settings/*
88 | !.vscode/settings.json
89 | !.vscode/tasks.json
90 | !.vscode/launch.json
91 | !.vscode/extensions.json
92 |
93 | ### VisualStudioCode Patch ###
94 | # Ignore all local history of files
95 | .history
96 |
97 |
98 | # End of https://www.toptal.com/developers/gitignore/api/visualstudiocode
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 ReZero
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # [GIT](https://github.com/ReZeroS/git) Development Log
2 |
3 | Thanks for the great tutorial: [https://www.leshenko.net/p/ugit](https://www.leshenko.net/p/ugit)
4 |
5 | Thanks again, Nikita!
6 |
7 | ## Resources
8 |
9 | I found some good reference materials and placed them in the `./doc` directory.
10 |
11 | Additionally, these links might be helpful:
12 |
13 | - [Nick Butler](http://simplygenius.net/Article/DiffTutorial1): A concise post that provides a high-level understanding of diff
14 | - [jcoglan](https://blog.jcoglan.com/2017/02/12/the-myers-diff-algorithm-part-1/): A detailed description of the diff algorithm
15 | - [Visualization](https://blog.robertelder.org/diff-algorithm/): A great resource for debugging or visualizing the diff algorithm
16 | - [PDF](https://github.com/ReZeroS/zit/blob/main/doc/ZIT.pptx): A short PDF about Git that I hope you'll find interesting
17 |
18 | If you're interested in this project, feel free to share your ideas in the discussion or contact me via email.
19 |
20 | ## Usage
21 |
22 | 0. `alias zit='java -jar ../zit-1.0-SNAPSHOT-shaded.jar'` - Alias the zit executable file
23 |
24 | - On Windows, you can set this in `C:\Program Files\Git\etc\profile.d\aliases.sh`
25 |
26 | 1. `zit init` - Initialize the `.zit` directory with an `objects` subdirectory, create an index file (stage area), and set the default `main` branch
27 |
28 | - The default HEAD file content is `ref: ref/heads/main`
29 |
30 | 2. `zit hash-object file`
31 |
32 | - Get the file path to store
33 | - Read the file
34 | - Hash the *content* of the file using SHA-1
35 | - Store the file under `.ugit/objects/{the SHA-1 hash}`
36 |
37 | 3. `zit cat-file hash [object|tree|commit|...type]` - Print the file content
38 |
39 | 4. `zit write-tree` - Generate a tree describing the entire repository
40 |
41 | - Executed after the `add` command, creating a tree from the index file
42 |
43 | 5. `zit read-tree hash`
44 |
45 | - Caution: This action will delete all existing content before reading
46 | - Use `cat-file` to find the `root` tree
47 | - Logs from `write-tree` can also help you find all trees
48 |
49 | 6. While `write-tree` can save versions, it lacks context information, so a `zit commit -m "message"` command is needed
50 |
51 | - Use `cat-file hash commit-id` to check commit content
52 | - `HEAD` will record the commit with its parent
53 |
54 | 7. Enjoy committing and use `log` to view commit history
55 |
56 | 8. Checkout: Select a commit ID from the `log` and verify the state
57 |
58 | - [Fixed with getBytes(Charsets.UTF-8)] Bug: Chinese file or directory names may appear garbled
59 | - Arguments can be head alias, hash, or ref (branch, tags, HEAD...)
60 |
61 | 9. `tag` will alias a commit ID, introducing a core concept
62 |
63 | - [git-ref](https://git-scm.com/book/en/v2/Git-Internals-Git-References) official post helps learn basic reference knowledge
64 |
65 | 10. TODO: `zit lg` graph feature with Graphviz
66 |
67 | 11. `zit branch name [id]` - Familiar branch creation
68 |
69 | - Every ref under `refs/heads` is treated as a branch
70 | - File content is simply the commit ID, defaulting to the head point
71 |
72 | 12. `zit show` will display detailed changes using diff, while `status` shows simple change information
73 |
74 | 13. `zit add` adds files or directories to the stage file: `.zit/index`
75 |
76 | 14. `zit commit` calls `write-tree` and updates the HEAD pointer to the commit ID
77 |
78 | - First-time usage creates the default `main` branch and rewrites the HEAD file content
79 | - Merge HEAD is deleted, and the message is added to the commit message
80 |
81 | 15. `zit status` shows the current situation
82 |
83 | - If not in a detached HEAD state, logs the current HEAD-pointed branch
84 | - Logs merge hash ID if in a merge state
85 | - Lists changes to be committed (diff between HEAD tree and index)
86 | - Lists changes not staged for the next commit (diff between index and working tree)
87 |
88 | 16. `zit diff` uses the Myers diff algorithm without linear space refinement optimization
89 |
90 | 17. `zit reset` changes HEAD to the current commit (difference from `checkout` is pending)
91 |
92 | 18. `zit merge` checks if the merge base equals the head, using fast-forward merge if possible
93 |
94 | - Fast-forward requires no commit
95 | - Otherwise, uses diff3 to merge the merge base, head tree, and other tree
96 | - Leaves `merge_head` in the zit root directory, requiring manual commit
97 | - `zit merge-base` helps find the first common parent commit for merging and debugging
98 |
99 | 19. `zit fetch` and `zit push` download or upload objects and update references
100 |
101 | ## Summary
102 |
103 | ### UPDATED 2021.02.21
104 |
105 | - Implemented diff (Myers diff without linear space optimization) and merge algorithms (simple diff3) instead of using Unix tools
106 | - `Ugit` uses Pythonic code, while zit aims to make the code easily understandable for developers of other languages
107 |
108 | ### TODO
109 |
110 | 1. `git hash-object` improvement: When real Git stores objects, it:
111 | - Writes the object size to the file
112 | - Compresses objects
113 | - Divides objects into 256 directories to avoid performance issues with large numbers of files
114 |
--------------------------------------------------------------------------------
/doc/DiffTutorial.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ReZeroS/git/bbf78f7ce7f8a07008b5bbcdb70da44701b8d917/doc/DiffTutorial.exe
--------------------------------------------------------------------------------
/doc/OptionsAndParameters2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ReZeroS/git/bbf78f7ce7f8a07008b5bbcdb70da44701b8d917/doc/OptionsAndParameters2.png
--------------------------------------------------------------------------------
/doc/ZIT.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ReZeroS/git/bbf78f7ce7f8a07008b5bbcdb70da44701b8d917/doc/ZIT.pdf
--------------------------------------------------------------------------------
/doc/diff2.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ReZeroS/git/bbf78f7ce7f8a07008b5bbcdb70da44701b8d917/doc/diff2.pdf
--------------------------------------------------------------------------------
/doc/diff3-short.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ReZeroS/git/bbf78f7ce7f8a07008b5bbcdb70da44701b8d917/doc/diff3-short.pdf
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | club.qqtim
8 | zit
9 | 1.0-SNAPSHOT
10 |
11 |
12 |
13 |
14 |
15 | org.projectlombok
16 | lombok
17 | 1.18.10
18 | provided
19 |
20 |
21 |
22 |
23 | ch.qos.logback
24 | logback-classic
25 | 1.3.12
26 |
27 |
28 | com.google.guava
29 | guava
30 | 32.0.0-jre
31 |
32 |
33 | info.picocli
34 | picocli
35 | 4.5.2
36 |
37 |
38 |
39 |
40 | com.google.code.gson
41 | gson
42 | 2.8.9
43 |
44 |
45 |
46 |
47 | junit
48 | junit
49 | 4.13.1
50 | test
51 |
52 |
53 |
54 |
55 | com.github.jnr
56 | jnr-posix
57 | 3.1.4
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 | org.apache.maven.plugins
66 | maven-shade-plugin
67 |
68 |
69 |
70 | shade
71 |
72 |
73 | true
74 |
75 |
77 | club.qqtim.Main
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 | org.apache.maven.plugins
86 | maven-compiler-plugin
87 |
88 | 8
89 | 8
90 |
91 |
92 |
93 |
94 |
95 |
--------------------------------------------------------------------------------
/src/main/java/club/qqtim/Main.java:
--------------------------------------------------------------------------------
1 | package club.qqtim;
2 |
3 | import club.qqtim.parser.Parser;
4 | import club.qqtim.parser.support.CommandLineParser;
5 | import lombok.extern.slf4j.Slf4j;
6 |
7 |
8 | @Slf4j
9 | public class Main {
10 |
11 | public static void main(String[] args) {
12 | log.info("hello, zit.");
13 | Parser parser = new CommandLineParser();
14 | parser.execute(args);
15 | }
16 |
17 |
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/club/qqtim/command/Add.java:
--------------------------------------------------------------------------------
1 | package club.qqtim.command;
2 |
3 | import club.qqtim.common.ConstantVal;
4 | import club.qqtim.util.FileUtil;
5 | import com.google.gson.Gson;
6 | import com.google.gson.JsonObject;
7 | import lombok.extern.slf4j.Slf4j;
8 | import picocli.CommandLine;
9 |
10 | import java.io.File;
11 | import java.io.IOException;
12 | import java.nio.file.Files;
13 | import java.nio.file.Paths;
14 | import java.util.List;
15 |
16 | /**
17 | * @title: Add
18 | * @Author ReZeroS
19 | * @Date: 2021/2/3
20 | * @Version 1.0.0
21 | */
22 |
23 | @Slf4j
24 | @lombok.Data
25 | @CommandLine.Command(name = "add")
26 | public class Add implements Runnable{
27 |
28 |
29 | @CommandLine.Parameters(paramLabel = "FILE", description = "one ore more files to archive", defaultValue = ".")
30 | List files;
31 |
32 |
33 |
34 | @Override
35 | public void run() {
36 | addFiles(files);
37 | }
38 |
39 | /**
40 | * update the index file content: if new file then add while old file will be updated
41 | * content is a big easy (key val directly) json like this:
42 | * {
43 | * ".\\doc\\OptionsAndParameters2.png": "f4a36c21ce890b0fa10067c10dab416e9b71a13e"
44 | * }
45 | */
46 | private void addFiles(List files) {
47 | final String indexContent = FileUtil.getFileAsString(ConstantVal.INDEX, ConstantVal.NONE);
48 | final JsonObject asJsonObject = new Gson().fromJson(indexContent, JsonObject.class);
49 |
50 | files.forEach(file -> {
51 | final boolean isFile = FileUtil.isFile(file);
52 | if (isFile) {
53 | addFile(asJsonObject, file);
54 | } else { // is directory
55 | addDirectory(asJsonObject, file);
56 | }
57 | });
58 | FileUtil.createFile(asJsonObject.toString(), ConstantVal.INDEX);
59 | }
60 |
61 | private void addFile(JsonObject asJsonObject, String file) {
62 | HashObject hashObject = new HashObject();
63 | hashObject.setFile(new File(file));
64 | hashObject.setType(ConstantVal.BLOB);
65 | final String objectId = hashObject.call();
66 | asJsonObject.addProperty(file, objectId);
67 | }
68 |
69 |
70 | private void addDirectory(JsonObject asJsonObject, String file) {
71 | try {
72 | FileUtil.walk(Paths.get(file), Integer.MAX_VALUE)
73 | .filter(Files::isRegularFile)
74 | .forEach(regularFile -> addFile(asJsonObject, regularFile.toString()));
75 | } catch (IOException e) {
76 | log.error(e.toString());
77 | }
78 | }
79 |
80 |
81 |
82 |
83 |
84 | }
85 |
--------------------------------------------------------------------------------
/src/main/java/club/qqtim/command/Branch.java:
--------------------------------------------------------------------------------
1 | package club.qqtim.command;
2 |
3 | import club.qqtim.common.ConstantVal;
4 | import club.qqtim.context.ZitContext;
5 | import club.qqtim.converter.IdConverter;
6 | import club.qqtim.data.RefObject;
7 | import club.qqtim.data.RefValue;
8 | import lombok.extern.slf4j.Slf4j;
9 | import picocli.CommandLine;
10 |
11 | import java.nio.file.Paths;
12 | import java.util.List;
13 | import java.util.Objects;
14 | import java.util.stream.Collectors;
15 |
16 | /**
17 | * @title: Branch
18 | * @Author rezeros.github.io
19 | * @Date: 2020/12/9
20 | * @Version 1.0.0
21 | */
22 | @lombok.Data
23 | @Slf4j
24 | @CommandLine.Command(name = "branch")
25 | public class Branch implements Runnable{
26 |
27 |
28 | @CommandLine.Parameters(index = "0", defaultValue = ConstantVal.NONE)
29 | private String name;
30 |
31 |
32 |
33 | @CommandLine.Parameters(index = "1", defaultValue = ConstantVal.HEAD_ALIAS, converter = IdConverter.class)
34 | private String startPoint;
35 |
36 |
37 | /**
38 | * Pay attention, only you create a commit at branch, the branch will generate at real
39 | * so if you execute `zit init`, then `zit branch` immediately will get nothing, this is not a bug.
40 | */
41 | @Override
42 | public void run() {
43 | // none name for readable command
44 | if(ConstantVal.NONE.equals(name)) {
45 | final String currentBranch = ZitContext.getBranchName();
46 | final List branchNames = iteratorBranchNames();
47 | branchNames.forEach(branchName ->
48 | log.info("{} {}", branchName.equals(currentBranch) ? ConstantVal.STAR : ConstantVal.EMPTY, branchName)
49 | );
50 | } else {
51 | // got name for branch will be created base on the commit point
52 | createBranch(name, startPoint);
53 | log.info("created branch {} at {}", name, startPoint.substring(0, 11));
54 | }
55 | }
56 |
57 | /**
58 | * list all branches in the repository
59 | */
60 | private List iteratorBranchNames(){
61 | final List refObjects = ZitContext.iteratorRefs(ConstantVal.HEADS_PATH);
62 | return refObjects.stream()
63 | .map(RefObject::getRefName)
64 | .map(path -> Paths.get(path).relativize(Paths.get(ConstantVal.HEADS_PATH)).toString())
65 | .collect(Collectors.toList());
66 | }
67 |
68 |
69 | /**
70 | * create a branch
71 | * this will create a file with commitId content called branch name under the refs/heads directory
72 | */
73 | private static void createBranch(String name, String startPoint) {
74 | String branch = String.format(ConstantVal.BASE_REFS_HEADS_PATH, name);
75 | ZitContext.updateRef(branch, new RefValue(false, startPoint));
76 | }
77 |
78 |
79 | /**
80 | * whether the branch exist
81 | */
82 | public static boolean existBranch(String name){
83 | String branch = String.format(ConstantVal.BASE_REFS_HEADS_PATH, name);
84 | final RefValue ref = ZitContext.getRef(branch);
85 | return Objects.nonNull(ref.getValue());
86 | }
87 |
88 | }
89 |
--------------------------------------------------------------------------------
/src/main/java/club/qqtim/command/CatFile.java:
--------------------------------------------------------------------------------
1 | package club.qqtim.command;
2 |
3 |
4 | import club.qqtim.context.ZitContext;
5 | import club.qqtim.converter.IdConverter;
6 | import lombok.Data;
7 | import lombok.extern.slf4j.Slf4j;
8 | import picocli.CommandLine;
9 |
10 | import java.util.concurrent.Callable;
11 |
12 | /**
13 | * @author rezeros.github.io
14 | */
15 | @Data
16 | @Slf4j
17 | @CommandLine.Command(name = "cat-file")
18 | public class CatFile implements Callable {
19 |
20 | @CommandLine.Parameters(index = "0", converter = IdConverter.class)
21 | private String id;
22 |
23 | @CommandLine.Parameters(index = "1", defaultValue = "blob")
24 | private String type;
25 |
26 | @Override
27 | public String call() {
28 | final String fileContent = ZitContext.getObjectAsString(id, type);
29 | log.info(fileContent);
30 | return fileContent;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/java/club/qqtim/command/Checkout.java:
--------------------------------------------------------------------------------
1 | package club.qqtim.command;
2 |
3 | import club.qqtim.common.ConstantVal;
4 | import club.qqtim.context.ZitContext;
5 | import club.qqtim.data.CommitObject;
6 | import club.qqtim.data.RefValue;
7 | import lombok.extern.slf4j.Slf4j;
8 | import picocli.CommandLine;
9 |
10 | import java.nio.file.Path;
11 |
12 | /**
13 | * @title: Checkout
14 | * @Author rezeros.github.io
15 | * @Date: 2020/11/7
16 | * @Version 1.0.0
17 | */
18 | @lombok.Data
19 | @Slf4j
20 | @CommandLine.Command(name = "checkout")
21 | public class Checkout implements Runnable {
22 |
23 |
24 | @CommandLine.Parameters(index = "0")
25 | private String ref;
26 |
27 |
28 |
29 | @Override
30 | public void run() {
31 | checkout(this.ref);
32 | }
33 |
34 |
35 | /**
36 | * checkout command generate module by the commit describe
37 | * @param name could be head alias, hash and ref(branch, tags, HEAD...)
38 | */
39 | private void checkout(String name) {
40 | String id = ZitContext.getId(name);
41 | // get the name reference commit
42 | final CommitObject commit = Commit.getCommit(id);
43 | ReadTree.readTree(commit.getTree(), true);
44 |
45 | RefValue refValue;
46 | if (Branch.existBranch(name)) {
47 | refValue = new RefValue(true, String.format(ConstantVal.BASE_REFS_HEADS_PATH, name));
48 | } else {
49 | refValue = new RefValue(false, id);
50 | }
51 | ZitContext.updateRef(ConstantVal.HEAD, refValue,false);
52 | }
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/src/main/java/club/qqtim/command/Commit.java:
--------------------------------------------------------------------------------
1 | package club.qqtim.command;
2 |
3 | import club.qqtim.common.ConstantVal;
4 | import club.qqtim.context.ZitContext;
5 | import club.qqtim.data.CommitObject;
6 | import club.qqtim.data.RefValue;
7 | import com.google.common.base.Charsets;
8 | import lombok.Data;
9 | import picocli.CommandLine;
10 |
11 | import java.util.ArrayList;
12 | import java.util.Arrays;
13 | import java.util.List;
14 | import java.util.concurrent.Callable;
15 |
16 | /**
17 | * @title: Commit
18 | * @Author rezeros.github.io
19 | * @Date: 2020/10/31
20 | * @Version 1.0.0
21 | */
22 | @Data
23 | @CommandLine.Command(name = "commit")
24 | public class Commit implements Callable {
25 |
26 | @CommandLine.Option(names = {"-m", "--message"}, description = "do commit", required = true)
27 | private boolean commit;
28 |
29 | @CommandLine.Parameters(index = "0")
30 | private String message;
31 |
32 |
33 | @Override
34 | public String call() {
35 | return commit();
36 | }
37 |
38 | private String commit() {
39 | // calc the commit message
40 | WriteTree writeTree = new WriteTree();
41 | String commitMessage = String.format("%s %s\n", ConstantVal.TREE, writeTree.call());
42 |
43 | // set last commit as parent commit
44 | String headId = ZitContext.getRef(ConstantVal.HEAD).getValue();
45 | if (headId != null) {
46 | commitMessage += String.format("%s %s\n", ConstantVal.PARENT, headId);
47 | }
48 |
49 | String mergedHead = ZitContext.getRef(ConstantVal.MERGE_HEAD).getValue();
50 | if (mergedHead != null) {
51 | commitMessage += String.format("%s %s\n", ConstantVal.PARENT, mergedHead);
52 | ZitContext.deleteRef(ConstantVal.MERGE_HEAD, false);
53 | }
54 |
55 | // write commit message
56 | commitMessage += String.format("\n%s\n", message);
57 |
58 | // write commit object and return commit id
59 | final String commitId = HashObject.hashObject(commitMessage.getBytes(Charsets.UTF_8), ConstantVal.COMMIT);
60 | ZitContext.updateRef(ConstantVal.HEAD, new RefValue(false, commitId));
61 | return commitId;
62 | }
63 |
64 |
65 | public static CommitObject getCommit(String id) {
66 | final byte[] commit = ZitContext.getObject(id, ConstantVal.COMMIT);
67 | assert commit != null;
68 | final String commitContent = new String(commit, Charsets.UTF_8);
69 | final String[] lines = commitContent.split(ConstantVal.NEW_LINE);
70 |
71 | List parents = new ArrayList<>();
72 | CommitObject commitObject = new CommitObject();
73 | Arrays.stream(lines).forEach(line -> {
74 | final String[] fields = line.split(ConstantVal.SINGLE_SPACE);
75 | if (ConstantVal.TREE.equals(fields[0])) {
76 | commitObject.setTree(fields[1]);
77 | }
78 | if (ConstantVal.PARENT.equals(fields[0])) {
79 | parents.add(fields[1]);
80 | }
81 | });
82 | commitObject.setParents(parents);
83 | commitObject.setMessage(commitContent);
84 | return commitObject;
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/src/main/java/club/qqtim/command/Diff.java:
--------------------------------------------------------------------------------
1 | package club.qqtim.command;
2 |
3 | import club.qqtim.common.ConstantVal;
4 | import club.qqtim.context.ZitContext;
5 | import club.qqtim.diff.DiffUtil;
6 | import club.qqtim.util.FileUtil;
7 | import com.google.gson.Gson;
8 | import com.google.gson.reflect.TypeToken;
9 | import lombok.Data;
10 | import lombok.extern.slf4j.Slf4j;
11 | import picocli.CommandLine;
12 |
13 | import java.io.File;
14 | import java.io.IOException;
15 | import java.nio.file.Files;
16 | import java.nio.file.Path;
17 | import java.nio.file.Paths;
18 | import java.util.Collections;
19 | import java.util.HashMap;
20 | import java.util.Map;
21 | import java.util.concurrent.Callable;
22 | import java.util.function.Function;
23 | import java.util.stream.Collectors;
24 |
25 | /**
26 | * show what changes in working directory since last commit
27 | *
28 | * @title: Diff
29 | * @Author rezeros.github.io
30 | * @Date: 2020/12/26
31 | * @Version 1.0.0
32 | */
33 | @Data
34 | @Slf4j
35 | @CommandLine.Command(name = "diff")
36 | public class Diff implements Callable {
37 |
38 | @CommandLine.Option(names = {"--cached"})
39 | private boolean cached = false;
40 |
41 |
42 | @CommandLine.Option(names = {"--commit"})
43 | private String commit;
44 |
45 |
46 | @Override
47 | public String call() {
48 | String objectId;
49 | Map treeFrom = new HashMap<>(16), treeTo;
50 | if (this.commit != null) {
51 | objectId = ZitContext.getId(this.commit);
52 | treeFrom = ReadTree.getTree(Commit.getCommit(objectId).getTree());
53 | }
54 | final String indexContent = FileUtil.getFileAsString(ConstantVal.INDEX, ConstantVal.NONE);
55 |
56 | if (cached) {
57 | treeTo = new Gson().fromJson(indexContent, new TypeToken